Linux. Утилита xargs
Утилита xargs позволяет выполнять команды с аргументами из стандартного ввода или из файла. Обычно используется в сочетании с другими командами через конвейер. Использование xargs позволяет передать вывод одной команды в качестве аргументов второй команды.
$ первая_команда | xargs [опции] вторая_команда
Утилита xargs разбивает вывод первой команды на отдельные строки по пробелам или символам новой строки. После чего вызывает вторую команду, передавая ей в качестве аргументов вывод первой команды. По умолчанию вывод первой команды целиком передается в качестве аргумента второй команды. Но с помощью опций можно настроить передачу аргументов небольшими порциями — тогда вторая команда будет вызвана несколько раз. Если вторая команда не указана, по умолчанию используется /bin/echo.
Давайте создадим три файла
$ echo "file1 file2 file3" | xargs touch $ ls file1 file2 file3
Давайте теперь их удалим
$ ls | xargs rm
Опции утилиты
Чтобы вывести вторую команду на терминал перед ее выполнением — используем опцию -t или --verbose
$ echo "file1 file2 file3" | xargs -t touch touch file1 file2 file3 $ ls file1 file2 file3
Чтобы получить подтверждение выполнения второй команды — используем опцию -p или --interactive
$ echo "file1 file2 file3" | xargs -p touch touch file1 file2 file3 ?...n
Эта опция полезна при выполнении потенциально опасных команд.
Опция -n или --max-args указывает количество аргументов, передаваемых второй команде. Вторая команда будет вызываться несколько раз, пока не закончатся все аргументы.
$ ls | xargs -n1 -t rm rm file1 rm file2 rm file3
Чтобы не удалить лишнего — можно запрашивать разрешение на удаление каждого файла
$ ls | xargs -n1 -p rm rm file1 ?...n rm file2 ?...y rm file3 ?...y
Опция -l или --max-lines указывает количество строк, передаваемых одной команде. Это в какой-то мере похоже на использование опции --max-args. Вывод первой команды может содержать несколько строк, разделенных символом перевода строки. А каждая строка может содержать несколько слов, разделенных пробелами.
$ ls 'file one' 'file two' 'file three'
$ ls | xargs -p rm rm file one file two file three ?...y rm: невозможно удалить 'file': Нет такого файла или каталога rm: невозможно удалить 'one': Нет такого файла или каталога rm: невозможно удалить 'file': Нет такого файла или каталога rm: невозможно удалить 'two': Нет такого файла или каталога rm: невозможно удалить 'file': Нет такого файла или каталога rm: невозможно удалить 'three': Нет такого файла или каталога
Первая попытка удаления файлов была неудачной, давайте попробуем еще раз — укажем, что передавать rm надо строку целиком.
$ ls | xargs -p -l1 rm rm file one ?...y rm: невозможно удалить 'file': Нет такого файла или каталога rm: невозможно удалить 'one': Нет такого файла или каталога rm file two ?...y rm: невозможно удалить 'file': Нет такого файла или каталога rm: невозможно удалить 'two': Нет такого файла или каталога rm file three ?...y rm: невозможно удалить 'file': Нет такого файла или каталога rm: невозможно удалить 'three': Нет такого файла или каталога
Опять неудача! Нам нужно, чтобы имена файлов, состоящие из нескольких слов, были заключены в кавычки. Давайте попробуем еще раз.
$ ls | xargs -p -i rm {} rm 'file one' ?...y rm 'file two' ?...y rm 'file three' ?...y
Опция -i или --replace позволяет не просто передать второй команде аргументы из вывода первой команды, а вставить их в конкретное место, обозначенное двумя фигурными скобками {}. Причем вывод первой команды ls будет разбиваться только по символу новой строки.
$ printf '%s\n' file1 file2 file3 | xargs --verbose --replace bash -c 'touch {}; echo "content of file {}" > {}; cat {}' bash -c 'touch file1; echo "content of file file1" > file1; cat file1' content of file file1 bash -c 'touch file2; echo "content of file file2" > file2; cat file2' content of file file2 bash -c 'touch file3; echo "content of file file3" > file3; cat file3' content of file file3
Важный момент — при использовании опции -i или --replace утилита xargs будет разбивать вывод первой команды на отдельные строки только по символам новой строки. Поэтому в примере выше мы используем printf, чтобы между именами файлов был символ новой строки. Мы создаем три новых файла, в каждый записываем строку и выводим содержимое каждого файла.
Вместо двух фигурных скобок {} можно использовать любую строку, если указать ее в качестве значения опции -i или --replace.
$ ls 'file one' 'file two'
$ ls | xargs -p -l1 -iARG rm ARG rm 'file one' ?...n rm 'file two' ?...n
$ ls | xargs -p -l1 --replace=ARG rm ARG rm 'file one' ?...n rm 'file two' ?...n
Опции --max-lines (или -l), --replace (или -i) и --max-args (или -n) являются взаимоисключающими. Если некоторые из них указаны одновременно, тогда xargs будет использовать опцию, указаную последней в командной строке.
Исключением из этого правила является указание опции -n со значением 1 после опции -i — в этом случае опция -n игнорируется.
Опция -d или --delimiter позволяет задать другой разделитель вместо пробела.
$ echo -n "file1,file2,file3" | xargs -d ',' -n1 -t touch touch file1 touch file2 touch file3
Обратите внимание на опцию -n для echo, которая запрещает вывод символа новой строки после "file1,file2,file3". Без этой опции последний аргумент будет не таким file3, а вот таким 'file3'$'\n'.
Опция -a или --arg-file позволяет читать аргументы из файла вместо стандартного ввода.
$ cat ips.txt 8.8.8.8 1.1.1.1
$ xargs -t -l1 -a ips.txt ping -c1 ping -c1 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=109 time=28.0 ms .......... ping -c1 1.1.1.1 PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data. 64 bytes from 1.1.1.1: icmp_seq=1 ttl=57 time=10.8 ms ..........
Вместе с find
Утилита xargs чаще всего используется в сочетании с командой find. Команда find позволяет найти определенные файлы, а xargs — выполнинить над ними нужные операции.
Чтобы избежать проблем с именами файлов, которые содержат пробелы, нужно использовать опцию -print0, которая заставит find выводить имя файла с нулевым символом в конце. Этот вывод может быть правильно интерпретирован xargs с помощью опции -0 или --null.
Удалить временные файлы, которые не изменялись более 7 дней
$ find /tmp -type f -mtime +7 -print0 | xargs --null rm -f
Параллельный запуск
Утилита позволяет запустить параллельно нескольких команд. Для этого используется опция -P или --max-procs, которая указывает максимальное количество процессов, которые будут выполняться одновременно.
Запустим архивирование нескольких больших файлов
$ fallocate -l 100M large-size-1.txt $ fallocate -l 100M large-size-2.txt $ fallocate -l 100M large-size-3.txt $ ls -la итого 307208 drwxrwxr-x 2 evgeniy evgeniy 4096 июн 14 13:25 . drwxr-x--- 22 evgeniy evgeniy 4096 июн 14 13:06 .. -rw-rw-r-- 1 evgeniy evgeniy 104857600 июн 14 13:25 large-size-1.txt -rw-rw-r-- 1 evgeniy evgeniy 104857600 июн 14 13:25 large-size-2.txt -rw-rw-r-- 1 evgeniy evgeniy 104857600 июн 14 13:25 large-size-3.txt
$ ls | xargs -n1 --max-procs=3 gzip $ ls -la итого 308 drwxrwxr-x 2 evgeniy evgeniy 4096 июн 14 13:26 . drwxr-x--- 22 evgeniy evgeniy 4096 июн 14 13:06 .. -rw-rw-r-- 1 evgeniy evgeniy 101808 июн 14 13:25 large-size-1.txt.gz -rw-rw-r-- 1 evgeniy evgeniy 101808 июн 14 13:25 large-size-2.txt.gz -rw-rw-r-- 1 evgeniy evgeniy 101808 июн 14 13:25 large-size-3.txt.gz
Рекомендуется использовать опцию -n или -l — в противном случае есть вероятность, что команда будет запущена только один раз.
Примеры
Изменить имена всех файлов в директории, добавив в конце расширение .tmp
$ ls one.txt two.txt
$ ls | xargs -t -i mv {} {}.tmp mv one.txt one.txt.tmp mv two.txt two.txt.tmp
$ ls | sed "s/^.*$/& &.tmp/" | xargs -t -n2 mv mv one.txt one.txt.tmp mv two.txt two.txt.tmp
Сохранить все файлы в архив в директории backup, после чего удалить их все
$ ls one.txt two.txt
$ ls | tr '\n' ' ' | xargs -t -i bash -c 'tar -zcf ~/backup/$(date +%Y.%m.%d-%H.%M.%S).tar.gz {} && rm {}' bash -c 'tar -zcf ~/backup/$(date +%Y.%m.%d-%H.%M.%S).tar.gz one.txt two.txt && rm one.txt two.txt '
$ ls ~/backup/ 2022.12.01-09.00.07.tar.gz 2022.12.02-09.00.05.tar.gz 2022.12.03-09.00.06.tar.gz
Обратите внимание, что команда ls разделяет вывод файлов символами перевода строки. А поскольку используется опция -i — утилита xargs будет разбивать на строки отдельные имена файлов. Нам же нужно получить все имена файлов одной строкой, чтобы передать их tar и rm. Поэтому с помощью команды tr заменяем перевод строки на пробел.
Справка
Справку по использованию утилиты можно получить с помощью опции --help.
$ xargs --help Использование: xargs [ПАРАМЕТР]… КОМАНДА [НАЧАЛЬНЫЕ-АРГУМЕНТЫ]… Выполняет КОМАНДУ с аргументами НАЧАЛЬНЫЕ-АРГУМЕНТЫ и дополнительными аргументами, прочитанными из стандартного ввода. Обязательность или необязательность аргументов для длинного параметра аналогична и для соответствующего короткого параметра. -0, --null Элементы разделяются null, а не пробельным символом. Отключается обработка кавычек, обратной косой черты и логического EOF -a, --arg-file=ФАЙЛ Читать аргументы из ФАЙЛа, а не стандартного ввода -d, --delimiter=СИМВОЛ Входные элементы разделяются СИМВОЛом, а не пробельным символом. Отключается обработка кавычек, обратной косой черты и логического EOF -E END Задаёт логический EOF строки. Если в входных данных встречается END в виде строки, то оставшиеся входные данные игнорируются (игнорируются, если указан -0 или -d) -e [END], --eof[=END] Эквивалентно -E END, если указан END. Иначе, строка конец-файла отсутствует -I R тоже, что и --replace=R -i,--replace=[R] Заменить R в НАЧАЛЬНЫХ-АРГУМЕНТАХ именами, прочитанными из стандартного ввода. Если R не не указан, то предполагается {} -L,-l, --max-lines=MAX-LINES Использовать не более MAX-LINES непробельных входных строк на командную строку -l[MAX-LINES] Подобен -L, но по умолчанию используется не более одной непробельной входной строки на командную строку, если не указан MAX-LINES -n, --max-args=MAX-ARGS Использовать не более MAX-ARGS аргументов на на командую строку -o, --open-tty Reopen stdin as /dev/tty in the child process before executing the command; useful to run an interactive application. -P, --max-procs=MAX-PROCS Выполнять до MAX-PROCS процессов одновременно -p, --interactive Спрашивать перед выполнением команд --process-slot-var=VAR Добавлять переменную окружения VAR в процессы-потомки -r, --no-run-if-empty Если аргументы не указаны, то КОМАНДУ не выполнять. Если не указан этот параметр, то КОМАНДА будет выполнена не менее одного раза. -s, --max-chars=MAX-CHARS Ограничить длину командной строки до MAX-CHARS --show-limits Показать ограничения на длину командной строки -t, --verbose Выводить команды перед их выполнением -x, --exit Выйти, если превышен размер (см. -s) --help Показать эту справку и выйти --version output version information and exit