Інколи виникає потреба об’єднати кілька команд в Linux, але одна з них не підтримує конвеєрне введення. Саме тут на допомогу приходить `xargs`, інструмент, що перетворює вихід однієї команди на параметри для іншої.
Усі стандартні утиліти Linux використовують три потоки даних: стандартний вхідний потік (stdin), стандартний вихідний потік (stdout) та стандартний потік помилок (stderr).
Ці потоки оперують текстом. Введення (stdin) передається команді у вигляді тексту, а відповідь (stdout) виводиться в термінал також як текст. Повідомлення про помилки (stderr) також відображаються в терміналі текстовому форматі.
Однією з переваг Linux і Unix-подібних систем є можливість перенаправляти вихід stdout однієї команди на вхід stdin іншої. При цьому перша команда не турбується про те, що її вихід не йде у вікно термінала, а другій байдуже, що її введення не з клавіатури.
Попри те, що всі команди Linux мають три стандартні потоки, не всі здатні приймати стандартний вихід іншої команди як вхід. Це означає, що ви не можете напряму передавати їм дані.
`xargs` – це утиліта для створення конвеєрів виконання з використанням стандартних потоків даних. За допомогою `xargs` ми можемо примусити такі команди, як `echo`, `rm` та `mkdir` приймати стандартний вхід як аргументи.
Як працює команда xargs
`xargs` отримує конвеєрне введення або введення з файлу. Потім він використовує це введення як параметри для вказаної команди. Якщо команду не вказано, `xargs` за замовчуванням використовує `echo`.
Розглянемо приклад, щоб продемонструвати, як `xargs` завжди створює один рядок виводу, навіть якщо вхідні дані складаються з декількох рядків.
Використовуючи параметр `-1` (виводити по одному файлу в рядку) з командою `ls`, ми отримаємо список імен файлів в один стовпчик.
ls -1 ./*.sh
Зображено файли сценаріїв оболонки в поточному каталозі.
Ми бачимо один стовпчик, як і очікувалось. А що станеться, якщо ми перенаправимо цей вивід через `xargs`?
ls -1 ./*.sh | xargs
Вивід з’явиться в терміналі у вигляді одного довгого текстового рядка.
Саме завдяки цій можливості `xargs` може передавати параметри іншим командам.
Застосування xargs з wc
За допомогою `xargs` можна легко передати декілька файлів команді `wc` для підрахунку слів, символів та рядків у них.
ls *.page | xargs wc
Ось що відбувається:
1. `ls` перелічує файли `*.page` та передає список у `xargs`.
2. `xargs` передає імена файлів у `wc`.
3. `wc` обробляє імена файлів, ніби їх отримано як параметри командного рядка.
Виводиться статистика для кожного файлу разом із загальною сумою.
Інтерактивний режим xargs
Можна використовувати параметр `-p` (інтерактивний), щоб `xargs` запитував підтвердження перед виконанням кожної команди.
Якщо передати список імен файлів для команди `touch` через `xargs`, то `touch` створить ці файли.
echo 'one two three' | xargs -p touch
Відображається команда, яку потрібно виконати, і `xargs` очікує на введення `y` або `Y`, або `n` чи `N` з подальшим натисканням Enter.
Якщо просто натиснути Enter, це буде інтерпретовано як `n`. Команда виконається тільки якщо ввести `y` або `Y`.
Після введення `y` та натискання Enter, файли буде створено. Перевіримо це за допомогою команди `ls`:
ls one two three
Використання xargs з декількома командами
За допомогою параметра `-I` (початкові аргументи) можна використовувати декілька команд з `xargs`.
Цей параметр визначає “рядок заміни”. Значення, що були надані в `xargs`, вставляються в командний рядок у місці, де зустрічається маркер для рядка заміни.
Скористаємося командою `tree` для перегляду підкаталогів у поточному каталозі. Параметр `-d` примушує `tree` ігнорувати файли та виводити тільки каталоги.
tree -d
Маємо один підкаталог під назвою `images`.
У файлі `directories.txt` є імена деяких каталогів, які ми хочемо створити. Подивимося на його вміст за допомогою `cat`:
cat directories.txt
Ми будемо використовувати цей файл як вхід для `xargs`. Команда для виконання виглядатиме так:
cat directories.txt | xargs -I % sh -c 'echo %; mkdir %'
Пояснення:
1. `cat directories.txt |`: це перенаправляє вміст файлу `directories.txt` (тобто нові імена каталогів) у `xargs`.
2. `xargs -I %`: це встановлює “рядок заміни” з маркером `%`.
3. `sh -c`: це запускає нову підоболонку. Параметр `-c` вказує оболонці читати команди з командного рядка.
4. `’echo %; mkdir %’`: кожний `%` буде замінено іменами каталогів, що передані `xargs`. Команда `echo` виведе ім’я каталогу; а `mkdir` створить відповідний каталог.
Каталоги виводяться один за одним.
Знову використаємо `tree` для перевірки, чи створено каталоги:
tree -d
Копіювання файлів у декілька місць
За допомогою `xargs` можна копіювати файли в декілька місць однією командою.
Передамо імена двох каталогів у `xargs` як вхідні параметри. Вкажемо `xargs` передавати команді лише один з цих параметрів за раз.
В даному випадку команда – `cp`. Тому фактично виклик `cp` відбудеться двічі, кожен раз з одним з двох каталогів як параметром. Параметр `-n` (максимальне число) вказує `xargs` передавати не більше одного параметра за раз. Встановимо його значення у `1`.
Також використаємо параметр `-v` (докладний вивід) з `cp`, щоб бачити, що відбувається.
echo ~/Backups/ ~/Documents/page-files/ | xargs -n 1 cp -v ./*.page
Файли копіюються в два каталоги, по одному за раз. `cp` виводить інформацію про кожну операцію копіювання.
Видалення файлів у вкладених каталогах
Якщо імена файлів містять пробіли чи інші спеціальні символи, наприклад, символи нового рядка, `xargs` може їх неправильно інтерпретувати. Цю проблему можна вирішити, використовуючи параметр `-0` (нульовий термінатор). Це вказує `xargs` використовувати нульовий символ як роздільник імен файлів.
У цьому прикладі ми використаємо команду `find`. `find` має власний варіант для роботи з пробілами та іншими спеціальними символами в іменах файлів. Це параметр `-print0` (повне ім’я, нульовий символ).
find . -name "*.png" -type f -print0 | xargs -0 rm -v -rf "{}"
Пояснення:
1. `find . -name “*.png” -type f`: команда `find` шукатиме в поточному каталозі `.` об’єкти, імена яких відповідають `*.png`, і які є файлами (тип `-f`).
2. `-print0`: імена файлів будуть завершуватись нульовим символом, що дозволить коректно обробляти пробіли та спеціальні символи.
3. `xargs -0`: `xargs` також сприйматиме, що імена файлів закінчуються нульовим символом, і пробіли та спеціальні символи не створять проблем.
4. `rm -v -rf “{}”`: команда `rm` виводитиме інформацію про те, що відбувається (`-v`). Вона буде рекурсивною (`-r`), тобто шукатиме файли у вкладених підкаталогах, а також видалятиме файли без підтвердження (`-f`). Символи `{}` замінюються на кожне ім’я файлу.
Шукаються всі вкладені підкаталоги, а файли, що відповідають шаблону пошуку, видаляються.
Видалення вкладених каталогів
Припустимо, потрібно видалити набір вкладених підкаталогів. Команда `tree` допоможе їх побачити.
tree -d
find . -name "level_one" -type d -print0 | xargs -0 rm -v -rf "{}"
Ця команда використає `find` для рекурсивного пошуку в поточному каталозі. Шукатиметься каталог з іменем `level_one`. Імена каталогів передаються через `xargs` до команди `rm`.
Єдиною суттєвою відмінністю від попередньої команди є пошуковий термін, який тепер є ім’ям каталогу, а `-type d` вказує `find` шукати каталоги, а не файли.
Під час видалення виводиться ім’я кожного каталогу. Перевіримо за допомогою `tree`:
tree -d
Усі вкладені підкаталоги видалені.
Видалення всіх файлів, окрім одного типу
Можна використати `find`, `xargs` та `rm` для видалення всіх файлів, окрім одного конкретного типу. Це трохи нетипово, бо ми вказуємо тип файлів, які хочемо *зберегти*, а не ті, що потрібно видалити.
Параметр `-not` вказує `find` повернути імена файлів, які *не* відповідають шаблону пошуку. Знову скористаємось параметром `-I` (початкові аргументи) з `xargs`. Цього разу маркером заміни рядка буде `{}`, аналогічно до `%`, що використовувався раніше.
find . -type f -not -name "*.sh" -print0 | xargs -0 -I {} rm -v {}
Перевіримо за допомогою `ls`. У каталозі залишилися тільки файли, що відповідають шаблону `*.sh`.
ls -l
Створення архіву з xargs
`find` можна використовувати для пошуку файлів та передачі їх через `xargs` до `tar`, щоб створити архів.
Пошук буде вестись в поточному каталозі. Шаблон пошуку – `*.page`, тобто шукатимуться файли `.page`.
find ./ -name "*.page" -type f -print0 | xargs -0 tar -cvzf page_files.tar.gz
Файли відображаються, як очікується, під час створення архівного файлу.
Інструмент для передачі даних
Інколи потрібні інструменти для “зшивання” різних елементів разом. `xargs` заповнює прогалину між командами, що можуть виводити інформацію, і командами, що не призначені для її отримання.
І `xargs`, і `find` мають велику кількість опцій. Для отримання детальнішої інформації радимо переглянути їхні сторінки керівництва (`man xargs`, `man find`).