Недавно я был вдохновлен на написание этого поста после того когда я наткнулся на ситуации когда мне нужно было отредактировать несколько файлов из проекта и заменить одни слова на другие. Раньше я это делал через Notepad++ (CTRL + H), но это хорошо если мы знаем в каком файле нам надо заменить слово, а если этих файлов тысячи. Так вот я хочу вам представить 3 базовых инструмента системы UNIX на сегодняшний день: grep , xargs , и sed .

Вы могли уже слышать они и раньше и уже знаете как ими пользоваться то вы но верно ничего нового уже не узнаете. Но если вы хотите узнать основы по использованию этих инструментов читайте дальше.

Grep

Grep является базой Unix командной для поиска фраз как из стандартного потока так и файлов. Grep возвращает информацию по файлам которые содержат вашу фразу и немного информации о том, где они находятся. Вот пример стандартного способа использование Grep:

Grep "footer" index.html

Теперь добавим несколько опции для поиска:

  • i - Игнорирует регистр символов при сравнениях.
  • n - Выдает перед каждой строкой ее номер в файле.
  • b - Выдает номер столбца в найдено файле.
grep -i -n -b "footer" index.html

А теперь поиск по цельным словам:

  • w - Ведет поиск по цельным словам. Например при обычном поиске строки text grep может найти слово textarea . А если используется данный ключ то будут найдены только строки где есть слово text.
grep -w "text" index.html

Поиск по файлу это хорошо но лучше если мы сможем искать файлы рекурсивно по проекту.

  • r - Рекурсивный поиск файлов из текущей директории.
  • l - Выдает только имена файлов, содержащие искомое значение.
grep -r -l "footer" ./

А теперь попробуем вывести файлы в которых нет искомого значения.

  • L - Выводит имена файлов в которых нету искомого значения.
grep -r -L -i "footer" ./

Также очень часто нам надо искать в конкретных типах файлов данное значения.

  • –include=«PATTERN» - Поиск в файлах по заданному шаблону
grep -r --include="*.html" -l "footer" ./

Поиск по файлам которые начинаются на index и заканчиваются на.php

Grep -r --include="index*.php" -l "footer" ./

Теперь давайте передадим результат работы в файл.

Grep -r -l "footer" ./ > out.txt

Xargs

Это удивительная команда работает по прицепу, берет данные из стандартного ввода или из файла, разбивает их в соответствии с указанными параметрами, а затем передает другой программе в качестве аргумента. Xargs выполняется до тех пор пока данные из потока не исчерпаны. Теперь давайте рассмотрим несколько простых примеров.

Разбивай стандартный поток на аргументы.

  • –n - Указываем количество возвращаемых аргументов.
echo 1 2 3 4 | xargs -n 1

Теперь зададим разделитель аргументов.

  • –d - Указывает на разделитель для вывода аргументов.
echo "1-2-3-4" | xargs -d "-" -n 1

Вот более интересный пример где xargs используется для передачи файлов которых надо удалить.

Find . -name "*.txt"| xargs rm -rf

Вся суть использования xargs это дробления входящих аргументов для последующей передачи аргументов другим программам.

Sed

Sed - это потоковый редактор который принимает текст либо с стандартного потока, либо из текстового файла, выполняет некоторые операции над строками и затем выводит результат на стандартный вывода или в файл. Как правило, в сценариях, sed используется в конвейерной обработке данных, совместно с другими командами и утилитами.

Делаем простую замену строк из файла text.txt и сохраняем в файл out.txt.

Sed "s/text/test/g" text.txt > out.txt

А теперь сохраним все изменения в текущий файл.

  • i - для записи изменений в текущий файл.
sed -i "s/text/test/g" text.txt

Комбинируем все вмести

Комбинируя эти три команды, которые являются общими для всех unix систем, мы можем сделать довольно мощный сценарий для замены текста. Большая часть действий происходит от использования команды sed, но другие команды являются вспомогательным для построения запроса. Теперь давайте рассмотрим пример использования всех трех программ вымести:

Grep -r -l "foo" ./ | xargs sed -i "s/foo/bar/g"

Таким образом мы смогли заменить все foo на bar используя простую комбинацию из этих программ.

Теперь когда мы узнали основы по работе таких замечательных инструментах как grep, xargs и sed. Мы сможем писать более продвинутых сценарии для наших задач.

Откажитесь от неудачных приемов работы в UNIX

Часто используя какую-либо систему, мы привыкаем к некоторым шаблонам работы. Но не всегда эти шаблоны оказываются оптимальными. Иногда мы даже приобретаем плохие привычки, которые приводят к беспорядку и неуклюжести в работе. Один из наилучших путей исправления таких недостатков – выработка привычки использовать хорошие приемы, которые препятствуют беспорядку. Эта статья предлагает 10 методов работы с командной строкой UNIX, достойных стать привычками, которые помогут вам избежать много общих недостатков и повысить эффективность работы. Подробное описание каждого из методов приведено после списка.

Усвоим 10 хороших привычек

Десять хороших методов, к которым стоит привыкнуть:

Создавайте деревья каталогов одной командой

Листинг 1 иллюстрирует один из наиболее распространенных неудачных методов работы в UNIX: пошаговое определение дерева каталогов.

Листинг 1. Пример плохого метода работы #1: Пошаговое определение дерева каталогов
~ $ mkdir tmp ~ $ cd tmp ~/tmp $ mkdir a ~/tmp $ cd a ~/tmp/a $ mkdir b ~/tmp/a $ cd b ~/tmp/a/b/ $ mkdir c ~/tmp/a/b/ $ cd c ~/tmp/a/b/c $

Намного быстрее использовать опцию -p mkdir и создавать все родительские и дочерние каталоги одной командой. Но даже администраторы, которые знают об этой опции, до сих пор работают пошагово, создавая в командной строке каждый подкаталог в отдельности. Берегите свое время - привыкайте к хорошим методам:

Листинг 2. Пример хорошего метода работы #1: Определение дерева каталогов одной командой
~ $ mkdir -p tmp/a/b/c

Вы можете использовать эту опцию, чтобы создать не только простую иерархию, но и сложные деревья каталогов, которые очень удобно использовать в скриптах. Например:

Листинг 3. Еще один пример хорошего метода работы #1: Определение сложного дерева каталогов одной командой
~ $ mkdir -p project/{lib/ext,bin,src,doc/{html,info,pdf},demo/stat/a}

Раньше была только одна причина пошагово определять дерево каталогов - реализация mkdir не поддерживала эту опцию, но теперь это не актуально для большинства систем. IBM, AIX, mkdir, GNU mkdir и остальные системы, соответствующие единой спецификации UNIX, сейчас имеют эту опцию.

Для немногих систем, в которых отсутствует эта возможность, следует использовать скрипт mkdirhier (см. ), который служит оболочкой для mkdir и выполняет ту же функцию.

~ $ mkdirhier project/{lib/ext,bin,src,doc/{html,info,pdf},demo/stat/a}

Изменяйте пути; не переносите архив

Еще один неудачный метод работы - это перемещение архивного файла.tar в определенный каталог, если вам захотелось извлечь архив именно в этот каталог. Вам не стоит так делать. Вы можете распаковать любой архивный файл.tar в любой каталог - для этого предназначена опция -C . Используйте опцию -C , чтобы указать каталог для распаковки:

Листинг 4: Пример хорошего метода работы #2: Использование опции -C для распаковки архивного файла.tar
~ $ tar xvf -C tmp/a/b/c newarc.tar.gz

Использование опции -C предпочтительнее, чем перемещение архивного файла в нужный вам каталог, смена каталога и извлечение содержимого файла – особенно, если сам архивный файл находится где-то в другом месте.

Объединяйте ваши команды с операторами управления

Вы, вероятно, уже знаете, что в большинстве оболочек вы можете комбинировать команды в одной командной строке с помощью использования точки с запятой (;) между ними. Точка с запятой - это управляющий оператор оболочки, и хотя он предназначен, для того чтобы связывать множественные отдельные команды в единую командную строку, его применение не всегда возможно. Например, предположим, что вы используете точку с запятой для объединения двух команд, в которых выполнение второй команды всецело зависит от успешного окончания первой. Если первая команда не выполняется так, как вы ожидаете, вторая команда все же запускается - и терпит неудачу. Вместо этого, используйте более подходящий оператор управления (некоторые описаны в данной статье). Если ваша оболочка их поддерживает, к ним стоит привыкнуть.

Запуск команды только в том случае, если другая команда возвратила нулевое значение

Используйте оператор управления && для объединения двух команд таким образом, что вторая команда будет выполняться, только если первая возвратила нулевое значение. Другими словами, если первая команда выполняется успешно, вторая команда тоже запускается. Если первая команда не выполняется, то вторая команда не запускается совсем. Например:

Листинг 5. Пример хорошего метода работы #3: Объединяйте команды с операторами управления
~ $ cd tmp/a/b/c && tar xvf ~/archive.tar

В этом примере содержимое архива извлекается в каталог ~/tmp/a/b/c, если этот каталог существует. Если каталог не существует, команда tar не запустится и из архива ничего не будет извлечено.

Запуск команды только в том случае, если другая команда возвратила ненулевое значение

Точно так же оператор управления || разделяет две команды и запускает вторую команду, только если первая команда возвратила ненулевое значение. Другими словами, если первая команда была успешной , вторая команда не запустится. Если первая команда завершилась с ошибкой, то вторая запустится . Этот оператор часто применяется для тестирования с условием: существует ли заданный каталог, если нет, то он его создает:

Листинг 6. Еще один пример хорошего метода работы #3: Объединяйте команды с операторами управления
~ $ cd tmp/a/b/c || mkdir -p tmp/a/b/c

Также можно комбинировать операторы управления, описанные в этом разделе. Каждый из них относится к последней запускавшейся команде:

Листинг 7. Комбинированный пример хорошего метода работы #3: Объединение команд с операторами управления
~ $ cd tmp/a/b/c || mkdir -p tmp/a/b/c &&

Будьте внимательны при использовании кавычек в работе с переменными

Всегда будьте осторожны с расширением оболочки и именами переменных. Вообще-то, это хорошая идея - брать обращения к переменным в двойные кавычки, только если нету особых причин не делать этого. Аналогично, если непосредственно за именем переменной следует буквенно-цифровой текст, имя переменной следует заключить в фигурные скобки ({}), чтобы отличать ее от окружающего текста. Иначе оболочка интерпретирует замыкающий текст как часть имени переменной - и наиболее вероятный результат - нулевой код возврата. В Листинге 8 приведены примеры вариантов использования кавычек с именами переменных и получаемые результаты.

Листинг 8. Пример хорошего метода работы #4: Использование (и не использование) кавычек в работе с переменными
~ $ ls tmp/ a b ~ $ VAR="tmp/*" ~ $ echo $VAR tmp/a tmp/b ~ $ echo "$VAR" tmp/* ~ $ echo $VARa ~ $ echo "$VARa" ~ $ echo "${VAR}a" tmp/*a ~ $ echo ${VAR}a tmp/a ~ $

Используйте управляющую последовательность символов для ввода длинной строки

Вы, вероятно, встречали примеры кода, в котором обратный слэш (\) продолжает длинную строку, и вы знаете, что большинство оболочек трактует то, что вы печатаете в последующих строках, присоединенных с помощью обратного слэша, как одну длинную строку. Тем не менее, вы можете получить больше пользы от этой функции в командной строке, используя ее чаще. Обратный слэш особенно удобен, если ваш терминал не справляется должным образом с обработкой многострочных переносов или когда командная строка короче, чем обычно (к примеру, когда у вас в командной строке указан длинный путь). Обратный слэш также полезен для выделения смысловых частей длинной строки, как в следующем примере:

Листинг 9. Пример хорошего метода работы #5: Использование обратного слэша в длинных строках
~ $ cd tmp/a/b/c || \ > mkdir -p tmp/a/b/c && \ > tar xvf -C tmp/a/b/c ~/archive.tar

В качестве альтернативы приведена еще одна конфигурация, которая также работает:

Листинг 10. Альтернативный пример хорошего метода работы #5: Использование обратного слэша в длинных строках
~ $ cd tmp/a/b/c \ > || \ > mkdir -p tmp/a/b/c \ > && \ > tar xvf -C tmp/a/b/c ~/archive.tar

Когда вы разделяете строку на множество строк, оболочка всегда рассматривает ее как одну целую, удаляя из полученной длинной строки все обратные слэши и лишние пробелы.

Примечание: В большинстве оболочек, когда вы нажимаете стрелку вверх, вся мульти-строка изображается в виде единой длинной строки.

Объединяйте команды в список

Большинство оболочек позволяет объединять наборы команд в список; таким образом, вы можете организовать конвейерную передачу результатов работы от одной команды к другой или перенаправить их другим способом. Вы можете, как правило, сделать это с помощью запуска списка команд в дополнительной или в текущей оболочке.

Запуск списка команд в дополнительной оболочке

Используйте круглые скобки для заключения списка команд в одну группу. Это позволяет запустить команды в дополнительной оболочке и перенаправить или иным способом собрать результаты их работы, как показано в следующем примере:

Листинг 11. Пример хорошего метода работы #6: Запуск списка команд в дополнительной оболочке
~ $ (cd tmp/a/b/c/ || mkdir -p tmp/a/b/c && \ > VAR=$PWD; cd ~; tar xvf -C $VAR archive.tar) \ > | mailx admin -S "Archive contents"

В этом примере содержимое архива извлекается в каталог tmp/a/b/c, в то время как вывод сгруппированных команд, включая список извлеченных файлов, отправляется по почте на адрес admin .

Использование дополнительных оболочек удобно в тех случаях, когда вы переопределяете переменные среды в списке команд и не хотите принимать эти изменения в своей текущей оболочке.

Запуск списка команд в текущей оболочке

Используйте фигурные скобки ({}) для заключения списка команд для запуска в текущей оболочке. Убедитесь, что вы добавили пробелы между скобками и командами, т.к. в противном случае оболочка может неправильно интерпретировать скобки.. Также убедитесь, что после последней команды в списке стоит точка с запятой (;), как в следующем примере:

Листинг 12. Еще один пример хорошего метода работы #6: Запуск списка команд в текущей оболочке
~ $ { cp ${VAR}a . && chown -R guest.guest a && \ > tar cvf newarchive.tar a; } | mailx admin -S "New archive"

Применяйте xargs к результатам работы поиска find

Используйте команду xargs в качестве фильтра для работы со списком файлов, полученным в результате выполнения поиска командой find . Обычно команда find выдает список файлов, соответствующий некоторому критерию. Этот список передается к xargs , которая выполняет другие операции, используя список файлов в качестве аргументов, как в следующем примере:

Листинг 13. Пример классического применения команды xargs
~ $ find | \ > xargs

Тем не менее, не думайте, что xargs - это только дополнение к find ; это один из тех инструментов, привыкнув к которому, хочешь работать с ним постоянно, например, в следующих случаях:

Передача списка с разделяющими пробелами

В этой простой иллюстрации xargs похож на фильтр, который берет на вход список (каждый элемент на отдельной строке) и ставит его элементы на одну строку, разделяя их пробелами:

Листинг 14. Пример результата работы команды xargs
~ $ xargsabc a b c ~ $

Вы можете посылать вывод любого инструмента, который выводит имена файлов, через xargs , чтобы получить список аргументов для некоторых других инструментов, которые рассматривают имена файла как аргумент, как в следующем примере:

Листинг 15. Пример использования команды xargs
~/tmp $ ls -1 | xargs December_Report.pdf README a archive.tar mkdirhier.sh ~/tmp $ ls -1 | xargs file December_Report.pdf: PDF document, version 1.3 README: ASCII text a: directory archive.tar: POSIX tar archive mkdirhier.sh: Bourne shell script text executable ~/tmp $

Команда xargs полезна не только для передачи имен файлов. Используйте ее каждый раз, когда вам надо отфильтровать текст в одну строку:

Листинг 16. Пример хорошего метода работы #7: Использование инструмента xargs для фильтрации текста в одну строку
~/tmp $ ls -l | xargs -rw-r--r-- 7 joe joe 12043 Jan 27 20:36 December_Report.pdf -rw-r--r-- 1 \ root root 238 Dec 03 08:19 README drwxr-xr-x 38 joe joe 354082 Nov 02 \ 16:07 a -rw-r--r-- 3 joe joe 5096 Dec 14 14:26 archive.tar -rwxr-xr-x 1 \ joe joe 3239 Sep 30 12:40 mkdirhier.sh ~/tmp $

Будьте осторожны, используя xargs

Формально, бывают редкие случаи, когда использование xargs может вызвать затруднение. По умолчанию, символ конца файла - это нижнее подчеркивание (_); если этот символ был передан на вход, как единственный аргумент, все после него будет игнорироваться. Как меру предосторожности используйте флаг -e , который без аргументов отключает символ конца файла.

Знайте, когда использовать grep для подсчета - а когда от него лучше отказаться

Избегайте использовать после grep команду wc -l для подсчета количества полученных строк. Опция -c в grep позволяет подсчитать количество строк, которые соответствуют заданным шаблонам и в целом работает быстрее, чем комбинация " wc после grep ", как в следующем примере:

Листинг 17. Пример хорошего метода работы #8: Подсчет числа строк с и без grep
~ $ time grep and tmp/a/longfile.txt | wc -l 2811 real 0m0.097s user 0m0.006s sys 0m0.032s ~ $ time grep -c and tmp/a/longfile.txt 2811 real 0m0.013s user 0m0.006s sys 0m0.005s ~ $

Опция -c является хорошим способом подсчета не только из-за фактора скорости. При использовании нескольких файлов grep с опцией -c возвращает отдельное значение для каждого файла, по одному на каждой строке, тогда как направление вывода в wc дает общий результат для всех файлов.

Однако, этот пример интересен не только с точки зрения быстродействия – он иллюстрирует еще одну распространенную ошибку. Этот метод подсчета предоставляет только количество строк, в которых найдены соответствия образцу - и хорошо, если это то, что вы ищете. Но в случаях, где строки могут иметь несколько совпадений с конкретным образцом, эти методы не дадут реального числа соответствий образцу . Для подсчета числа совпадений используйте wc . Для начала запустите команду grep с опцией -o , если ваша версия поддерживает это. Эта опция выводит только соответствие образцу, по одному на каждую строку, но не саму строку. Ее нельзя использовать вместе с опцией -c , поэтому используйте wc -l для подсчета числа строк, как в следующем примере:

Пример хорошего метода работы #8: Подсчет числа совпадений с образцом с grep
~ $ grep -o and tmp/a/longfile.txt | wc -l 3402 ~ $

В этом случае обращение к wc работает немного быстрее, чем второй вызов grep с фиктивным образцом, позволяющим выполнить сравнение и подсчет каждой строки (как grep -c).

Сравнение определенных полей вывода, не только строк

Инструмент, подобный awk , предпочтительнее grep , когда вы хотите найти соответствие образцу только в конкретных полях строк вывода, а не во всей этой строке.

Следующий упрощенный пример показывает, как отсортировать только те файлы, которые были изменены в декабре:

Листинг 19. Пример плохого метода работы #9: Использование grep для поиска образца в определенных полях
~/tmp $ ls -l /tmp/a/b/c | grep Dec -rw-r--r-- 7 joe joe 12043 Jan 27 20:36 December_Report.pdf -rw-r--r-- 1 root root 238 Dec 03 08:19 README -rw-r--r-- 3 joe joe 5096 Dec 14 14:26 archive.tar ~/tmp $

В этом примере grep фильтрует строки и выводит все файлы, в имени или дате изменения которых содержится Dec . Поэтому такой файл, как December_Report.pdf, подходит, даже если он не был изменен с января. Это, возможно, не то, что вы хотите. Для поиска соответствия образцу в конкретном поле лучше использовать awk , в котором относительный оператор соответствует конкретному полю, как показано в следующем примере

Листинг 20. Пример хорошего метода работы #9: Использование awk для поиска образца в определенных полях
~/tmp $ ls -l | awk "$6 == "Dec"" -rw-r--r-- 3 joe joe 5096 Dec 14 14:26 archive.tar -rw-r--r-- 1 root root 238 Dec 03 08:19 README ~/tmp $

Более подробно об использовании awk можно прочитать в разделе .

Не используйте передачу вывода cat

Основная и распространенная ошибка в использовании grep заключается в передаче вывода cat в grep в grep для поиска по содержимому отдельного файла. Это абсолютно ненужная потеря времени, потому что такие инструменты, grep , принимают названия файлов в качестве аргументов. Поэтому использовать cat в этой ситуации совершенно не нужно, как в следующем примере:

Листинг 21. Пример хорошего метода работы #10: Использование grep с cat и без него.
~ $ time cat tmp/a/longfile.txt | grep and 2811 real 0m0.015s user 0m0.003s sys 0m0.013s ~ $ time grep and tmp/a/longfile.txt 2811 real 0m0.010s user 0m0.006s sys 0m0.004s ~ $

Эта ошибка допускается во многих случаях. Так как большинство инструментов получают стандартный ввод в виде аргумента, используя тире (-), даже аргумент для использования cat для разнообразия многочисленных файлов с stdin часто не действителен. На самом деле необходимо выполнить конкатенацию перед использованием cat с одной из его нескольких опций фильтрации.

Заключение: привыкайте к хорошему

Мы рассмотрели несколько методов работы в командной строке. Плохие привычки замедляют работу и часто приводят к непредвиденным ошибкам. Эта статья рассказывает о 10 новых способах, которые могут помочь вам избавиться от многих наиболее распространенных ошибок. Привыкайте к этим хорошим методам и улучшайте свои навыки работы с командной строкой Unix.


Автор: Joshua Reed
Дата публикации: 25 сентября 2014 года
Перевод: А. Кривошей
Дата перевода: апрель 2015 г.

Попадали ли вы когда-нибудь в ситуацию, когда вам нужно запускать одну и ту же команду много раз подряд? Если да, то вы понимаете, насколько это скучно и неэффективно. А хорошая новость в том, что в операционных системах на базе Unix есть замечательная команда xargs, которая позволяет решить эту проблему.С помощью этой команды вы можете очень эффективно работать с большим количеством файлом, экономя свое время и нервы. В этой статье вы увидите, как это делается.
У команды xargs есть два компонента. Во-первых, вы должны указать файлы, которые вас интересуют. Во-вторых, вы должны задать команду или скрипт, который вы хотите применить к каждому из этих файлов.
В этом руководстве мы рассмотрим три сценария использования команды xargs для обработки файлов, расположенных в нескольких разных директориях:

1. Подсчет количества строк во всех файлах.
2. Вывод первой строки заданных файлов.
3. Обработка каждого файла с помощью заданного скрипта.

Рассмотрим директорию с именем xargstest (дерево директорий можно вывести с помощью команды tree с опциями -i и -f, которые позволяют вывести результаты без отступов и с полным префиксом пути для каждого файла):

$ tree -if xargstest/

Директория xargstest, ее поддиректории и файлы будут далее использоваться в наших примерах.

Сценарий 1: подсчет количества строк во всех файлах

Как говорилось ранее, первым компонентом команды xargs является список файлов, с которыми работает команда. Для того, чтобы индентифицировать эти файлы и вывести их спиоск, можно воспользоваться командой find. Опция -name "file??" показывает, что нас интересуют только файлы, чье название начинается с "file", после чего в названии есть еще два любых любых символа. Поиск производится в директории xargstest, причем поиск осуществляется рекурсивно, то есть и во всех поддиректориях xargstest (если они имеются).

$ find xargstest/ -name "file??" xargstest/dir3/file3B xargstest/dir3/file3A xargstest/dir1/file1A xargstest/dir1/file1B xargstest/dir2/file2B xargstest/dir2/file2A

Мы можем перенаправить результаты поиска в команду sort, чтобы упорядочить их по имени:

$ find xargstest/ -name "file??" | sort xargstest/dir1/file1A xargstest/dir1/file1B xargstest/dir2/file2A xargstest/dir2/file2B xargstest/dir3/file3A xargstest/dir3/file3B

Теперь нам нужен второй компонент, представляющий собой команду, которую мы хотим выполнить. Для подсчета количества строк в каждом файле мы будем использовать команду wc с опцией -l (количество строк отображается в начале каждой строки вывода):

$ find xargstest/ -name "file??" | sort | xargs wc -l 1 xargstest/dir1/file1A 2 xargstest/dir1/file1B 3 xargstest/dir2/file2A 4 xargstest/dir2/file2B 5 xargstest/dir3/file3A 6 xargstest/dir3/file3B 21 total

Вы видите, что вместо того, чтобы вручную запускать команду wc -l для каждого из этих файлов, xargs позволяет выполнить эту операцию одной командой. Вы можете легко осуществлять различные операции с сотнями файлов.

Сценарий 2: вывод первой строки каждого из заданных файлов

Теперь, когда вы в общем понимаете, как использовать команду xargs, вы имеете полную свободу в выборе команды для выполнения. Иногда вам может понадобиться обработать только некоторую выборку файлов, но не все, расположенные в данной директории. В таком случае необходимо воспользоваться командой find с опцией -name для того, чтобы выбрать требуемые файлы и перенаправить их в команду xargs. Например, если вам нужно вывести первую строку всех файлов, имена которых заканчиваются на "B", используется следующая комбинация команд find, xargs и head (head -n1 выводит первую строку файла):

$ find xargstest/ -name "file?B" | sort | xargs head -n1 ==> xargstest/dir1/file1B <== one ==> xargstest/dir2/file2B <== one ==> xargstest/dir3/file3B <== one

Вы видите, что обрабатываются только файлы, чье имя заканчивается на "B", остальные игнорируются.

Сценарий 3: Обработка каждого файла с помощью скрипта

Наконец, у вас может быть свой скрипт (написанный на Bash, Python, или, например, на Perl) для обработки файлов. Просто подставьте его имя вместо команд wc и head в предыдущем примере.

$ find xargstest/ -name "file??" | xargs myscript.sh

Необходимо, чтобы скрипт myscript.sh был написан таким образом, чтобы он принимал имя файла в качестве аргумента и обрабатывал этот файл. Приведенная выше команда будет применять этот скрипт к каждому файлу, найденному командой find.

Обратите внимание, что в вышеприведенных примерах имена файлов не содержат пробелов. В целом можно сказать, что жизнь в Linux значительно приятнее в том случае, если вы не используете пробелы в именах файлов. Если же вам необходимо обработать файлы, чьи названия включают пробелы, приведенная выше команда не будет работать. Ее необходимо немного модифицировать, добавив опцию "-print0" к команде find (которая выводит полное имя с символом "null" после него в stdout) и опцию "-0" к команде xargs (которая интерпретирует символ "null" в конце строки), как показано ниже:

$ find xargstest/ -name "file*" -print0 | xargs -0 myscript.sh

Обратите внимание, что аргумент для опции -name изменился на "file*", что подразумевает любые файлы с именами, начинающимися с "file" и заканчивающимися любыми символами.

Заключение

После прочтения этого руководства вы будете знать возможности команды xargs и как вы можете использовать их в своей работе. Скоро вы будете тратить гораздо меньше времени на выполнение повторяющихся задач. Если вы хотите узнать обо всех опциях и возможностях команды xargs, просто наберите в терминале.

Посмотрело: 717

Об утилите xargs написано очень много - что можно написать еще? Но если, что называется, копнуть поглубже, то выясняется, что во многих публикациях излагаются лишь самые основы, но нет главного: не объясняется, как можно применять xargs в реальной практике. Статей с разбором сложных и нетривиальных вариантов применения этого весьма полезного для системного администратора инструмента, к сожалению, очень мало. Именно поэтому мы написали свою статью и постарались включить в нее как можно больше примеров использования xargs для решения различных проблем.

Сначала мы рассмотрим принцип работы xargs и разберем примеры попроще, а затем перейдем к разбору сложных и интересных кейсов.

Вспоминаем основы

Принцип работы xargs можно описать следующим образом: программа берет данные из стандартного ввода или из файла, разбивает их в соответствии с указанными параметрами, а затем передает другой программе в качестве аргумента.

В общем виде синтаксис команды xargs можно представить так:

[команда_генератор_списка] | xargs [опции_xargs] [команда]

Рассмотрим, как все это работает, на материале простых и хрестоматийных примеров.

Удаление файлов

Одна из самых частых ситуаций, в которых используется xargs - удаление файлов, найденных при помощи команды find.

Представим себе следующую ситуацию: имеется директория, в которой хранится большое количество файлов. Из нее нужно удалить файлы определенного типа (в нашем примере - файлы с расширением *.sh). Чтобы осуществить эту операцию, нужно передать xargs вывод команды find, и к файлам с указанным расширением будет применена команда -rm:

$ ls
one.sh one.py two.sh two.py

$ ls
one.py two.py

Отметим, что операцию удаления файлов можно осуществить и без xargs, а с помощью команды

$ find . -name "*.sh" -exec rm -rf "{}"

Описанный способ не сработает, если в имени одного из удаляемых файлов содержится пробел. Имя, состоящее из двух слов, разделенных пробелом, не будет воспринято как единое целое.

Проиллюстрируем это следующим примером:

$ ls
new file.sh one.sh one.py two.sh two.py

$ find . -name "*.sh"| xargs rm -rf

$ ls
new file.sh one.py two.py

Как видим, файл, в имени которого имеется пробел, не был удалён.

Чтобы решить эту проблему, используется опция print0 для команды find и опция -0 для команды xargs. Она заменяет стандартный разделитель (перенос строки на нуль-символ (x0), который и означает конец хранимой строки:

$ find . -name "*.sh" -print0 | xargs -0 rm -rf

Xargs может также помочь, например, быстро удалить все временные файлы, имеющие расширение tmp:

$ find /tmp -name "*.tmp"| xargs rm

Сжатие файлов

Сжать все файлы в текущей директории с помощью gzip можно, введя следующую команду:

$ ls | xargs -p -l gzip

Рассмотрим еще один пример: сжатие с помощью tar всех файлов с расширением *.pl:

$ find . -name "*.pl" | xargs tar -zcf pl.tar.gz

Переименование файлов

С помощью xargs можно осуществлять массовое переименование файлов. Представим себе, что у нас есть группа файлов с расширением *.txt, и нам нужно заменить это расширение на *.sql. Это можно сделать при помощи xargs и потокового текстового редактора sed:

$ ls | sed -e "p;s/.txt$/.sql/" | xargs -n2 fmv

В результате ее выполнения на консоль будет выведен список переименованных файлов.

С помощью xargs можно также добавлять к дополнительные элементы к именам файлов (например, дату):

$ ls | xargs -I FILE mv {} -{}

Вместо можно подставить всё, что угодно.
Фигурные скобки {} в этом примере означают «текущий аргумент» (т.е. текущее имя файла).

Изменение прав для папок и файлов

С помощью xargs можно также ускорить процесс смены прав на файлы и папки для определенного пользователя или группы. Предположим, нам нужно найти все папки пользователя root и заменить их владельца на temp. Эта операция осуществляется при помощи команды:

$ find . -group root -print | xargs chown temp

Чтобы найти все папки группы root и заменить группу на temp, используется команда:

$ find . -group root -print | xargs chgrp temp

Xargs и find: сложные операции

С помощью команд find и xargs можно выполнять и более сложные операции. Вот так, например, можно удалить временные файлы, созданные более 7 дней назад:

$ find /tmp -type f -name "*" -mtime +7 -print0 | xargs -0 rm -f

А вот так - принудительно остановить процессы, которые уже работают больше 7 дней:

$ find /proc -user myuser -maxdepth 1 -type d -mtime +7 -exec basename {} ; | xargs kill -9

Xargs и сut

Xargs довольно часто используется в сочетании с командой cut, позволяющей вырезать строки из текстовых файлов. Рассмотрим некоторые практические примеры. С помощью приведённой ниже команды на консоль будет выведен список всех пользователей системы:

$ cut -d: -f1

А команда вида

file * | grep ASCII | cut -d":" -f1 | xargs -p vim

будет последовательно открывать файлы для редактирования в vim.
Обратим внимание на опцию -p. Благодаря ей команда будет выполняться в интерактивном режиме: перед открытием каждого файла будет запрашиваться подтверждение (y/n).

В заключение приведём ещё один сложный и интересный пример - рекурсивный поиск файлов самого большого размера в некоторой директории:

$ find . -type f -printf " s %pn" | sort -n | cut -b22- | tr "n" "

On Сб, 19/09/2015 - 16:07

Задача

Есть консольная команда вида:

./do-something.sh -x 1

Значение аргумента x может меняться в диапазоне от 1 до 30 000. Выполнение команды для одного аргумента занимает от 30 секунд до 15 минут. Нужно максимально быстро выполнить эту команду для заданного диапазона аргументов на N-ядерном сервере максимально используя ресурсы сервера.

Возможные варианты решения

  1. Простой цикл от 1 до 30 тысяч с запуском команды на каждой итерации будет использовать только 1 ядро. Это решение неприемлемо: оно будет работать слишком долго и не задействует все доступные ресурсы сервера.
  2. Можно вручную разбить диапазон на N частей и запустить N циклов вида:

For i in `seq 1 1000 ` do ./do-something.sh -x $i done

Второе решение лучше первого - оно задействует все доступные ядра процессора, но оно все равно неприемлемо. Команды выполняются с непостоянной скоростью. В каком-то из диапазонов могут попасться только легкие команды, которые выполнятся, предположим, за несколько минут, а в каком-то - тяжелые и их выполнение затянется на несколько часов. Таким образом, часть ядер быстро освободится, будет простаивать и ресурсы сервера опять будут использованы неоптимально.

Решение с xargs

Утилита xargs , входящая во все современные дистрибутивы Linux, позволяет выполнить заданную команду для списка аргументов поступивших на стандартный ввод. Полезные ссылки:

В следующем примере берется список файлов текущей директории ls (в примере использован корень проекта на фреймворке Yii2, ничего секретного) и для каждого файла в директории применяется команда file , определяющая тип файла:

Ls | xargs file assets: directory commands: directory composer.json: ASCII text composer.lock: UTF-8 Unicode text config: directory controllers: directory mail : directory migrations: directory models: directory modules: directory requirements.php: PHP script, ASCII text runtime: directory tests: directory vendor: directory views: directory web: directory yii: a /usr/bin/env php script, ASCII text executable yii.bat: DOS batch file , ASCII text

Аргумент -P позволяет задать сколько параллельных потоков будет использовано для выполнения задачи.

Поэкспериментируем. Возьмем следующий скрипт и назовем его do-something.sh:

#!/usr/bin/env bash # Check for command line arguments if [ $# -lt 1 ] then echo "No options found!" exit 1 fi # Get number while getopts "x:" opt do case $opt in x) num=$OPTARG ;; *) echo "No reasonable options found!" ;; esac done rnd=$(shuf -i 1 -100 -n 1 ) rnd=$(echo "$rnd 100" | awk "{printf "%.2f \n ", $1/$2}" ) sleep $rnd echo $num

Этот скрипт берет на вход число и выводит его на экран с задержкой от 0 до 1 секунды. Теперь запустим этот скрипт командой time echo {1..10} | xargs -n 1 ./do-something.sh -x . Эта команда выполняет следующие задачи:

  • генерирует последовательность чисел от 1 до 10: echo { 1 ..10 } ,
  • передает эти числа по одному в наш скрипт (за это отвечает аргумент -n 1 , без него вся последовательность будет воспринята как один длинный аргумент, так как значения разделены пробелом, а не переводом строки),
  • в конце работы скрипта командой time выводит затраченное время.

В результате мы получим примерно такой вывод:

Time echo { 1 ..20 } | xargs -n 1 ./do-something.sh -x 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 real 0m10.301s user 0m0.042s sys 0m0.194s

Результат 1

А теперь запустим ту же команду с опцией -P 4 , что заставит скрипт выполняться в 4 потока:

Time echo { 1 ..20 } | xargs -n 1 -P 4 ./do-something.sh -x 4 2 6 1 3 8 10 5 12 9 7 11 13 15 14 19 16 17 20 18 real 0m2.651s user 0m0.032s sys 0m0.215s

Результат 2

В этом примере мы видим, что команды выполнились в 4 раза быстрее, так как запускались параллельно. То что на результат практически не влияет генератор случайных чисел легко убедиться заменив случайную паузу константой.

Теоретически, можно для числа потоков задать значение превышающее число доступных ядер на компьютере. В зависимости от выполняемой программы это может привести как к повышению, так и к понижению скорости выполнения, поэтому оптимальное значение числа потоков должно быть выбрано индивидуально для каждого конкретного приложения.

У xargs есть один недостаток. Давайте заменим в скрипте do-something.sh sleep $rnd на sleep 0.1 . Это сделает задержку не случайной, а постоянной. Теперь еще раз выполним time echo {1..20} | xargs -n 1 -P 4 ./do-something.sh -x:

Time echo { 1 ..20 } | xargs -n 1 -P 4 ./do-something.sh -x 1 3 2 4 5 6 7 8 9 10 12 11 13 14 15 16 17 18 19 20 real 0m0.560s user 0m0.034s sys 0m0.186s

Результат 3

Видно, что результаты выводятся не последовательно, это не всегда приемлемо.

Решение с GNU Parallel

Ниже перевод введения из мануала к утилите:

Утилита командной строки для параллельного запуска задач на одном или нескольких компьютерах. Задача в данном контексте - это одна команда или скрипт, который должен быть запущен для каждого входящего аргумента. Типичный набор аргументов - это список файлов, хостов, пользователей, урлов или таблиц. Аргументы также могут быть переданы через пайп. GNU parallel может разделить аргументы и параллельно передать их командам.

Если вы используете xargs , то вы легко сможете использовать parallel , так как эта утилита поддерживает те же аргументы командной строки что и xargs . Если вы используете циклы в шелл-скриптах, то, вероятно, parallel поможет вам избавиться от них и ускорить выполнение за счет параллельного запуска команд.

GNU parallel возаращает результаты выполнения команд в том же порядке как если бы они были запущены последовательно. Это делает возможным использование результатов работы parallel как входных данных для других программ.

Для каждой входящей строки GNU parallel запустит команду и передаст ей эту строку в качетсве аргументов. Если команда не задана, то входящая строка будет исполнена. Несколько строк будут выполнены одновременно. GNU parallel может быть использована как замена для xargs и cat | bash .

У этой утилиты как минмум 2 видимых преимущества перед xargs :

  • она позволяет запускать команды не в рамках одного сервера, а сразу на нескольких,
  • руководство обещает, что результаты будут выводиться последовательно.

Испытаем. Поверим обещаниям того, что parallel принимает те же аргументы, что и xargs и просто заменим имя одной утилиты на другую в команде, которую использовали ранее:

Time echo { 1 ..20 } | parallel -n 1 -P 4 ./do-something.sh -x 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 real 0m0.562s user 0m0.135s sys 0m0.096s

Результат 4

Работает! Команда выполнилась примено за те же 0,5 секунд, что и xargs и результат возвращен в правильной последовательности.

Теперь попробуем вернуть обратно случайную задержку, зменим в скрипте do-something.sh sleep 0.1 на sleep $rnd и запустим еще раз. Результат будет возвращен опять в правильной последовательности, несмотря на то, что из-за разной задержки команды запущенные позже могут быть выполнены раньше предыдущих команд (это хорошо видно во втором результате выше).

Единственным недостатком является то, что xargs возвращает результаты как только они готовы, а parallel - только тогда когда выполнение всех команд завершено. Но это цена, которую приходится платить за корректную последовательность результатов. Если запустить parallel с аргументом --bar , то во время работы будет выводиться прогресс бар, показывающий процент выполненных команд.

Теперь испытаем еще одну киллер-фичу parallel - возможность запустить команду на нескольких серверах сразу. Для этого воспользуемся примером из доки: https://www.gnu.org/software/parallel/man.html#EXAMPLE:-Parallel-grep .

# Добавим список серверов в конфиг. В моем случае сервера имеют имена dev и test (echo dev; echo test) > .parallel/my_cluster # Убедимся, что существует файл.ssh/config и забэкапим его touch .ssh/config cp .ssh/config .ssh/config.backup # Временно отключим StrictHostKeyChecking (echo "Host *" ; echo StrictHostKeyChecking no) >> .ssh/config parallel --slf my_cluster --nonall true # Откатываем назад изменения StrictHostKeyChecking в конфиге SSH mv .ssh/config.backup .ssh/config

Теперь сервера из файла.parallel/my_cluster добавлены в.ssh/known_hosts .

Наконец, нужно скопировать скрипт do-something.sh в домашнюю директорию текущего пользователя на удаленных серверах (в моем примере test и dev).

После выполненной подготовки мы можем запустить команду на серверах dev и test добавив к вызову parallel опцию --sshlogin dev,test .

Попробуем:

Time echo { 1 ..3200 } | parallel -n 1 -P 4 --sshlogin test,dev ./do-something.sh -x real 0m0.334s user 0m0.080s sys 0m0.032s

Результат 5

Виден выигрыш в скорости даже на такой элементарной операции, несмотря на оверхед связанный с установкой соединения по сети. В случае с действительно тяжелыми командами, выполнение которых может занимать десятки секунд или минут, выигрыш от такого распределенного выполнения может оказаться еще заметнее.

  • Ромка"s blog
  • Log in or register to post comments