Dlaczego xargs jest konieczne?

25

Załóżmy, że chcę usunąć wszystkie pliki z katalogu oprócz jednego o nazwie „notes.txt”. Chciałbym to zrobić z rurociągu ls | grep -v "notes.txt" | xargs rm. Dlaczego potrzebuję xargs, jeśli wyjście drugiego potoku jest wejściem, którego powinien używać rm?

Dla porównania potok echo "#include <knowledge.h>" | cat > foo.cwstawia echo tekstu do pliku bez użycia xargs. Jaka jest różnica między tymi dwoma rurociągami?

marynarz
źródło
3
Nie należy używać ls | grep -v "notes.txt" | xargs rmdo usuwania wszystkiego oprócz notes.txt, lub ogólnie, nigdy nie analizować lsdanych wyjściowych . Twoje polecenie przestanie działać, jeśli na przykład pojedynczy plik zawiera spację. Bezpieczniej byłoby rm !(notes.txt)w Bash (z shopt -s extglobsetem) lub rm ^notes.txtw Zsh (z EXTENDED_GLOB) itp.
slhck
Aby uniknąć spacji, find . -maxdepth 1 -mindepth 1 -print0 | xargs -0zamiast ls | xargs:-)
flob

Odpowiedzi:

35

Mylisz dwa bardzo różne rodzaje danych wejściowych: STDIN i argumenty. Argumenty to lista ciągów znaków przekazywanych do komendy na początku, zwykle poprzez podanie ich po nazwie komendy (np. echo these are some argumentsLub rm file1 file2). Z drugiej strony STDIN jest strumieniem bajtów (czasem tekstowym, a czasem nie), które polecenie (opcjonalnie) może odczytać po uruchomieniu. Oto kilka przykładów (uwaga, która catmoże przyjmować argumenty lub STDIN, ale robi z nimi różne rzeczy):

echo file1 file2 | cat    # Prints "file1 file2", since that's the stream of
                          # bytes that echo passed to cat's STDIN
cat file1 file2    # Prints the CONTENTS of file1 and file2
echo file1 file2 | rm    # Prints an error message, since rm expects arguments
                         # and doesn't read from STDIN

xargs można uznać za konwersję danych wejściowych w stylu STDIN na argumenty:

echo file1 file2 | cat    # Prints "file1 file2"
echo file1 file2 | xargs cat    # Prints the CONTENTS of file1 and file2

echo faktycznie robi mniej więcej odwrotnie: konwertuje swoje argumenty na STDOUT (który może być potokowany do STDIN innej komendy):

echo file1 file2 | echo    # Prints a blank line, since echo doesn't read from STDIN
echo file1 file2 | xargs echo    # Prints "file1 file2" -- the first echo turns
                                 # them from arguments into STDOUT, xargs turns
                                 # them back into arguments, and the second echo
                                 # turns them back into STDOUT
echo file1 file2 | xargs echo | xargs echo | xargs echo | xargs echo    # Similar,
                                 # except that it converts back and forth between
                                 # args and STDOUT several times before finally
                                 # printing "file1 file2" to STDOUT.
Gordon Davisson
źródło
9

catpobiera dane wejściowe od STDINi rmnie. W przypadku takich poleceń należy xargsiterować STDINwiersz po wierszu i wykonywać polecenia z parametrami wiersza polecenia.

Alex P.
źródło