Jak używać> w poleceniu xargs?

160

Chcę znaleźć polecenie bash, które pozwoli mi grepować każdy plik w katalogu i zapisać dane wyjściowe tego grepa w oddzielnym pliku. Przypuszczam, że zrobiłbym coś takiego

ls -1 | xargs -I{} "grep ABC '{}' > '{}'.out"

ale, o ile wiem, xargs nie lubi podwójnych cudzysłowów. Jeśli jednak usunę podwójne cudzysłowy, polecenie przekieruje dane wyjściowe całego polecenia do pojedynczego pliku o nazwie „{}” zamiast do serii pojedynczych plików.

Czy ktoś wie, jak to zrobić za pomocą xargs? Właśnie użyłem tego scenariusza grep jako przykładu, aby zilustrować mój problem z xargs, więc wszelkie rozwiązania, które nie używają xargs, nie są dla mnie odpowiednie.

Jesse Shieh
źródło

Odpowiedzi:

201

Nie popełniaj tego błędu:

sh -c "grep ABC {} > {}.out"

Zepsuje się to w wielu warunkach, w tym w dziwnych nazwach plików i nie można tego poprawnie zacytować. Twój {}zawsze musi być całkowicie oddzielone pojedynczym argumentem polecenia do kodu uniknąć błędów wtryskowych. Co musisz zrobić, to:

xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}

Dotyczy xargsjak również find.

Nawiasem mówiąc, nigdy nie używaj xargs bez tej -0opcji (chyba że do bardzo rzadkich i kontrolowanych jednorazowych interaktywnych zastosowań, w których nie martwisz się o zniszczenie swoich danych).

Nie analizuj też ls. Zawsze. Użyj globbingu lub findzamiast tego:http://mywiki.wooledge.org/ParsingLs

Używaj finddo wszystkiego, co wymaga rekursji i prostej pętli z globem dla wszystkiego innego:

find /foo -exec sh -c 'grep "$1" > "$1.out"' -- {} \;

lub nierekurencyjne:

for file in *; do grep "$file" > "$file.out"; done

Zwróć uwagę na prawidłowe użycie cytatów.

lhunath
źródło
Głosował za, ale mam wątpliwości regd. nie używać xargsbez -0: dotyczy to tylko sytuacji, gdy potokujesz findwyjście z xargs, prawda? kiedy zrobię, xargs -a <input_file>jak mam tego użyć? Większość poleceń, takich jak grepwyjścia zi \nnie, \0.Jedynym sposobem, w jaki mogę to obejść, jest trponowne użycie, aby to naprawić. Ale dlaczego ważne jest, aby używać go tylko z -0?
legends2k
3
@ legends2k, ponieważ kiedy nie używasz -0, xargsweźmie twoje nazwy plików i połamie w nich wszystkie spacje, cudzysłowy i ukośniki odwrotne. Powinieneś po prostu zapomnieć o xargsnarzędziu. Jeśli masz linie, użyj pętli bash, aby je iterować: while read line; do <command> "$REPLY"; done < file-with-lineslubcommand | while ...
lhunath
1
Wow, nie wiedziałem o tym, dzięki za szczegóły! Tak więc ze względu na przenośność (ponieważ nie wszystkie xargssą GNU), xargsnależy tego unikać, chyba że można go używać z -0. Dziękuję Ci.
legends2k
1
Chociaż doceniam szczegółowe wyjaśnienie tego konkretnego przypadku użycia, pytanie dotyczy przekierowania wyjścia xargs, co nie zawsze wiąże się z analizowaniem lslub użyciem sh -c. To w najmniejszym stopniu nie odpowiada na pytanie, ale jest pierwszym wynikiem w Google na to pytanie, tylko zwiększającym zamieszanie.
pandasauce
1
@Ihunath, cześć, twoja odpowiedź działa dobrze dla mnie. Ale czy możesz podać szczegółowe wyjaśnienie lub linki xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}? W szczególności zasady osadzania (podwójnych) cudzysłowów i znaku „-” na końcu. Dziękuję
Scott Yang
40

Rozwiązanie bez xargs jest następujące:

find . -mindepth 1 -maxdepth 1 -type f -exec sh -c "grep ABC '{}' > '{}.out'" \;

... i to samo można zrobić z xargs , jak się okazuje:

ls -1 | xargs -I {} sh -c "grep ABC '{}' > '{}.out'"

Edycja : pojedyncze cudzysłowy dodane po uwadze przez lhunatha .

Stephan202
źródło
Powiedział, że chce użyć xargs. Opublikowałem również rozwiązanie bez niego, ale skasowałem je, gdy zobaczyłem, że potrzebuje xargs.
Zifre
Masz rację. Powodem, dla którego opublikowałem swoją odpowiedź, było to, że lepiej mieć alternatywne rozwiązanie, aby wykonać zadanie, niż żadne. Okazuje się, że skierowało mnie to na właściwą ścieżkę do znalezienia pożądanej odpowiedzi (czyli sztuczki sh -c).
Stephan202
14

Zakładam, że twój przykład jest tylko przykładem i możesz potrzebować> do innych rzeczy. GNU Parallel http://www.gnu.org/software/parallel/ może być Twoim ratunkiem. Nie wymaga dodatkowego cytowania, o ile nazwy plików nie zawierają \ n:

ls | parallel "grep ABC {} > {}.out"

Jeśli masz nazwy plików zawierające \ n:

find . -print0 | parallel -0 "grep ABC {} > {}.out"

Jako dodatkowy bonus otrzymujesz równoległe prace.

Obejrzyj filmy wprowadzające, aby dowiedzieć się więcej: http://pi.dk/1

Podczas instalacji trwającej 10 sekund spróbujemy wykonać pełną instalację; jeśli to się nie powiedzie, instalacja osobista; jeśli to się nie powiedzie, minimalna instalacja:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 3374ec53bacb199b245af2dda86df6c9
12345678 3374ec53 bacb199b 245af2dd a86df6c9
$ md5sum install.sh | grep 029a9ac06e8b5bc6052eac57b2c3c9ca
029a9ac0 6e8b5bc6 052eac57 b2c3c9ca
$ sha512sum install.sh | grep f517006d9897747bed8a4694b1acba1b
40f53af6 9e20dae5 713ba06c f517006d 9897747b ed8a4694 b1acba1b 1464beb4
60055629 3f2356f3 3e9c4e3c 76e3f3af a9db4b32 bd33322b 975696fc e6b23cfb
$ bash install.sh

Jeśli chcesz przenieść go na serwer, na którym nie ma zainstalowanego GNU Parallel, spróbuj parallel --embed.

Ole Tange
źródło