Kiedy potrzebne są xargs?


134

xargsKomenda zawsze mnie dezorientuje. Czy istnieje ogólna zasada?

Rozważ dwa poniższe przykłady:

$ \ls | grep Cases | less

drukuje pliki, które pasują do „Cases”, ale zmiana polecenia touchbędzie wymagała xargs:

$ \ls | grep Cases | touch
touch: missing file operand
Try `touch --help' for more information.

$ \ls | grep Cases | xargs touch

Odpowiedzi:


143

Różnica polega na tym, jakie dane akceptuje program docelowy.

Jeśli użyjesz tylko potoku, odbiera on dane w STDIN (standardowy strumień wejściowy) jako nieprzetworzony stos danych, który może sortować według jednej linii na raz. Jednak niektóre programy nie akceptują swoich poleceń w standardzie, oczekują, że zostaną one zapisane w argumentach polecenia. Na przykład touchprzyjmuje nazwę pliku jako parametr w linii poleceń tak: touch file1.txt.

Jeśli masz program, który wyprowadza nazwy plików na standardowe wyjście i chcą wykorzystać je jako argumenty do touch, trzeba użyć xargsktóra odczytuje dane strumienia stdin i konwertuje każdy wiersz w przestrzeni oddzielonych argumenty polecenia.

Te dwie rzeczy są równoważne:

# touch file1.txt
# echo file1.txt | xargs touch

Nie używaj, xargschyba że wiesz dokładnie, co robi i dlaczego jest potrzebny. Dość często zdarza się, że jest lepszy sposób na wykonanie zadania niż xargswymuszenie konwersji. Proces konwersji jest również obarczony potencjalnymi pułapkami, takimi jak ucieczka i rozwijanie słów itp.


2
Ostrzeżenie wydaje mi się trochę sznurkiem. Z dwóch typowych opcji pobierania strumienia do wiersza poleceń ( xargsi $(...)), xargs jest znacznie bezpieczniejszy niż zastępowanie poleceń. I nie mogę sobie przypomnieć, że kiedykolwiek natrafiłem na prawidłową nazwę pliku z nową linią. Czy problemy z ucieczką i rozszerzaniem słów nie są związane z zastępowaniem poleceń, a nie xargsem?
camh

6
@camh: Są to potencjalne pułapki w obu przypadkach. W powłoce musisz się martwić, że nazwy plików zostaną podzielone na spacje, tabulatory i znaki nowej linii. W xargs musisz się martwić tylko o nowe linie. W xargs, jeśli dane wyjściowe są poprawnie sformatowane, możesz zamiast tego podzielić słowa / nazwy plików na znak NUL ( xargs -0), co jest przydatne w połączeniu z find -print0.
Ken Bloom

Czy xargswywołuje program przez powłokę z argumentami oddzielonymi spacjami, czy faktycznie konstruuje listę argumentów wewnętrznie (np. Do użycia z execv/ execp)?
detly

1
Konstruuje go wewnętrznie i używa execvp, więc jest bezpieczny. Ponadto GNU xargs (używane w systemie Linux i kilku innych) pozwala określić znak nowej linii jako separator -d \n, chociaż BSarg xargs (OSX i in.) Nie wydaje się obsługiwać tej opcji.
puszysty

72

Aby rozwinąć już udzielone odpowiedzi, xargsmożna zrobić jedną fajną rzecz, która staje się coraz ważniejsza w dzisiejszym środowisku przetwarzania wielordzeniowego i rozproszonego: może równolegle przetwarzać zadania.

Na przykład:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8

zakoduje * .wav => * .flac, używając trzech procesów jednocześnie ( -P 3).


Łał. Powinienem był to wiedzieć tydzień temu, kiedy robiłem dokładnie to samo (oprócz używania OGG) z 50GiB WAV. :)
Alois Mahdal

dlaczego nie użyć parametru -exec, który ma find?
Evgeny,

3
@ Evgeny -execParametr nie będzie przetwarzał zadań równoległych.
amfetamachina,

Warto zauważyć, że -0argument przemawiający zaxargs uznaniem NULLznaku za separator elementu wejściowego. find -print0wyprowadza elementy rozdzielane wartościami NULL. Jest to świetna praktyka w przypadku nazw plików, które mogą zawierać spacje, cudzysłowy lub inne znaki specjalne.
Dan Dascalescu

24

xargs jest szczególnie użyteczny, gdy masz listę ścieżek plików na stdin i chcesz coś z nimi zrobić. Na przykład:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Przeanalizujmy to krok po kroku:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....

Innymi słowy, nasze dane wejściowe to lista ścieżek, z którymi chcemy coś zrobić.

Aby dowiedzieć się, co xargs robi z tymi ścieżkami, fajną sztuczką jest dodanie echoprzed poleceniem:

$ git ls-files "*.tex" | xargs -n 1 echo sed -i "s/color/colour/g"
sed -i "s/color/colour/g" tex/ch1/intro.tex
sed -i "s/color/colour/g" tex/ch1/motivation.tex
....

-n 1Argumentem uczyni xargs kolei każdą linię do polecenia własnych. sed -i "s/color/colour/g"Polecenie zastąpi wszystkie wystąpienia colorze colourdla określonego pliku.

Pamiętaj, że działa to tylko wtedy, gdy nie masz spacji na ścieżkach. Jeśli to zrobisz, powinieneś użyć ścieżek zakończonych zerem jako danych wejściowych do xargs, przekazując -0flagę. Przykładem użycia może być:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"

Co robi to samo, co opisano powyżej, ale działa również, jeśli na jednej ze ścieżek jest spacja.

Działa to z każdym poleceniem, które generuje nazwy plików jako dane wyjściowe, takie jak findlub locate. Jeśli jednak zdarzy się, że użyjesz go w repozytorium git z dużą ilością plików, bardziej efektywne może być użycie go git grep -lzamiast git ls-files, na przykład:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

git grep -l "color" "*.tex"Komenda da listę plików „* .tex” zawierających frazę „kolor”.


1
To prawda, ale jeśli się tego nauczyłeś, powinieneś także dowiedzieć się, dlaczego zapętlanie wyników wyszukiwania jest złą praktyką?
Wildcard

6

Twój pierwszy argument dość dobrze ilustruje różnicę.

\ls | grep Cases | lesspozwala przeglądać listę nazw plików utworzonych przez lsi grep. Nie ma znaczenia, że ​​są to nazwy plików, to tylko tekst.

\ls | grep Cases | xargs lesspozwala przeglądać pliki, których nazwy są tworzone przez pierwszą część polecenia. xargspobiera listę nazw plików jako dane wejściowe i polecenia w linii poleceń, a uruchamia polecenie z nazwami plików w swojej linii komend.

Kiedy rozważa wykorzystanie xargs, należy pamiętać, że oczekuje wejścia sformatowane w dziwny sposób: rozdzielany białe znaki, z \, 'i "służy do cytowania (w nietypowy sposób, bo \to nie specjalne cytaty wewnątrz). Używaj tylko xargswtedy, gdy twoje nazwy plików nie zawierają białych znaków lub \'".


@Gilles: xargs ma -0, --nullopcję obejścia problemu ze spacjami (jest bardzo prawdopodobne, że dowiedziałem się o tym od ciebie :), więc zakładam, że masz na myśli xargwywołanie braku opcji , ale jestem zaskoczony odniesieniem do cytatów. Czy masz link lub przykład na ten temat? .. (ps. | xargs lessto przydatna „sztuczka” +1 .. dzięki ..
Peter.O

4

W twoim przykładzie nie musisz wcale używać, xargsponieważ findzrobi dokładnie i bezpiecznie to, co chcesz zrobić.

Dokładnie to, czego chcesz użyć findto:

find -maxdepth 1 -name '*Cases*' -exec touch {} +

W tym przykładzie -maxdepth 1oznacza tylko wyszukiwanie w bieżącym katalogu, nie schodź do żadnych podkatalogów; domyślnie find będzie szukał we wszystkich podkatalogach (co często jest tym, czego chcesz), chyba że ograniczysz go z maxdepth. Jest {}to nazwa pliku, który zostanie zastąpiony na swoim miejscu i +jest jednym z dwóch znaczników końca polecenia, drugim jest ;. Różnica między nimi polega na tym, ;że polecenie wykonuje się na każdym pliku pojedynczo, natomiast +polecenie wykonuje się na wszystkich plikach jednocześnie. Należy jednak pamiętać, że powłoka będzie prawdopodobnie próbować interpretować ;siebie, więc trzeba będzie uciec z albo \;albo ';'. Tak, findma wiele takich niedogodności, ale jego moc nadrabia to.

Zarówno findi xargssą trudne, aby dowiedzieć się w pierwszej kolejności. Aby pomóc w nauce, xargsspróbuj użyć opcji -plub --interactive, która pokaże polecenie, które zamierza wykonać, i podpowie, czy chcesz je uruchomić.

Podobnie findmożesz użyć -okzamiast, -execaby zapytać, czy chcesz uruchomić polecenie.

Są jednak chwile, kiedy findnie będzie w stanie zrobić wszystkiego, co chcesz, i to jest miejsce xargs. -execPolecenie zaakceptuje tylko jedno wystąpienie {}, więc jeśli wystąpi błąd find -type f -exec cp {} {}.bak \;, możesz zamiast tego zrobić tak :find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

Możesz dowiedzieć się więcej o poleceniach uruchamiania w podręczniku GNU Findutils .

Wspomniałem również, że findbezpiecznie robi to, co chcesz, ponieważ podczas pracy z plikami napotkasz spacje i inne znaki, które będą powodować problemy, xargschyba że użyjesz opcji -0lub --nullwraz z czymś, co generuje elementy wejściowe zakończone znakiem null białych znaków.



@Wildcard nazwy plików ze spacjami lub znakami takimi jak 'lub "mogą być problematyczne, podczas findgdy bez problemu poradzą sobie z tymi sprawami.
aculich,

Tak, wiem. Zobacz moją odpowiedź na powiązane pytanie . Prawdopodobnie powinienem przeformułować to pytanie do oświadczenia w powyższym komentarzu lub dodać przed nim wyrażenie „Zobacz pytanie ...”. : D
Wildcard

1

xargs(wraz z find, sort, du, uniq, perli kilka innych) akceptuje przełącznik wiersza polecenia, aby powiedzieć „STDIN zawiera listę plików, oddzielone NUL (0x00) bajt”. Ułatwia to obsługę nazw plików ze spacjami i innymi zabawnymi postaciami. Nazwy plików nie zawierają wartości NUL.


2
Myślę, że masz na myśli „nazwy plików nie mogą zawierać wartości null”.
amfetamachina
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.