Xargs

xargs – polecenie uniksowych i większości uniksopodobnych systemów operacyjnych, używane do budowania i wykonywania poleceń ze standardowego wejścia. Jądro Linuxa o wersji do 2.6.23 nie może pobierać dowolnie długiej listy argumentów[1], więc zadaniem xargs jest podzielenie jej na wystarczająco małe podlisty.

Na przykład polecenie:

 rm /path/*

lub

 rm `find /path -type f`

może spowodować błąd i wyświetlenie komunikatu: "Argument list too long" jeżeli w /path znajduje się zbyt wiele plików. Jednakże poniższa wersja (odpowiednik rm `find /path -type f`) nie powinna już sprawiać kłopotów:

 find /path -type f -print0 | xargs -0 rm

W tym przykładzie, find przesyła na wejście xargs długą listę nazw plików. Xargs dokonuje podziału na podlisty i dopiero wtedy wywołuje polecenie rm dla każdej z nich. To polecenie jest wydajniejsze niż gdybyśmy chcieli napisać:

 find /path -type f -exec rm '{}' \;

co spowoduje wywołanie rm dla każdego, pojedynczego pliku. Warto jednak zwrócić uwagę na to, że w nowszych wersjach find poniższy wariant wykonuje dokładnie to samo, co wersja z użyciem xargs:

 find /path -type f -exec rm '{}' +

xargs często wykazuje się takim samym działaniem, jak backquote, lecz jest on bardziej uniwersalny i okazuje się bezpieczniejszym gdy na wejściu pojawiają się znaki białe lub specjalne. Dobrze łączyć go z poleceniami zwracającymi długie listy plików, jak chociażby find, locate, czy grep. Musimy jednak pamiętać wtedy o parametrze -0, jako że bez niego xargs nie radzi sobie najlepiej z nazwami zawierającymi ', " i spacje.

Przykłady

 find . -name "*.foo" | xargs grep bar

Równoznaczne z:

 grep bar `find . -name "*.foo"`

Zauważ, że powyższa komenda korzysta z odwróconych apostrofów (`) zamiast zwykłych ('). Jej zadaniem jest odnalezienie wszystkich plików o rozszerzeniu .foo, zawierających słowo bar, położonych w bieżącym folderze oraz w jego podkatalogach. Żadne z tych poleceń nie zadziała, jeżeli wśród nazw plików znajdują się białe znaki. Możemy rozwiązać ten problem wpisując:

 find . -name "*.foo" -print0 | xargs -0 grep bar

W ten sposób możemy wyodrębnić nazwy plików, rozwiązując tym samym problem z białymi znakami;

 find . -name "*.foo" -print0 | xargs -0 -t -r vi

Polecenie to przypomina poprzednie, jednakowoż uruchamia ono edytor vi dla każdego z plików. Flaga -t powoduje wypisanie komendy do strumienia stderr przed przystąpieniem do jej realizacji. -r jest rozszerzeniem GNU informującym, że xargs nie wykona polecenia jeżeli nie otrzyma nic na wejście.

 find . -name "*.foo" -print0 | xargs -0 -I {} mv {} /tmp/trash

Powyższa komenda korzysta z -I aby rozkazać xargs wypełnienie {} listą argumentów. Pamiętaj jednak, że nie wszystkie wersje xargs wspomagają składnię {}. W takim wypadku możesz napisać np:

 find . -name "*.foo" -print0 | xargs -0 -I xxx mv xxx /tmp/trash

Powyższe polecenie wykorzystuje xxx zamiast {} jako znacznik listy argumentów.

 find . -maxdepth 1 -type f -name "*.ogg" -print0 | xargs -0 -r cp -v -p --target-directory=/home/media

To polecenie to nic innego jak:

 cp -v -p *.ogg /home/media

lecz nie zajmuje ono tak wielu zasobów, kosztem niezwracania błędu kiedy polecenie cp otrzyma więcej plików niż może obsłużyć. Innym sposobem (w którym to sami decydujemy, gdzie umieścimy argumenty) to:

 find . -maxdepth 1 -type f -name "*.ogg" -print0 | xargs -0 -IMYFILES cp MYFILES /home/media

Znak -I w powyższej linii poleceń informuje xargs o tym z którego słowa zastępczego chcesz skorzystać (w przeciwnym wypadku doda argumenty na końcu komendy). Oprócz tego możesz użyć atrybutu -L w celu ograniczenia liczby argumentów. Wtedy polecenie będzie uruchamiane ponownie, aż do wyczerpania listy argumentów. Zaś użycie -L1 spowoduje uruchomienie programu tylko raz dla każdego z zadanych argumentów.

Problem separatorów

Wiele uniksowych narzędzi jest zorientowanych liniowo. Będą one więc działać z xargs tak długo, jak linie nie zawierają ', " lub spacji. Część uniksowych narzędzi może użyć znaku NUL jako separatora (np. perl (wymaga -0 i \0 zamiast \n), locate (wymaga użycia -0), find (wymaga użycia -print0), grep (potrzebuje -z lub -Z), sort (wymaga użycia -z)). Umieszczenie -0 po xargs rozwiązuje problem, lecz wiele innych narzędzi nie może używać znaku NUL jako separatora, np. head, tail, ls, echo, sed, tar, wc, which.

Ludzie często o tym zapominają i uznają, że xargs jest także zorientowany liniowo[2].

Poniżej zilustrowano problem separatorów:

 touch 'wazny_plik'
 touch 'mniej wazny_plik'
 ls mniej* | xargs rm
 mkdir -p '12" records'
 ls | xargs rmdir

Wykonanie poleceń spowoduje usunięcie wazny_plik i pozostawi katalog 12" records oraz plik o nazwie mniej wazny_plik nietknięte.

GNU Parallel jest zorientowanym liniowo odpowiednikiem Xargs, co umożliwia wykonanie powyższych zadań przy jego pomocy[3].

Przypisy

Linki zewnętrzne