Wydaje mi się, że wszystkie istniejące odpowiedzi na tej stronie są błędne, w tym ta oznaczona jako poprawna. Wynika to z tego, że pytanie jest niejednoznacznie sformułowane.
Podsumowanie: Jeśli chcesz wykonać polecenie „dokładnie raz dla każdej podanej linii wejściowej”, przekazując całą linię (bez nowej linii) do polecenia jako pojedynczy argument, jest to najlepszy sposób na to, aby to zrobić:
... | tr '\n' '\0' | xargs -0 -n1 ...
GNU xargs
może, ale nie musi, mieć przydatne rozszerzenia, które pozwalają ci się pozbyć tr
, ale nie są one dostępne w OS X i innych systemach UNIX.
Teraz długie wyjaśnienie…
Podczas korzystania z xargs należy wziąć pod uwagę dwa problemy:
- jak dzieli dane wejściowe na „argumenty”; i
- ile argumentów przekazać polecenie potomne naraz.
Aby przetestować zachowanie xargs, potrzebujemy narzędzia, które pokazuje, ile razy jest wykonywane i ile argumentów. Nie wiem, czy jest do tego standardowe narzędzie, ale dość łatwo możemy to napisać w bash:
#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo
Zakładając, że zapiszesz go show
w bieżącym katalogu i sprawisz, że będzie wykonywalny, oto jak to działa:
$ ./show one two 'three and four'
-> "one" "two" "three and four"
Teraz, jeśli oryginalne pytanie naprawdę dotyczy punktu 2 powyżej (tak mi się wydaje, po kilkukrotnym przeczytaniu) i należy go przeczytać w ten sposób (zmiany pogrubioną czcionką):
Jak mogę zmusić xargs do wykonania polecenia dokładnie raz dla każdego podanego argumentu wejściowego? Jego domyślne zachowanie polega na dzieleniu danych wejściowych na argumenty i wykonywaniu polecenia tak mało, jak to możliwe , przekazując wiele argumentów do każdej instancji.
wtedy odpowiedź brzmi -n 1
.
Porównajmy domyślne zachowanie xargs, które dzieli dane wejściowe wokół białych znaków i wywołuje polecenie tak mało razy, jak to możliwe:
$ echo one two 'three and four' | xargs ./show
-> "one" "two" "three" "and" "four"
i jego zachowanie z -n 1
:
$ echo one two 'three and four' | xargs -n 1 ./show
-> "one"
-> "two"
-> "three"
-> "and"
-> "four"
Z drugiej strony, jeśli pierwotne pytanie dotyczyło dzielenia danych w punkcie 1. i powinno być czytane w ten sposób (wiele osób przybywających tutaj wydaje się myśleć, że tak jest, lub mylą te dwie kwestie):
Jak mogę zmusić xargs do wykonania polecenia z dokładnie jednym argumentem dla każdego wiersza danych wejściowych? Jego domyślne zachowanie polega na dzieleniu linii wokół białych znaków .
wtedy odpowiedź jest bardziej subtelna.
Można by pomyśleć, że -L 1
może to być pomocne, ale okazuje się, że nie zmienia to analizy argumentów. Wykonuje polecenie tylko raz dla każdej linii wejściowej, z tyloma argumentami, ile było w tej linii wejściowej:
$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show
-> "one"
-> "two"
-> "three" "and" "four"
Nie tylko to, ale jeśli linia kończy się spacją, jest ona dodawana do następnej:
$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show
-> "one" "two"
-> "three" "and" "four"
Oczywiście -L
nie chodzi o zmianę sposobu, w jaki xargs dzieli dane wejściowe na argumenty.
Jedynym argumentem, który robi to w sposób wieloplatformowy (z wyłączeniem rozszerzeń GNU), jest -0
podział danych wejściowych na bajty NUL.
Następnie jest tylko kwestia przetłumaczenia nowego wiersza na NUL za pomocą tr
:
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show
-> "one " "two" "three and four"
Teraz parsowanie argumentów wygląda dobrze, w tym końcowe białe znaki.
Wreszcie, jeśli połączysz tę technikę -n 1
, uzyskasz dokładnie jedno wykonanie polecenia w wierszu wprowadzania, niezależnie od tego, co masz, co może być jeszcze innym sposobem spojrzenia na pierwotne pytanie (być może najbardziej intuicyjne, biorąc pod uwagę tytuł):
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show
-> "one "
-> "two"
-> "three and four"
find /path -type f -delete
byłoby jeszcze bardziej wydajne :)