Chcę przesyłać nazwy plików do innych programów, ale wszystkie dławią się, gdy zawierają spacje.
Powiedzmy, że mam plik o nazwie.
foo bar
Jak mogę uzyskać find
poprawną nazwę?
Oczywiście chcę:
foo\ bar
lub:
"foo bar"
EDYCJA : Nie chcę przechodzić xargs
, chcę uzyskać poprawnie sformatowany ciąg znaków, find
aby móc przesyłać ciąg nazw plików bezpośrednio do innego programu.
-exec
flagę zfind
? możesz potencjalnie złagodzić ten błąd i sprawić, że twoje polecenie będzie bardziej wydajne, wykonując je-exec
zamiast potokowania go do innych poleceń. Tylko moje 0,02 $find
formatuje nazwy plików; są zapisywane jedna nazwa w wierszu. (Oczywiście jest to niejednoznaczne, jeśli nazwa pliku zawiera znak nowego wiersza.) Problemem jest więc „zadławienie” końca odbierającego, gdy otrzymuje spację, co oznacza, że musisz powiedzieć nam, co to jest koniec odbierający, jeśli chcesz sensowną odpowiedź .find
zaoferowanie opcji wyświetlania nazw plików w formacie odpowiednim dla powłoki. Zasadniczo jednak rozszerzenie-print0
GNUfind
działa również w wielu innych scenariuszach i powinieneś nauczyć się go używać w każdym przypadku.ls $(command...)
nie przesyła listystdin
. Umieszcza dane wyjściowe$(command...)
bezpośrednio w wierszu polecenia. W takim przypadku jest to powłoka, która czyta z c, i użyje bieżącej wartości,$IFS
aby zdecydować, jak wyrazić wyjście. Ogólnie lepiej jest używaćxargs
. Nie zauważysz uderzenia wydajności.find -printf '"%p"\n'
doda podwójne cudzysłowy wokół każdej znalezionej nazwy, ale nie będzie poprawnie cytować żadnych podwójnych cudzysłowów w nazwie pliku. Jeśli w nazwach plików nie ma osadzonych podwójnych cudzysłowów, możesz zignorować problem: lub przepuścićsed 's/"/&&/g;s/^""/"/;s/""$/"/'
. Jeśli twoje nazwy plików są obsługiwane przez powłokę, prawdopodobnie powinieneś użyć pojedynczych cudzysłowów zamiast podwójnych cudzysłowów (inaczejsweet$HOME
stanie się czymś podobnymsheet/home/you
). I to wciąż nie jest bardzo odporne na nazwy plików z nowymi liniami. Jak chcesz sobie z tym poradzić?Odpowiedzi:
POSIXLY:
Z
find
podporami-print0
ixargs
podporami-0
:-0
opcja mówi xargsowi, aby użyła znaku NUL ASCII zamiast spacji do zakończenia (oddzielenia) nazw plików.Przykład:
źródło
ls $(find . -maxdepth 1 -type f -print0 | xargs -0)
, dostajęls: cannot access ./foo: No such file or directory
ls: cannot access bar: No such file or directory
$(..)
"$(..)"
find
ixargs
.Używanie
-print0
jest jedną z opcji, ale nie wszystkie programy obsługują używanie strumieni danych rozdzielanych nullbyte, więc będziesz musiał używaćxargs
tej-0
opcji do niektórych rzeczy, jak zauważyła odpowiedź Gnouc.Alternatywą byłoby użyć
find
„s-exec
lub-execdir
opcji. Pierwsza z poniższych opcji będzie podawać nazwy plikówsomecommand
pojedynczo, a druga rozwinie się do listy plików:Może się okazać, że lepiej jest używać globowania w wielu przypadkach. Jeśli masz nowoczesną powłokę (bash 4+, zsh, ksh), możesz uzyskać rekurencyjne globowanie za pomocą
globstar
(**
). W bash musisz to ustawić:Mam wiersz
shopt -s globstar extglob
w moim .bashrc, więc zawsze jest to dla mnie włączone (podobnie jak rozszerzone globusy, które są również przydatne).Jeśli nie chcesz rekurencyjności, po prostu użyj
./*.txt
zamiast tego, aby użyć każdego * .txt w katalogu roboczym.find
ma kilka bardzo przydatnych funkcji szczegółowego wyszukiwania i jest obowiązkowy dla dziesiątek tysięcy plików (w tym momencie natrafisz na maksymalną liczbę argumentów powłoki), ale w codziennym użyciu często jest to niepotrzebne.źródło
Osobiście użyłbym
-exec
akcji find, aby rozwiązać ten problem. Lub, jeśli to koniecznexargs
, co pozwala na równoległe wykonywanie.Istnieje jednak sposób na uzyskanie
find
listy nazw plików do odczytu. Nic więc dziwnego, że używa-exec
ibash
, w szczególności rozszerzenie doprintf
polecenia:Jednak mimo tego, że wydrukuje on poprawnie słowa, które nie mają znaku powłoki, nie będzie można ich używać
$(...)
, ponieważ$(...)
nie interpretuje cudzysłowów ani znaków ucieczki. (Resut z$(...)
jest podzielony na słowa i rozwinięcie nazwy ścieżki, chyba że jest otoczony cudzysłowami.) Więc następujące czynności nie będą robić, co chcesz:Co musisz zrobić:
(Zauważ, że nie podjąłem żadnej prawdziwej próby przetestowania powyższej potworności.)
Ale równie dobrze możesz zrobić:
źródło
ls
scenariusz odpowiednio wychwycił przypadek użycia PO, ale jest to tylko spekulacja, ponieważ nie pokazano nam, co on tak naprawdę chce osiągnąć. To rozwiązanie działa naprawdę bardzo ładnie; Dostaję oczekiwany (niejasno) wynik dla wszystkich śmiesznych nazw plików, których próbowałem, w tymtouch "$(tr a-z '\001-\026' <<<'the quick brown fox jumped over the lazy dogs')"
eval
jest to, że nie musisz goeval
jeszcze przekazywać ; możesz zapisać go w parametrze i użyć później, być może kilka razy z różnymi poleceniami. Jednak PO nie daje żadnych wskazówek, że jest to sprawa zastosowanie (a gdyby tak było, to może być lepiej umieścić nazwy plików do tablicy, choć to trudne, zbyt.)dostanie ci pliki i katalogi zawierające spacje
dostanie ci pliki zawiera spacje
dostanie ci katalogi zawierające spacje
źródło
źródło