Próbuję wywołać skrypt z listą nazw plików zebranych przez find
. Nic specjalnego, po prostu coś takiego:
$ myscript `find . -name something.txt`
Problem polega na tym, że niektóre ścieżki zawierają spacje, więc przy interpretacji argumentów dzielą się one na dwie nieprawidłowe nazwy. Zazwyczaj otaczałbym te nazwiska cudzysłowami, ale tutaj są one wstawiane przez rozszerzenie cudzysłowu. Próbowałem filtrować dane wyjściowe find
i otaczać każdą nazwę pliku cudzysłowami, ale zanim bash je zobaczy, jest już za późno, aby je rozebrać i są one traktowane jako część nazwy pliku:
$ myscript `find . -name something.txt | sed 's/.*/"&"/'`
No such file or directory: '"./somedir/something.txt"'
Tak, to są zasady dotyczące przetwarzania wiersza poleceń, ale jak go obejść?
To zawstydzające, ale nie potrafię znaleźć odpowiedniego podejścia. W końcu wymyśliłem, jak to zrobić xargs -0 -n 10000
... ale jest to tak brzydki hack, że wciąż chcę zapytać: jak zacytować wyniki rozwinięcia cudzysłowu lub uzyskać ten sam efekt w inny sposób?
Edit: Byłem zdezorientowany o tym, że xargs
robi zebrać wszystkie argumenty w jednym liście argumentów, chyba że jest to powiedziano inaczej lub ograniczenia systemowe mogą być przekroczone. Dziękujemy wszystkim za wyprostowanie mnie! Inni, pamiętajcie o tym, czytając zaakceptowaną odpowiedź, ponieważ nie została ona wskazana bezpośrednio.
Przyjąłem odpowiedź, ale moje pytanie pozostaje: Czy nie ma sposobu na ochronę przestrzeni w $(...)
rozszerzeniu wstecznym (lub )? (Zauważ, że zaakceptowane rozwiązanie jest odpowiedzią bez bashu).
IFS="
nowa linia"
). Ale czy istnieje potrzeba wykonania skryptu dla wszystkich nazw plików? Jeśli nie, rozważ użycie polecenia find, aby wykonać skrypt dla każdego pliku.Odpowiedzi:
Możesz wykonać następujące czynności, używając niektórych implementacji
find
ixargs
podobnych.lub standardowo po prostu
find
:Przykład
Powiedz, że mam następujący przykładowy katalog.
Powiedzmy teraz, że mam to
./myscript
.Teraz, gdy uruchomię następujące polecenie.
Lub kiedy używam drugiego formularza w ten sposób:
Detale
znajdź + xargs
Powyższe 2 metody, choć wyglądają inaczej, są zasadniczo takie same. Pierwszym z nich jest pobranie wyniku z find, podzielenie go za pomocą NULLs (
\0
) za pomocą-print0
przełącznika find.xargs -0
Jest specjalnie zaprojektowana, aby wejście to jest podzielone za pomocą wartości null. Że niestandardowe składnia została wprowadzona przez GNUfind
ixargs
ale jest również obecnie w kilku innych podobnych najnowszych BSD.-r
Opcja jest wymagana, aby uniknąć wywoływaniamyscript
jeżelifind
stwierdzi nic z GNUfind
, ale nie z BSD.UWAGA: Całe to podejście opiera się na fakcie, że nigdy nie przejedziesz struny, która jest wyjątkowo długa. Jeśli tak, to drugie wywołanie
./myscript
zostanie rozpoczęte wraz z pozostałymi wynikami wyszukiwania.znajdź za pomocą +
Jest to standardowy sposób (choć został dodany stosunkowo niedawno (2005) do implementacji GNU
find
). Możliwość robienia tego, co robimy,xargs
jest dosłownie wbudowanafind
. Więcfind
znajdzie listę plików, a następnie przekaże tę listę tylu argumentów, ile może zmieścić się w poleceniu określonym później-exec
(zwróć uwagę, że w tym przypadku{}
może być tylko przed+
chwilą), uruchamiając polecenia kilka razy, jeśli to konieczne.Dlaczego nie ma cytatu?
W pierwszym przykładzie używamy skrótu, całkowicie unikając problemów z cytowaniem, używając NULL do oddzielenia argumentów. Gdy
xargs
podana jest ta lista, nakazuje się podział na NULL skutecznie chroniąc nasze indywidualne atomy dowodzenia.W drugim przykładzie trzymamy wyniki wewnętrznie,
find
więc wie, czym jest każdy atom pliku, i zagwarantuje, że odpowiednio z nimi poradzimy, unikając w ten sposób nikogo, kto by je cytował.Maksymalny rozmiar wiersza poleceń?
To pytanie pojawia się od czasu do czasu, więc jako bonus dodam je do tej odpowiedzi, głównie po to, by znaleźć je w przyszłości. Możesz użyć,
xargs
aby zobaczyć, jaki jest limit środowiska:źródło
+
argumentach przeciwkofind
(a ty też używasz+
prozy, więc za pierwszym razem przegapiłem twoje wyjaśnienie). Ale bardziej do rzeczy, źle zrozumiałem, coxargs
domyślnie !!! Przez trzy dekady używania Unixa do tej pory nie miałem z niego żadnego zastosowania, ale wydawało mi się, że znam mój zestaw narzędzi ...xargs
to diabeł rozkazu. Musisz to przeczytać ifind
wielokrotnie otwierać strony podręcznika, aby sprawdzić, co potrafią. Maj przełączników jest przeciwny do siebie, co powoduje zamieszanie.$(..)
zamiast tego użyj teraz. Automatycznie obsługuje zagnieżdżanie cudzysłowów itp. Backticks są przestarzałe.Powyżej
find
znajduje wszystkie pasujące nazwy plików i podaje je jako argumentymyscript
. Działa to z nazwami plików niezależnie od spacji i innych nieparzystych znaków.Jeśli wszystkie nazwy plików mieszczą się w jednym wierszu, skrypt myscript jest wykonywany raz. Jeśli lista jest zbyt długa, aby mogła ją obsłużyć powłoka, find w razie potrzeby uruchomi myscript wiele razy.
WIĘCEJ: ile plików mieści się w linii poleceń?
man find
mówi, żefind
buduje to wiersze poleceń „w taki sam sposób, jak xargs buduje swoje”. Iman xargs
że limity są zależne od systemu i że można je ustalić, uruchamiającxargs --show-limits
. (getconf ARG_MAX
jest również możliwość). W Linuksie limit wynosi zwykle (ale nie zawsze) około 2 milionów znaków na linię poleceń.źródło
Kilka dodatków do dobrej odpowiedzi @ slm.
Ograniczenie wielkości argumentów dotyczy
execve(2)
wywołania systemowego (w rzeczywistości dotyczy to skumulowanego rozmiaru argumentów oraz ciągów i wskaźników środowiska). Jeślimyscript
jest napisany w języku, który może interpretować twoja powłoka, być może nie musisz go uruchamiać , możesz po prostu go zinterpretować bez konieczności wykonywania innego tłumacza.Jeśli uruchomisz skrypt jako:
To jest jak:
Tyle że jest interpretowane przez dziecko bieżącej powłoki, zamiast jej wykonania (co ostatecznie wiąże się z wykonaniem
sh
(lub cokolwiek, co określa linia she-bang, jeśli taka istnieje) z jeszcze większą liczbą argumentów).Oczywiście nie można używać
find -exec {} +
tego.
polecenia, ponieważ.
jest to wbudowane polecenie powłoki, które musi być wykonywane przez powłokę, a nie przezfind
.Dzięki
zsh
jest to łatwe:Lub:
Mimo
zsh
to nie będziesz potrzebować,find
ponieważ większość jego funkcji jest wbudowana wzsh
globbing.bash
zmienne nie mogą jednak zawierać znaków NUL, więc musisz znaleźć inny sposób. Jednym ze sposobów może być:Możesz również użyć rekurencyjnego globowania w stylu zsh z
globstar
opcją w wersjibash
4.0 i nowszych:Zauważ, że
**
następowały dowiązania symboliczne do katalogów, dopóki nie zostało to naprawione wbash
4.3. Pamiętaj też, żebash
nie implementujezsh
kwalifikatorów globowania, więc nie uzyskasz wszystkich jego funkcjifind
.Inną alternatywą byłoby użycie GNU
ls
:Powyższych metod można również użyć, jeśli chcesz mieć pewność, że
myscript
zostanie wykonane tylko raz (błąd, jeśli lista argumentów jest zbyt duża). W najnowszych wersjach Linuksa możesz podnieść, a nawet znieść to ograniczenie na liście argumentów za pomocą:(Rozmiar stosu 1GiB, którego jedna czwarta może być wykorzystana do listy arg + env).
(bez limitu)
źródło
W większości systemów istnieje ograniczenie długości wiersza poleceń przekazywanego do dowolnego programu za pomocą
xargs
lub-exec command {} +
. Odman find
:Inwokacje będą znacznie mniejsze, ale nie gwarantuje się, że będą jednym. To, co powinieneś zrobić, to odczytać nazwy plików rozdzielone przez NUL ze skryptu ze standardowego wejścia, możliwe na podstawie argumentu wiersza poleceń
-o -
. Zrobiłbym coś takiego:i odpowiednio zaimplementuj argumenty opcji
myscript
.źródło
xargs
działa. Twoje rozwiązanie jest rzeczywiście najbardziej niezawodne, ale w tym przypadku jest nadmierne.Nie, nie ma. Dlaczego?
Bash nie ma pojęcia, co należy chronić, a co nie.
W pliku / potoku unix nie ma tablic. To tylko strumień bajtów. Polecenie wewnątrz
``
lub$()
wyprowadza strumień, który bash połyka i traktuje jak pojedynczy ciąg. W tym momencie masz tylko dwie możliwości: umieść go w cudzysłowie, aby zachować go jako jeden ciąg lub umieść go nagiego, aby bash podzielił go zgodnie ze skonfigurowanym zachowaniem.Więc jeśli chcesz, aby tablica była zdefiniowana, musisz zdefiniować format bajtów, który ma tablicę, a takie narzędzia lubią
xargs
ifind
robią: jeśli uruchomisz je z-0
argumentem, będą działać zgodnie z formatem tablicy binarnej, który kończy elementy za pomocą bajt zerowy, dodając semantykę do inaczej nieprzezroczystego strumienia bajtów.Niestety
bash
nie można skonfigurować podziału ciągów w bajcie zerowym. Dzięki /unix//a/110108/17980 za pokazanie nam, żezsh
może.xargs
Chcesz, aby polecenie wykonało się raz, i powiedziałeś, że to
xargs -0 -n 10000
rozwiązuje twój problem. Nie zapewnia, że jeśli masz więcej niż 10000 parametrów, twoje polecenie uruchomi się więcej niż raz.Jeśli chcesz, aby był ściśle uruchamiany raz lub nie powiódł się, musisz podać
-x
argument i-n
argument większy niż-s
argument (naprawdę: wystarczająco duży, aby cała wiązka argumentów o zerowej długości plus nazwa polecenia nie pasowały-s
wielkości). ( man xargs , patrz fragment daleko poniżej)System, na którym aktualnie pracuję, ma ograniczony stos do około 8 milionów, więc oto mój limit:
grzmotnąć
Jeśli nie chcesz włączać zewnętrznego polecenia, pętla podczas odczytu zasilająca tablicę, jak pokazano na /unix//a/110108/17980 , jest jedynym sposobem na podzielenie rzeczy przez bash bajt zerowy.
Pomysł na źródło skryptu,
( . ... "$@" )
aby uniknąć limitu wielkości stosu, jest fajny (próbowałem, działa!), Ale prawdopodobnie nie jest ważny w normalnych sytuacjach.Użycie specjalnego fd dla potoku procesu jest ważne, jeśli chcesz przeczytać coś innego ze standardowego wejścia, ale w przeciwnym razie nie będziesz go potrzebował.
Zatem najprostszy „rodzimy” sposób na codzienne potrzeby gospodarstwa domowego:
Jeśli podoba ci się drzewo procesu czyste i ładne, ta metoda pozwala to zrobić
exec mynonscript "${files[@]}"
, co usuwa proces bash z pamięci, zastępując go wywoływanym poleceniem.xargs
zawsze pozostanie w pamięci podczas działania wywoływanej komendy, nawet jeśli komenda uruchomi się tylko raz.To, co przemawia przeciwko natywnej metodzie bash, to:
bash nie jest zoptymalizowany do obsługi tablicy.
człowiek xargs :
źródło
ls "what is this"
vsls `echo '"what is this"'`
. Ktoś zaniedbał wdrożenie przetwarzania cytatów w wyniku cudzysłowów.$(...)
rozszerzeniu wstecznym (lub )?”, Więc wydaje się właściwe ignorowanie przetwarzania, które nie jest wykonywane w tej sytuacji.bash
nie obsługuje tego natywnie, jak się wydajezsh
.printf "%s\0"
ixargs -0
przeszedłem dookoła sytuacji cytowania, w której narzędzie pośrednie przepuszczałoby parametry przez ciąg analizowany przez powłokę. Cytowanie zawsze wraca, by cię ugryźć.