Pamiętaj, że w nazwie pliku mogą być także znaki nowej linii. Dlatego jest find -print0i xargs -0.
Daniel Beck
Odpowiedzi:
12
Idealnie nie robisz tego w ogóle, ponieważ prawidłowe analizowanie nazw plików w skrypcie powłoki jest zawsze trudne (napraw to spacje, nadal będziesz mieć problemy z innymi osadzonymi znakami, w szczególności nową linią). Jest to nawet wymienione jako pierwszy wpis na stronie BashPitfalls.
To powiedziawszy, istnieje sposób, aby prawie zrobić to, co chcesz:
oIFS=$IFS
IFS=$'\n'
find . -name '*.txt' | while read -r i; do
# use "$i" with whatever you're doing
done
IFS=$oIFS
Pamiętaj, aby cytować także $ipodczas korzystania z niego, aby uniknąć innych interpretacji spacji później. Pamiętaj również, aby $IFSzrezygnować po jego użyciu, ponieważ nieprzestrzeganie tego spowoduje później oszałamiające błędy.
Ma to jeszcze jedno zastrzeżenie: to, co dzieje się w whilepętli, może mieć miejsce w podpowłoce, w zależności od dokładnie używanej powłoki, więc ustawienia zmiennych mogą nie zostać zachowane. Wersja forpętli unika tego, ale za cenę, która nawet jeśli zastosujesz $IFSrozwiązanie w celu uniknięcia problemów ze spacjami, będziesz mieć kłopoty, jeśli findzwróci zbyt wiele plików.
W pewnym momencie poprawną poprawką do tego wszystkiego staje się robienie tego w języku takim jak Perl lub Python zamiast powłoki.
Podoba mi się pomysł używania Pythona, aby tego uniknąć.
Scott C Wilson,
12
Użyj find -print0i potokuj go xargs -0lub napisz własny mały program w języku C i potokuj go do małego programu w języku C. Po to -print0i -0wymyślono.
Skrypty powłoki nie są najlepszym sposobem na obsługę nazw plików ze spacjami: możesz to zrobić, ale robi się nieporęcznie.
Możesz ustawić „separator pola wewnętrznego” ( IFS) na coś innego niż miejsce na dzielenie argumentów pętli, np
ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
IFS=${ORIGIFS}
#do stuff
done
IFS=${ORIGIFS}
Resetuję IFSpo użyciu w find, głównie dlatego, że chyba ładnie wygląda. Nie widziałem żadnych problemów z ustawieniem go na nową linię, ale myślę, że jest to „czystsze”.
Inną metodą, zależnie od tego, co chcesz zrobić z danymi wyjściowymi find, jest albo bezpośrednie użycie -execz findpoleceniem, albo użycie -print0i wpięcie do niego xargs -0. W pierwszym przypadku finddba o ucieczkę nazwy pliku. W takim -print0przypadku findwypisuje dane wyjściowe z separatorem zerowym, a następnie xargsdzieli na to. Ponieważ żadna nazwa pliku nie może zawierać tego znaku (o czym wiem), jest to również zawsze bezpieczne. Jest to szczególnie przydatne w prostych przypadkach; i zwykle nie jest doskonałym zamiennikiem pełnej forpętli.
Używanie w find -print0połączeniu z xargs -0jest całkowicie odporne na legalne nazwy plików i jest jedną z najbardziej rozszerzalnych dostępnych metod. Załóżmy na przykład, że chcesz wyświetlić listę wszystkich plików PDF w bieżącym katalogu. Mógłbyś pisać
Znajduje każdy plik PDF (przez -iname '*.pdf') w bieżącym katalogu ( .) i dowolnym podkatalogu i przekazuje każdy z nich jako argument do echopolecenia. Ponieważ podaliśmy tę -n 1opcję, xargsprzekażemy tylko jeden argument na raz echo. Gdybyśmy pominęli tę opcję, xargsprzekazalibyśmy jej jak najwięcej echo. (Możesz echo short input | xargs --show-limitszobaczyć, ile bajtów jest dozwolonych w wierszu poleceń).
Co xargsdokładnie robi ?
Możemy wyraźnie zobaczyć, jaki wpływ xargsma na jego wejście - aw szczególności efekt -n- za pomocą skryptu, który powtarza swoje argumenty w sposób bardziej precyzyjny niż echo.
Nie zgadzam się z bashpodstawkami, ponieważ bashwraz z zestawem narzędzi * nix jest dość biegły w obsłudze plików (w tym tych, których nazwy mają osadzone białe znaki).
W rzeczywistości finddaje ci drobną kontrolę nad wyborem plików do przetworzenia ... Po stronie bash naprawdę musisz tylko zdać sobie sprawę, że musisz zrobić łańcuchy bash words; zazwyczaj przy użyciu „podwójnych cudzysłowów” lub innego mechanizmu, takiego jak IFS lub find's{}
Zauważ, że w większości / wielu sytuacjach nie musisz ustawiać i resetować IFS; po prostu użyj IFS lokalnie, jak pokazano w poniższych przykładach. Wszystkie trzy dobrze trzymają białe znaki. Także nie trzeba „standard” strukturę pętli, ponieważ znaleźć na \;to skutecznie pętla; wystarczy umieścić logikę pętli w funkcji bash (jeśli nie wywołujesz standardowego narzędzia).
Obie perspektywy mają pewną ważność. Kiedy pracowałem tylko nad własnymi plikami, po prostu użyłem find i nie martwię się o to, ponieważ moje pliki nie mają spacji (ani znaków powrotu karetki!) W swoich nazwach. Ale kiedy zaczniesz pracować z plikami innych ludzi, musisz użyć bardziej niezawodnych technik.
find -print0
ixargs -0
.Odpowiedzi:
Idealnie nie robisz tego w ogóle, ponieważ prawidłowe analizowanie nazw plików w skrypcie powłoki jest zawsze trudne (napraw to spacje, nadal będziesz mieć problemy z innymi osadzonymi znakami, w szczególności nową linią). Jest to nawet wymienione jako pierwszy wpis na stronie BashPitfalls.
To powiedziawszy, istnieje sposób, aby prawie zrobić to, co chcesz:
Pamiętaj, aby cytować także
$i
podczas korzystania z niego, aby uniknąć innych interpretacji spacji później. Pamiętaj również, aby$IFS
zrezygnować po jego użyciu, ponieważ nieprzestrzeganie tego spowoduje później oszałamiające błędy.Ma to jeszcze jedno zastrzeżenie: to, co dzieje się w
while
pętli, może mieć miejsce w podpowłoce, w zależności od dokładnie używanej powłoki, więc ustawienia zmiennych mogą nie zostać zachowane. Wersjafor
pętli unika tego, ale za cenę, która nawet jeśli zastosujesz$IFS
rozwiązanie w celu uniknięcia problemów ze spacjami, będziesz mieć kłopoty, jeślifind
zwróci zbyt wiele plików.W pewnym momencie poprawną poprawką do tego wszystkiego staje się robienie tego w języku takim jak Perl lub Python zamiast powłoki.
źródło
Użyj
find -print0
i potokuj goxargs -0
lub napisz własny mały program w języku C i potokuj go do małego programu w języku C. Po to-print0
i-0
wymyślono.Skrypty powłoki nie są najlepszym sposobem na obsługę nazw plików ze spacjami: możesz to zrobić, ale robi się nieporęcznie.
źródło
Możesz ustawić „separator pola wewnętrznego” (
IFS
) na coś innego niż miejsce na dzielenie argumentów pętli, npResetuję
IFS
po użyciu w find, głównie dlatego, że chyba ładnie wygląda. Nie widziałem żadnych problemów z ustawieniem go na nową linię, ale myślę, że jest to „czystsze”.Inną metodą, zależnie od tego, co chcesz zrobić z danymi wyjściowymi
find
, jest albo bezpośrednie użycie-exec
zfind
poleceniem, albo użycie-print0
i wpięcie do niegoxargs -0
. W pierwszym przypadkufind
dba o ucieczkę nazwy pliku. W takim-print0
przypadkufind
wypisuje dane wyjściowe z separatorem zerowym, a następniexargs
dzieli na to. Ponieważ żadna nazwa pliku nie może zawierać tego znaku (o czym wiem), jest to również zawsze bezpieczne. Jest to szczególnie przydatne w prostych przypadkach; i zwykle nie jest doskonałym zamiennikiem pełnejfor
pętli.źródło
Korzystanie
find -print0
zxargs -0
Używanie w
find -print0
połączeniu zxargs -0
jest całkowicie odporne na legalne nazwy plików i jest jedną z najbardziej rozszerzalnych dostępnych metod. Załóżmy na przykład, że chcesz wyświetlić listę wszystkich plików PDF w bieżącym katalogu. Mógłbyś pisaćZnajduje każdy plik PDF (przez
-iname '*.pdf'
) w bieżącym katalogu (.
) i dowolnym podkatalogu i przekazuje każdy z nich jako argument doecho
polecenia. Ponieważ podaliśmy tę-n 1
opcję,xargs
przekażemy tylko jeden argument na razecho
. Gdybyśmy pominęli tę opcję,xargs
przekazalibyśmy jej jak najwięcejecho
. (Możeszecho short input | xargs --show-limits
zobaczyć, ile bajtów jest dozwolonych w wierszu poleceń).Co
xargs
dokładnie robi ?Możemy wyraźnie zobaczyć, jaki wpływ
xargs
ma na jego wejście - aw szczególności efekt-n
- za pomocą skryptu, który powtarza swoje argumenty w sposób bardziej precyzyjny niżecho
.Pamiętaj, że doskonale radzi sobie ze spacjami i znakami nowej linii,
co byłoby szczególnie kłopotliwe w przypadku następującego wspólnego rozwiązania:
Notatkiźródło
Nie zgadzam się z
bash
podstawkami, ponieważbash
wraz z zestawem narzędzi * nix jest dość biegły w obsłudze plików (w tym tych, których nazwy mają osadzone białe znaki).W rzeczywistości
find
daje ci drobną kontrolę nad wyborem plików do przetworzenia ... Po stronie bash naprawdę musisz tylko zdać sobie sprawę, że musisz zrobić łańcuchybash words
; zazwyczaj przy użyciu „podwójnych cudzysłowów” lub innego mechanizmu, takiego jak IFS lub find's{}
Zauważ, że w większości / wielu sytuacjach nie musisz ustawiać i resetować IFS; po prostu użyj IFS lokalnie, jak pokazano w poniższych przykładach. Wszystkie trzy dobrze trzymają białe znaki. Także nie trzeba „standard” strukturę pętli, ponieważ znaleźć na
\;
to skutecznie pętla; wystarczy umieścić logikę pętli w funkcji bash (jeśli nie wywołujesz standardowego narzędzia).I jeszcze dwa przykłady
'znajdź
also allows you to pass multiple filenames as args to you script ..(if it suits your need: use
+instead
\; `)źródło