Поиск по сайту:

Использование xargs в сочетании с bash -c для создания сложных команд


xargs — это фейерверк для ваших команд оболочки. Любой вывод, сгенерированный любой командой оболочки, может быть отправлен в xargs для дальнейшей обработки в другой командной строке. Научитесь использовать эту великую силу xargs уже сегодня!

xargs Введение

Мой коллега Дейв Маккей написал интересную статью «Как использовать команду xargs в Linux», которую вы можете прочитать в первую очередь для подробного ознакомления и изучения xargs в целом.

Эта статья будет посвящена конкретной проблеме: что делать, когда вы сталкиваетесь с ограничениями традиционного стека на основе конвейера или обычного (; с разделителями) стекирования команд Linux, и когда даже использование xargs не сразу кажется, дать ответ?

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

Трубы и xargs

Любой, кто изучает Bash, со временем улучшит свои навыки написания сценариев в командной строке. Самое замечательное в Bash то, что любые навыки, полученные в командной строке, легко переводятся в сценарии Bash (которые обычно помечаются суффиксом .sh). Синтаксис почти идентичен.

И по мере улучшения навыков новые инженеры обычно первыми открывают для себя идиому Pipe. Использовать трубу легко и просто: выходные данные предыдущей команды «направляются» на входные данные для следующей команды. Думайте об этом как о водопроводной трубе, доставляющей воду из выходного источника к входному источнику в другом месте, например, вода из плотины. подается на водяную турбину.

Давайте рассмотрим простой пример:

echo 'a' | sed 's/a/b/'

Здесь мы просто повторили «a» и впоследствии изменили то же самое с помощью редактора текстового потока sed. Вывод, естественно, «b»: команда sed заменила (s command) «a» на «b».

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

Здесь мы начинаем спать в фоновом режиме. Затем мы используем pidof для получения PID выполняемой команды sleep и пытаемся уничтожить ее с помощью kill -9 (рассмотрите -9 как деструктивный режим для уничтожения процесс). Это терпит неудачу. Затем мы пытаемся использовать PID, предоставленный оболочкой при запуске фонового процесса, но это также не удается.

Проблема в том, что kill не принимает входные данные напрямую, будь то из ps или даже из простого echo. Чтобы решить эту проблему, мы можем использовать xargs для получения вывода из команды ps или echo) и предоставить их в качестве входных данных для kill. , сделав их аргументами для команды kill. Таким образом, как если бы мы выполнили kill -9 some_pid напрямую. Давайте посмотрим, как это работает:

sleep 300 &
pidof sleep | xargs kill -9

Это работает отлично и достигает того, что мы намеревались сделать: убить процесс сна. Одно небольшое изменение в коде (то есть просто добавьте xargs перед командой), но одно большое изменение в том, насколько полезным может быть Bash для инженера-разработчика!

Мы также можем использовать параметр -I (определяющий строку замены аргумента) для kill, чтобы было немного понятнее, как мы передаем аргументы для уничтожения: i12b Здесь мы определяем {} в качестве строки замены. Другими словами, всякий раз, когда xargs увидит {}, он заменит {} любым вводом, полученным от последней команды.

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

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

Что, если бы мы могли — раз и навсегда — оставить эти ограничения позади и быть на 100% свободными для создания любой командной строки Bash, которая нам нравится, только с использованием каналов, xargs и оболочки Bash, без временных промежуточных файлы и без запуска новой команды? Возможно.

И это совсем не сложно, если кто-то покажет вам, но в первый раз это заняло некоторое время и обсуждение, чтобы разобраться. Я особенно хочу воздать должное и поблагодарить моего предыдущего наставника по Linux и бывшего коллегу Эндрю Далглиша — вместе мы выяснили, как лучше всего это сделать чуть менее 10 лет назад.

Добро пожаловать в xargs с помощью bash -c

Как мы видели, даже когда каналы используются в сочетании с xargs, все равно будут сталкиваться с ограничениями для сценариев более высокого уровня инженера. Давайте возьмем наш предыдущий пример и введем некоторую отладочную информацию без постобработки результата. Обычно этого было бы трудно добиться, но это не так с xargs в сочетании с bash -c:

sleep 300 &
pidof sleep | xargs -I{} echo "echo 'The PID of your sleep process was: {}'; kill -9 {}; echo 'PID {} has now been terminated'" | xargs -I{} bash -c "{}"

Здесь мы использовали две команды xargs. Первая создает пользовательскую командную строку, используя в качестве входных данных вывод предыдущей команды в канале (являющейся pidof sleep), а вторая команда xargs выполняет сгенерированную, пользовательскую для каждого ввода (важно! ), команда.

Почему настраиваемый вход? Причина в том, что xargs по умолчанию будет обрабатывать ввод (вывод предыдущей команды в конвейере) построчно и выполнять все, что ему было приказано выполнить для каждой такой строки ввода.

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

some_command | xargs -I{} echo "echo '...{}...'; more_commands; more_commands_with_or_without{}" | xargs -I{} bash -c "{}"

Обратите внимание, что вложенное echo (второе эхо) действительно необходимо только в том случае, если вы хотите повторно вывести фактический текст. В противном случае, если второго echo не было, первое echo начало бы выводить 'The PID …' и т. д., а bash -c подоболочка не сможет проанализировать это как команду (IOW, «PID…» не является командой и не может быть выполнена как таковая, следовательно, вторичное/вложенное эхо).

Как только вы запомните bash -c, -I{} и способ эха из другого эха (и в качестве альтернативы можно использовать escape-последовательности, если это необходимо), вы обнаружите, что используете этот синтаксис снова и снова.

Допустим, вам нужно выполнить три действия для каждого файла в каталоге: 1) вывести содержимое файла, 2) переместить его в подкаталог, 3) удалить его. Обычно для этого требуется несколько шагов с различными поэтапными командами, а если это становится более сложным, вам могут даже понадобиться временные файлы. Но это очень легко сделать с помощью bash -c и xargs:

echo '1' > a
echo '2' > b
echo '3' > c
mkdir subdir
ls --color=never | grep -v subdir | xargs -I{} echo "cat {}; mv {} subdir; rm subdir/{}" | xargs -I{} bash -c "{}"

Во-первых, быстро отметим, что всегда полезно использовать --color=never для ls, чтобы предотвратить проблемы в системе, которая использует цветовое кодирование для вывода списка каталогов (по умолчанию включено). в Ubuntu), так как это часто вызывает серьезные проблемы с анализом из-за того, что цветовые коды фактически отправляются на терминал и добавляются к записям списка каталогов.

Сначала мы создаем три файла, a, b и c, и подкаталог с именем subdir. Мы исключаем этот подкаталог из списка каталогов с помощью grep -v после того, как замечаем, что при быстром пробном запуске (без выполнения команд или, другими словами, без второго xargs) подкаталог все еще отображается.

Всегда важно сначала протестировать ваши сложные команды, наблюдая за выводом, прежде чем фактически передавать их подоболочке bash с помощью bash -c для выполнения.

Наконец, мы используем тот же метод, что и ранее, для сборки или команды; cat (показать) файл, переместить файл (обозначенный {}) в подкаталог и, наконец, удалить файл внутри подкаталога. Мы видим, что содержимое трех файлов (1, 2, 3) отображается на экране, и если мы проверим текущий каталог, наши файлы исчезнут. Мы также можем видеть, что в подкаталоге больше нет файлов. Все работало хорошо.

Подведение итогов

Использование xargs — отличная возможность для продвинутого пользователя Linux (и в данном случае) Bash. Использование xargs в сочетании с bash -c еще больше увеличивает мощность; уникальная возможность создавать 100% бесплатные настраиваемые и сложные командные строки, без необходимости в промежуточных файлах или конструкциях, а также без необходимости иметь сложенные/последовательные команды.

Наслаждайтесь!