To pytanie jest inspirowane przez
Dlaczego używanie pętli powłoki do przetwarzania tekstu jest uważane za złą praktykę?
Widzę te konstrukty
for file in `find . -type f -name ...`; do smth with ${file}; done
i
for dir in $(find . -type d -name ...); do smth with ${dir}; done
używane tutaj prawie codziennie, nawet jeśli niektórzy ludzie poświęcają czas na komentowanie tych postów, wyjaśniając, dlaczego tego rodzaju rzeczy należy unikać ...
Widząc liczbę takich postów (i fakt, że czasami te komentarze są po prostu ignorowane) Pomyślałem, że równie dobrze mogę zadać pytanie:
Dlaczego zapętlanie find
wyjścia jest złą praktyką i jaki jest właściwy sposób uruchamiania jednej lub więcej komend dla każdej nazwy / ścieżki pliku zwracanej przez find
?
Odpowiedzi:
Problem
łączy dwie niezgodne rzeczy.
find
wypisuje listę ścieżek plików rozdzielonych znakami nowej linii. Podczas gdy operator split + glob, który jest wywoływany, gdy pozostawiasz go bez$(find .)
cudzysłowu w kontekście tej listy, dzieli go na znaki$IFS
(domyślnie obejmuje znak nowej linii, ale także spację i tabulator (i NUL wzsh
)) i wykonuje globowanie dla każdego wynikowego słowa (z wyjątkiem inzsh
) (a nawet nawias klamrowy w pochodnych ksh93 lub pdksh!).Nawet jeśli to zrobisz:
To nadal źle, ponieważ znak nowego wiersza jest tak samo ważny jak każdy na ścieżce pliku. Wynik działania
find -print
po prostu nie jest niezawodny po przetworzeniu (z wyjątkiem użycia skomplikowanej sztuczki, jak pokazano tutaj ).Oznacza to również, że powłoka musi w
find
pełni zapisać dane wyjściowe , a następnie podzielić je + glob (co oznacza przechowywanie tego wyniku po raz drugi w pamięci), zanim zacznie się pętla nad plikami.Zauważ, że
find . | xargs cmd
ma podobne problemy (tam puste miejsca, nowa linia, pojedynczy cudzysłów, podwójny cudzysłów i ukośnik odwrotny (a przy niektórychxarg
implementacjach bajty nie tworzące części prawidłowych znaków) stanowią problem)Więcej poprawnych alternatyw
Jedynym sposobem użycia
for
pętli na wyjściufind
byłoby użyciezsh
obsługiIFS=$'\0'
i:(wymienić
-print0
ze-exec printf '%s\0' {} +
dlafind
wdrożeń, które nie obsługują niestandardowe (ale dość powszechne w dzisiejszych czasach)-print0
).Tutaj poprawnym i przenośnym sposobem jest użycie
-exec
:Lub jeśli
something
może przyjąć więcej niż jeden argument:Jeśli potrzebujesz tej listy plików do obsługi przez powłokę:
(uwaga: może rozpocząć się więcej niż jeden
sh
).W niektórych systemach możesz użyć:
chociaż ma to niewielką przewagę nad standardową składnią i oznacza
something
, żestdin
albo jest potokiem, albo/dev/null
.Jednym z powodów, dla których warto skorzystać, może być skorzystanie z
-P
opcji GNUxargs
do przetwarzania równoległego.stdin
Problem może być także pracowali GNUxargs
z-a
wersji z powłok nośnych zmiany procesu:na przykład, aby uruchomić do 4 jednoczesnych wywołań
something
każdego z nich, biorąc 20 argumentów pliku.Za pomocą
zsh
lubbash
innym sposobem na zapętlenie wyjściafind -print0
jest użycie:read -d ''
czyta rekordy rozdzielane znakiem NUL zamiast rekordów rozdzielanych znakiem nowej linii.bash-4.4
i powyżej może również przechowywać pliki zwrócone przezfind -print0
w tablicy z:zsh
Odpowiednik (który ma tę zaletę, że zachowaniefind
jest stan wyjściowy):Za pomocą
zsh
można przełożyć większośćfind
wyrażeń na kombinację globowania rekurencyjnego z kwalifikatorami globu. Na przykład zapętleniefind . -name '*.txt' -type f -mtime -1
byłoby:Lub
(uwaga na to, że
--
tak jak w przypadku**/*
, ścieżki plików nie zaczynają się od./
, więc-
na przykład mogą zaczynać się od).ksh93
ibash
ostatecznie dodał wsparcie dla**/
(choć nie bardziej zaawansowanych form rekurencyjnego globowania), ale wciąż nie kwalifikatory globu, co sprawia, że użycie**
tam jest bardzo ograniczone. Uważaj również, abybash
przed wersją 4.3 po zejściu z drzewa katalogów następowały dowiązania symboliczne.Podobnie jak w przypadku zapętlania
$(find .)
, oznacza to również przechowywanie całej listy plików w pamięci 1 . Może to być pożądane, jednak w niektórych przypadkach, gdy nie chcesz, aby twoje działania na plikach miały wpływ na wyszukiwanie plików (np. Gdy dodasz więcej plików, które same mogą zostać znalezione).Inne kwestie dotyczące niezawodności / bezpieczeństwa
Warunki wyścigu
Teraz, jeśli mówimy o niezawodności, musimy wspomnieć o warunkach wyścigu między czasem
find
/zsh
znajduje plik i sprawdza, czy spełnia on kryteria i czas, w którym jest używany ( wyścig TOCTOU ).Nawet schodząc z drzewa katalogów, należy uważać, aby nie podążać za dowiązaniami symbolicznymi i robić to bez wyścigu TOCTOU.
find
(find
Przynajmniej GNU ) robi to, otwierając katalogi używającopenat()
odpowiednichO_NOFOLLOW
flag (jeśli są obsługiwane) i pozostawiając deskryptor pliku otwarty dla każdego katalogu,zsh
/bash
/ksh
nie rób tego. Wobec tego, gdy osoba atakująca może w odpowiednim czasie zastąpić katalog dowiązaniem symbolicznym, możesz zejść do niewłaściwego katalogu.Nawet jeśli
find
poprawnie opuści katalog, z,-exec cmd {} \;
a tym bardziej z-exec cmd {} +
, pocmd
wykonaniu, na przykład gdycmd ./foo/bar
lubcmd ./foo/bar ./foo/bar/baz
, do czasu , kiedy zostaniecmd
wykorzystany./foo/bar
, atrybutybar
mogą już nie spełniać kryteriówfind
, ale co gorsza,./foo
mogły być zastąpiony przez dowiązanie symboliczne do innego miejsca (a okno wyścigu jest znacznie większe,-exec {} +
gdziefind
czeka na wystarczającą ilość plików, aby zadzwonićcmd
).Niektóre
find
implementacje mają (jeszcze niestandardowe)-execdir
predykaty, aby złagodzić drugi problem.Z:
find
chdir()
s do katalogu nadrzędnego pliku przed uruchomieniemcmd
. Zamiast wywoływaćcmd -- ./foo/bar
, wywołujecmd -- ./bar
(cmd -- bar
z pewnymi implementacjami, stąd--
), więc./foo
unika się problemu zmiany na dowiązanie symboliczne. To sprawia, że korzystanie z poleceń jestrm
bezpieczniejsze (nadal może usunąć inny plik, ale nie plik z innego katalogu), ale nie polecenia, które mogą modyfikować pliki, chyba że zostały zaprojektowane tak, aby nie podążały za dowiązaniami symbolicznymi.-execdir cmd -- {} +
czasami też działa, ale z kilkoma implementacjami, w tym z niektórymi wersjami GNUfind
, jest to równoważne z-execdir cmd -- {} \;
.-execdir
ma również tę zaletę, że omija niektóre problemy związane ze zbyt głębokimi drzewami katalogów.W:
rozmiar podanej ścieżki
cmd
wzrośnie wraz z głębokością katalogu, w którym znajduje się plik. Jeśli rozmiar ten wzrośniePATH_MAX
((np. 4k w systemie Linux), wówczas każde wywołanie systemowe, którecmd
działa na tej ścieżce, zakończy sięENAMETOOLONG
błędem.Za
-execdir
pomocą./
przekazywana jest tylko nazwa pliku (ewentualnie z prefiksem )cmd
. Same nazwy plików w większości systemów plików mają znacznie niższy limit (NAME_MAX
) niżPATH_MAX
, więcENAMETOOLONG
prawdopodobieństwo wystąpienia błędu jest mniejsze.Bajty kontra postacie
Często pomijany przy rozważaniu bezpieczeństwa,
find
a bardziej ogólnie przy obsłudze nazw plików w ogóle, jest fakt, że w większości systemów uniksowych nazwy plików są ciągami bajtów (dowolna wartość bajtu oprócz 0 w ścieżce pliku i w większości systemów ( Te oparte na ASCII, na razie zignorujemy te rzadkie oparte na EBCDIC) 0x2f to separator ścieżki).Aplikacje muszą zdecydować, czy chcą traktować te bajty jako tekst. I zazwyczaj tak jest, ale generalnie tłumaczenie z bajtów na znaki odbywa się na podstawie ustawień regionalnych użytkownika i środowiska.
Oznacza to, że dana nazwa pliku może mieć różną reprezentację tekstu w zależności od ustawień regionalnych. Na przykład sekwencja bajtów
63 f4 74 e9 2e 74 78 74
byłaby przeznaczonacôté.txt
dla aplikacji interpretującej tę nazwę pliku w ustawieniach regionalnych, w których zestaw znaków to ISO-8859-1, orazcєtщ.txt
w ustawieniach regionalnych, w których zestaw znaków to IS0-8859-5.Gorzej. W lokalizacji, w której zestaw znaków to UTF-8 (obecnie norma), 63 f4 74 e9 2e 74 78 74 po prostu nie można było przypisać do postaci!
find
to jedna z takich aplikacji, która traktuje nazwy plików jako tekst dla swoich predykatów-name
/-path
(i więcej, takich jak-iname
lub-regex
z niektórymi implementacjami).Oznacza to na przykład, że ma kilka
find
implementacji (w tym GNUfind
).nie znajdzie naszego
63 f4 74 e9 2e 74 78 74
pliku powyżej, gdy zostanie wywołany w ustawieniach regionalnych UTF-8, ponieważ*
(który pasuje do 0 lub więcej znaków , a nie bajtów) nie może pasować do tych znaków innych niż znaki.LC_ALL=C find...
obejdzie problem, ponieważ ustawienia regionalne C sugerują jeden bajt na znak i (ogólnie) gwarantują, że wszystkie wartości bajtów zostaną odwzorowane na znak (aczkolwiek być może niezdefiniowane dla niektórych wartości bajtów).Teraz, gdy chodzi o zapętlanie tych nazw plików z powłoki, ten bajt vs znak może również stać się problemem. Zazwyczaj widzimy 4 główne rodzaje powłok w tym zakresie:
Te, które wciąż nie są świadome wielu bajtów
dash
. Dla nich bajt odwzorowuje postać. Na przykład w UTF-8côté
ma 4 znaki, ale 6 bajtów. W lokalizacji, w której UTF-8 jest zestawem znaków, wfind
z powodzeniem znajdzie pliki, których nazwa składa się z 4 znaków zakodowanych w UTF-8, aledash
zgłosi długości od 4 do 24.yash
: przeciwieństwo. Zajmuje się tylko postaciami . Wszystkie dane wejściowe są wewnętrznie tłumaczone na znaki. Tworzy najbardziej spójną powłokę, ale oznacza również, że nie radzi sobie z dowolnymi sekwencjami bajtów (tymi, które nie tłumaczą się na poprawne znaki). Nawet w ustawieniach regionalnych C nie radzi sobie z wartościami bajtów powyżej 0x7f.w ustawieniach regionalnych UTF-8 zawiedzie na przykład nasz wcześniejszy ISO-8859-1
côté.txt
.Te lubią
bash
lubzsh
gdzie stopniowo dodawana jest obsługa wielu bajtów. Powrócą do rozważania bajtów, których nie można zmapować na znaki tak, jakby były postaciami. Nadal mają kilka błędów tu i tam, zwłaszcza z mniej popularnymi wielobajtowymi zestawami znaków, takimi jak GBK lub BIG5-HKSCS (te są dość paskudne, ponieważ wiele ich znaków wielobajtowych zawiera bajty z zakresu 0-127 (jak znaki ASCII) ).Takie jak
sh
FreeBSD (przynajmniej 11) lubmksh -o utf8-mode
obsługujące wiele bajtów, ale tylko dla UTF-8.Notatki
1 Dla kompletności możemy wspomnieć o
zsh
hackerskim sposobie zapętlania plików za pomocą rekurencyjnego globowania bez zapisywania całej listy w pamięci:+cmd
to kwalifikator glob, który wywołujecmd
(zazwyczaj funkcję) z bieżącą ścieżką pliku w$REPLY
. Funkcja zwraca wartość true lub false, aby zdecydować, czy plik powinien zostać wybrany (i może również modyfikować$REPLY
lub zwracać kilka plików w$reply
tablicy). Tutaj wykonujemy przetwarzanie w tej funkcji i zwracamy false, aby plik nie został wybrany.źródło
find
się zachowywać bezpiecznie. Globowanie jest domyślnie bezpieczne, podczas gdy find jest domyślnie niebezpieczne.Prosta odpowiedź brzmi:
Ponieważ nazwy plików mogą zawierać dowolny znak.
W związku z tym nie ma znaku do wydrukowania, którego można by niezawodnie użyć do rozgraniczenia nazw plików.
Znaki nowej linii są często używane (niepoprawnie) do rozgraniczenia nazw plików, ponieważ umieszczanie znaków nowej linii w nazwach plików jest niezwykłe .
Jeśli jednak zbudujesz oprogramowanie w oparciu o arbitralne założenia, w najlepszym wypadku po prostu nie będziesz w stanie poradzić sobie z nietypowymi przypadkami, aw najgorszym wypadku otworzysz się na złośliwe exploity, które dają kontrolę nad twoim systemem. To kwestia solidności i bezpieczeństwa.
Jeśli możesz pisać oprogramowanie na dwa różne sposoby, a jeden z nich poprawnie obsługuje przypadki brzegowe (nietypowe dane wejściowe), ale drugi jest łatwiejszy do odczytania, możesz argumentować, że istnieje kompromis. (Nie zrobiłbym tego. Wolę poprawny kod.)
Jeśli jednak poprawna, solidna wersja kodu jest również łatwa do odczytania, nie ma usprawiedliwienia dla pisania kodu, który zawodzi w przypadkach skrajnych. Jest tak w przypadku
find
i potrzeby uruchomienia polecenia dla każdego znalezionego pliku.Mówiąc dokładniej: w systemie UNIX lub Linux nazwy plików mogą zawierać dowolny znak z wyjątkiem znaku
/
(który służy jako separator komponentu ścieżki) i nie mogą zawierać bajtu zerowego.Bajt zerowy jest zatem jedynym prawidłowym sposobem ograniczania nazw plików.
Ponieważ GNU
find
zawiera element-print0
główny, który będzie używał bajtu zerowego do rozgraniczenia nazw plików, które drukuje, GNUfind
można bezpiecznie używać z GNUxargs
i jego-0
flagą (i-r
flagą) do obsługi danych wyjściowychfind
:Nie ma jednak dobrego powodu, aby używać tego formularza, ponieważ:
find
jest przeznaczony do uruchamiania poleceń na znalezionych plikach.Ponadto GNU
xargs
wymaga-0
i-r
, podczas gdy FreeBSDxargs
wymaga tylko-0
(i nie ma-r
opcji), a niektórexargs
nie obsługują-0
wcale. Najlepiej więc trzymać się funkcji POSIXfind
(patrz następny rozdział) i pomijaćxargs
.Jeśli chodzi o punkt 2
find
- zdolność uruchamiania poleceń na znalezionych plikach - myślę, że Mike Loukides powiedział najlepiej:Określone zastosowania POSIX
find
Aby uruchomić jedno polecenie dla każdego znalezionego pliku, użyj:
Aby uruchomić wiele poleceń w sekwencji dla każdego znalezionego pliku, gdzie drugie polecenie powinno być uruchomione tylko, jeśli pierwsze polecenie się powiedzie, użyj:
Aby uruchomić jedno polecenie na wielu plikach jednocześnie:
find
w połączeniu zsh
Jeśli potrzebujesz użyć poleceń powłoki w poleceniu, takich jak przekierowanie wyjścia lub usunięcie rozszerzenia z nazwy pliku lub czegoś podobnego, możesz skorzystać z
sh -c
konstrukcji. Powinieneś wiedzieć o tym kilka rzeczy:Nigdy nie osadzaj
{}
bezpośrednio wsh
kodzie. Pozwala to na wykonanie dowolnego kodu ze złośliwie spreparowanych nazw plików. Poza tym POSIX nawet nie określa, że w ogóle będzie działać. (Zobacz następny punkt.)Nie używaj
{}
wiele razy lub używaj go jako części dłuższego argumentu. To nie jest przenośne. Na przykład nie rób tego:find ... -exec cp {} somedir/{}.bak \;
Cytując specyfikacje POSIX dla
find
:Argumenty występujące po ciągu poleceń powłoki przekazanym do
-c
opcji są ustawiane na parametry pozycyjne powłoki, zaczynając od$0
. Nie zaczynam od$1
.Z tego powodu dobrze jest dołączyć wartość „obojętną”
$0
, na przykładfind-sh
, która będzie używana do raportowania błędów z odradzanej powłoki. Pozwala to również na użycie konstrukcji takich jak"$@"
przekazywanie wielu plików do powłoki, natomiast pominięcie wartości dla$0
oznaczałoby, że pierwszy przekazany plik byłby ustawiony na,$0
a zatem nie został uwzględniony"$@"
.Aby uruchomić pojedyncze polecenie powłoki dla pliku, użyj:
Jednak zwykle poprawia wydajność obsługi plików w pętli powłoki, dzięki czemu nie spawnujesz powłoki dla każdego znalezionego pliku:
(Pamiętaj, że
for f do
jest to równoważne zfor f in "$@"; do
każdym z parametrów pozycyjnych i obsługuje je kolejno - innymi słowy, używa każdego z znalezionych plikówfind
, niezależnie od jakichkolwiek znaków specjalnych w ich nazwach).Dalsze przykłady prawidłowego
find
użytkowania:(Uwaga: przedłuż tę listę.)
źródło
find
danych wyjściowych - w której musisz uruchomić polecenia w bieżącej powłoce (np. Ponieważ chcesz ustawić zmienne) dla każdego pliku. W tym przypadkuwhile IFS= read -r -u3 -d '' file; do ... done 3< <(find ... -print0)
jest to najlepszy idiom, jaki znam. Uwagi:<( )
nie jest przenośny - użyj bash lub zsh. Ponadto,-u3
i3<
są tam na wypadek, gdyby cokolwiek w pętli próbowało odczytać standardowe wejście.find ... -exec
rozmowy. Lub po prostu użyj globu powłoki, jeśli zajmie się twoim przypadkiem użycia.filelist=(); while ... do filelist+=("$file"); done ...
).find
wyjściu lub nawet gorzejls
. Robię to codziennie bez problemów. Wiem o opcjach -print0, --null, -z lub -0 wszelkiego rodzaju narzędzi. Ale nie marnowałbym czasu na używanie ich w interaktywnym wierszu poleceń, chyba że jest to naprawdę potrzebne. Można to również zauważyć w swojej odpowiedzi.Ta odpowiedź dotyczy bardzo dużych zestawów wyników i dotyczy głównie wydajności, na przykład podczas pobierania listy plików w wolnej sieci. W przypadku małych ilości plików (powiedzmy kilka 100, a może nawet 1000 na dysku lokalnym) większość z nich jest dyskusyjna.
Równoległość i wykorzystanie pamięci
Oprócz innych udzielonych odpowiedzi, związanych z problemami z separacją, istnieje jeszcze inny problem
Część wewnątrz backticków należy najpierw w pełni ocenić, zanim zostanie podzielona w przypadku łamania linii. Oznacza to, że jeśli otrzymasz ogromną liczbę plików, może to spowodować uduszenie się, niezależnie od ograniczeń rozmiaru w różnych komponentach; możesz zabraknąć pamięci, jeśli nie ma żadnych ograniczeń; w każdym razie musisz poczekać, aż cała lista zostanie wypisana,
find
a następnie parsowana,for
zanim uruchomisz pierwsząsmth
.Preferowanym sposobem uniksowym jest praca z potokami, które z natury działają równolegle i które nie wymagają ogólnie dużych buforów. Oznacza to: wolałbyś,
find
aby uruchamiał się równolegle do twojegosmth
, i utrzymywał aktualną nazwę pliku w pamięci RAM, gdy przekazuje tosmth
.Jednym z co najmniej częściowo OKish tego rozwiązania jest wyżej wspomniane
find -exec smth
. Eliminuje to potrzebę przechowywania wszystkich nazw plików w pamięci i działa ładnie równolegle. Niestety uruchamia również jedensmth
proces na plik. Jeślismth
może działać tylko na jednym pliku, to właśnie tak musi być.Jeśli to w ogóle możliwe, optymalnym rozwiązaniem byłoby
find -print0 | smth
, zesmth
jest w stanie przetworzyć nazwy plików na swoim stdin. Następnie masz tylko jedensmth
proces, bez względu na liczbę plików, i musisz buforować tylko niewielką ilość bajtów (niezależnie od tego, co dzieje się wewnętrzne buforowanie potoków) między dwoma procesami. Oczywiście jest to raczej nierealne, jeślismth
jest to standardowe polecenie Unix / POSIX, ale może być podejściem, jeśli piszesz je samodzielnie.Jeśli nie jest to możliwe,
find -print0 | xargs -0 smth
jest to prawdopodobnie jedno z lepszych rozwiązań. Jak wspomniano w komentarzach @ dave_thompson_085,xargs
dzieli argumenty na wiele seriismth
po osiągnięciu limitów systemowych (domyślnie w zakresie 128 KB lub dowolnego limitu narzuconego przezexec
system) i ma opcje wpływające na liczbę pliki są przekazywane do jednego wywołaniasmth
, co pozwala znaleźć równowagę między liczbąsmth
procesów a początkowym opóźnieniem.EDYCJA: usunęła pojęcie „najlepszego” - trudno powiedzieć, czy pojawi się coś lepszego. ;)
źródło
find ... -exec smth {} +
jest rozwiązaniem.find -print0 | xargs smth
nie działa wcale, alefind -print0 | xargs -0 smth
(uwaga-0
) lubfind | xargs smth
jeśli nazwy plików nie mają cudzysłowów lub odwrotny ukośnik uruchamia jedensmth
z tyloma nazwami plików, ile jest dostępnych i mieści się na jednej liście argumentów ; jeśli przekroczysz maxargs, działasmth
tyle razy, ile potrzeba, aby obsłużyć wszystkie podane argumenty (bez limitu). Możesz ustawić mniejsze „fragmenty” (a więc nieco wcześniejszą równoległość) za pomocą-L/--max-lines -n/--max-args -s/--max-chars
.Jednym z powodów jest to, że białe znaki wrzucają klucz w prace, dzięki czemu plik „foo bar” jest oceniany jako „foo” i „bar”.
Działa ok, jeśli zamiast tego użyto opcji -exec
źródło
find
gdy istnieje opcja wykonania polecenia na każdym pliku, jest to z pewnością najlepsza opcja.-exec ... {} \;
versus-exec ... {} +
for file in "$(find . -type f)"
aecho "${file}"
następnie będzie działać nawet z białymi spacjami, inne znaki specjalne, jak sądzę, powodują więcej problemówfor file in "$(find . -type f)";do printf '%s %s\n' name: "${file}";done
które powinny (według ciebie) wydrukować każdą nazwę pliku w osobnym wierszu poprzedzonymname:
. Nie maPonieważ wyjście dowolnego polecenia jest pojedynczym ciągiem, ale twoja pętla potrzebuje tablicy ciągów do zapętlenia. Powodem, dla którego „działa”, jest to, że pociski zdradzająco dzielą cię na biały znak.
Po drugie, chyba że potrzebujesz konkretnej funkcji
find
, pamiętaj, że twoja powłoka najprawdopodobniej już sama może rozwinąć rekurencyjny wzorzec globu i, co najważniejsze, rozszerzy się do odpowiedniej tablicy.Przykład bash:
To samo u ryb:
Jeśli potrzebujesz funkcji
find
, upewnij się, że dzielisz tylko na NUL (np.find -print0 | xargs -r0
Idiom).Ryby mogą iterować dane rozdzielane wartościami NUL. Więc to nie jest wcale takie złe:
Jako ostatnia mała gotcha, w wielu powłokach (oczywiście nie Fish), zapętlenie nad wyjściem polecenia spowoduje, że ciało pętli stanie się podpowłoką (co oznacza, że nie można ustawić zmiennej w żaden sposób widoczny po zakończeniu pętli), co jest nigdy tego, czego chcesz.
źródło
zsh
na początku lat 90. (choć będziesz tego potrzebować**/*
).fish
podobnie jak wcześniejsze implementacje równoważnej funkcji bash podążają za dowiązaniami symbolicznymi podczas schodzenia z drzewa katalogów. Zobacz wynik ls *, ls ** i ls ***, aby zobaczyć różnice między implementacjami.Pętla wyników wyszukiwania nie jest złą praktyką - złą praktyką (w tej i wszystkich sytuacjach) jest zakładanie, że dane wejściowe mają określony format, a nie wiedza (testowanie i potwierdzanie), że jest to określony format.
tldr / cbf:
find | parallel stuff
źródło