Znam grep
polecenie i uczę się o jego funkcjach xargs
, więc przeczytałem tę stronę, która zawiera kilka przykładów użycia xargs
polecenia.
Mylę mnie ostatni przykład, przykład 10. Mówi on: „Polecenie xargs wykonuje polecenie grep, aby znaleźć wszystkie pliki (spośród plików udostępnionych przez polecenie find), które zawierały ciąg„ stdlib.h ””
$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...
Jaka jest jednak różnica w zwykłym użyciu
$ find . -name '*.c' | grep 'stdlib.h'
?
Oczywiście wciąż mam problemy z tym, co dokładnie robi xargs, więc doceniam każdą pomoc!
command-line
grep
xargs
AlphaOmega
źródło
źródło
Odpowiedzi:
To potoczy wyjście (stdout) * od
find
do (stdin of) *grep 'stdlib.h'
jako tekst (tzn. Nazwy plików są traktowane jak tekst).grep
robi to, co zwykle i znajduje pasujące linie w tym tekście (dowolne nazwy plików, które same zawierają wzorzec). Zawartość plików nigdy nie jest odczytywana.To konstruuje polecenie,
grep 'stdlib.h'
dla którego każdy wynikfind
jest argumentem - będzie więc szukał dopasowań w każdym znalezionym plikufind
(xargs
można go traktować jako przekształcenie standardowego wejścia w argumenty dla podanych poleceń) *Użyj
-type f
w poleceniu find, w przeciwnym razie pojawią się błędy związane zgrep
dopasowaniem katalogów. Ponadto, jeśli nazwy plików mają spacje,xargs
źle się zepsują, więc użyj separatora zerowego, dodając-print0
ixargs -0
dla bardziej wiarygodnych wyników:* dodał te dodatkowe wyjaśnienia, jak sugeruje w komentarzu @cat
źródło
|
rury stdout do grep stdin , która nie jest taka sama, jak argumenty grep i daje mylące wyniki.find -name '*.c' -exec grep stdlib.h {} +
. Prawie nigdy nie używam xargs. Zaskoczony również nikt nie wspomniał, że xargs służy podobnemu celowi dogrep $(find)
zastępowania poleceń, więc napisałem własną odpowiedź. Wyjaśnienie xargs jako podstawienia poleceń z mniejszymi ograniczeniami i problemami wydaje się naturalne.find -delete
w tym specjalnym przypadku? Lub w przypadku poleceń innych niżrm
, jeśli masz GNU find,-exec some_command {} +
grupuj je w pakiety takie jak xargs, zamiast tego\;
zachowanie uruchamiania polecenia osobno dla każdego.find
uruchamia polecenie dla każdego pliku, tylko wtedy, gdy używa-exec command \;
Oba,xargs
i-exec command \+
wywoła polecenie z maksymalną liczbą argumentów dozwolonych przez system. Innymi słowy, są one równoważnexargs pobiera standardowe dane wejściowe i zamienia je w argumenty wiersza poleceń.
find . -name '*.c' | xargs grep 'stdlib.h'
jest bardzo podobny doI da takie same wyniki, o ile lista nazw plików nie będzie zbyt długa dla pojedynczego wiersza poleceń. (Linux obsługuje megabajty tekstu w jednym wierszu poleceń, więc zwykle nie potrzebujesz xargs).
Ale oba są do bani, ponieważ pękają, jeśli twoje nazwy plików zawierają spacje . Zamiast tego
find -print0 | xargs -0
działa, ale tak też jestTo nigdy nie potokuje nazw plików: umieszcza
find
je w dużej linii poleceń i uruchamiagrep
bezpośrednio.\;
zamiast+
uruchamia grep osobno dla każdego pliku, co jest znacznie wolniejsze. Nie rób tego Ale+
jest rozszerzeniem GNU, więc musiszxargs
to zrobić skutecznie, jeśli nie możesz założyć GNU find.Jeśli pominiesz
xargs
,find | grep
czy jego wzorzec pasuje do listy nazw plików, które sąfind
drukowane.Więc w tym momencie równie dobrze możesz to zrobić
find -name stdlib.h
. Oczywiście, z-name '*.c' -name stdlib.h
, nie dostaniesz żadnego wyniku, ponieważ te wzorce nie mogą się zgadzać, a domyślne zachowanie find jest zgodne z regułami AND.Zastąp
less
w dowolnym momencie procesu, aby zobaczyć, jaki wynik generuje jakakolwiek część rurociągu.Dalsza lektura: http://mywiki.wooledge.org/BashFAQ ma kilka świetnych rzeczy.
źródło
-d
ustawić separator, abyś mógł-d'\n'
obsługiwać listę rozdzielaną znakiem nowej linii, co może być przydatne, jeśli obsługujesz listę nazw plików w pliku itp. (O ile nazwy plików nie mają nowe linie w nich, to znaczy.)myfunc(){ local IFS=$'\n'; fgrep
stdlib.h` $ (find); }
działa również z tym samym efektem. Lub jako(IFS=...; cmd...)
jednowierszowa podpowłoka również działa, aby zawrzeć zmianę w IFS bez konieczności jej zapisywania / przywracania.command $( find )
takich rzeczy. Problematyczne nazwy plików ze spacjami i znakami specjalnymi mogą uszkodzić tego typu rzeczy. Przynajmniej podwójnie cytuj zastąpienie polecenia.IFS
więc jest to równoważne z używaniemxargs '-d\n'
. Ekspansja globu i przetwarzanie metaznaków powłoki ma miejsce przed efektami podstawienia polecenia, więc myślę, że jest bezpieczny nawet w przypadku nazw plików zawierających$()
lub>
. Zgodzono się, że dzielenie słów przy zastępowaniu poleceń nie jest dobrą praktyką, z wyjątkiem jednorazowego interaktywnego użycia, w którym wiesz coś o nazwach plików.command "$(find)"
Przydaje się jednak tylko wtedy, gdy oczekuje się, że wygeneruje dokładnie 1 nazwę pliku ...Zasadniczo
xargs
jest używany w przypadkach, w których należy potokować (za pomocą symbolu|
) coś z jednego polecenia do drugiego (Command1 | Command2
), ale dane wyjściowe z pierwszego polecenia nie są poprawnie odbierane jako dane wejściowe dla drugiego polecenia.Zwykle dzieje się tak, gdy drugie polecenie nie obsługuje poprawnie wprowadzania danych przez Standard In (standardowe wejście) (np .: Wiele wierszy jako danych wejściowych, sposób konfiguracji linii, znaki używane jako dane wejściowe, wiele parametrów jako dane wejściowe, typ danych odbierany jako wejście itp.). Aby dać ci szybki przykład, przetestuj następujące elementy:
Przykład 1:
ls | echo
- To nic nie da, ponieważecho
nie wie, jak obsłużyć otrzymywane dane wejściowe. Teraz w tym przypadku, jeśli korzystamyxargs
go , przetworzy dane wejściowe w sposób, który może być poprawnie obsługiwany przezecho
(np .: jako pojedynczy wiersz informacji)ls | xargs echo
- Spowoduje to wyświetlenie wszystkich informacji zls
jednego wierszaPrzykład 2:
Załóżmy, że mam wiele plików goLang w folderze o nazwie go. Szukałbym ich z czymś takim:
find go -name *.go -type f | echo
- Ale jeśli symbol rury tam iecho
na końcu, nie zadziała.find go -name *.go -type f | xargs echo
- Tutaj zadziałałoby dzięki,xargs
ale gdybym chciał każdej odpowiedzi zfind
polecenia w jednym wierszu, zrobiłbym następujące:find go -name *.go -type f | xargs -0 echo
- W tym przypadku ta sama moc wyjściowafind
byłaby pokazana przezecho
.Polecenia takie jak
cp, echo, rm, less
i inne, które wymagają lepszego sposobu obsługi danych wejściowych, zyskują, gdy są używane zxargs
.źródło
xargs
służy do automatycznego generowania argumentów wiersza poleceń opartych (zwykle) na liście plików.Rozważając kilka alternatyw dla używania
xargs
polecenia następującego:Istnieje kilka powodów, aby używać go zamiast innych opcji, które nie zostały pierwotnie wymienione w innych odpowiedziach:
find . -name '*.c' -exec grep 'stdlib.h' {}\;
wygeneruje jedengrep
proces dla każdego pliku - jest to ogólnie uważane za złą praktykę i może powodować duże obciążenie systemu, jeśli znaleziono wiele plików.grep 'stdlib.h' $(find . -name '*.c')
polecenie prawdopodobnie się nie powiedzie, ponieważ wynik$(...)
operacji przekroczy maksymalną długość wiersza poleceń powłokiJak wspomniano w innych odpowiedziach, powodem użycia
-print0
argumentu dofind
w tym scenariuszu i-0
argumentu do xargs jest to, że nazwy plików z niektórymi znakami (np. Cudzysłowy, spacje, a nawet znaki nowej linii) są nadal obsługiwane poprawnie.źródło