Linux объединить вывод двух команд

Обновлено: 05.07.2024

Немного обо всем и все о немногом, или практический опыт системного администратора.

Декабрь 2009
Пн Вт Ср Чт Пт Сб Вс
« Ноя Янв »
123456
78910111213
14151617181920
21222324252627
28293031

Лекция №23 - bash. Объединение команд

Calendar

24 декабря 2009, 18:15

На этой лекции мы продолжаем изучать bash. Сегодня поговорим об объединении команд в bash, о конструкции case и некоторых других командах. Но сначала о незаслуженно забытой на прошлой лекции конструкции case.

Общий синтаксис команды case следующий:

Для нас case важна еще и потому, что содержится во всех скриптах каталога /etc/init.d/. В качестве шаблона могут указываться буквы, цифры, строки и шаблоны вида [a-z], 9, а также ? - один любой символ и * - любая комбинация. Хотя шаблон в общем синтаксисе указан в круглых скобках, на практике разрешается первую скобку не писать. Давайте рассмотрим практический пример:

В качестве значения здесь использована конструкция $1, которая содержит первый передаваемый скрипту параметр. На прошлой лекции мы говорили, что параметры можно передавать не только функции, но и скрипту. Значение начинает сверятся с шаблонами сверху вниз и как только будет найдено соответствие, будет выполнен блок команд между шаблоном и ;;. Пример работы скрипта:

/ linux$ . / case.sh a
a b c d
igor @ adm-ubuntu:

/ linux$ . / case.sh k
Это буква k
igor @ adm-ubuntu:

/ linux$ . / case.sh qq
Это два любых символа
igor @ adm-ubuntu:

/ linux$ . / case.sh 1
Это цифра 1
igor @ adm-ubuntu:

/ linux$ . / case.sh start
Это слово stop или restart или start

В каталоге /etc/init.d/ расположены скрипты управления службами Linux. С помощью case в них реализован механизм обработки передаваемых скрипту параметров: start, stop, restart, reload и других. Ниже фрагмент скрипта /etc/init.d/reboot:

Подобный case вы найдете в каждом скрипте каталога /etc/init.d/.

Объединение команд

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

/ linux$ a = 127 ; echo $a ; b = 172 ; echo $b ; let c = $a + $b ; echo $c
127
172
299

Как видите из примера на одной строке размещено 6 команд, которые были выполнены последовательно.

Рассмотрим два примера объединения команд и на результат их выполнения:

$ false || echo "1" || false || echo "2"
1
igor @ ubuntu:

Рассмотрим более подробно эту цепочку:

Ни одна из команд не завершается, поэтому я не уверен, как это сделать.

Если программы не заканчивают, предположительно, они записывают вывод непрерывно? Что вы хотите сделать с их выходом? Чередование строк, . почему ты хочешь сделать это? Команда узла не выводит много, но все равно должна быть запущена. Python один выводит все запросы, я хочу захватить оба и посмотреть их в одном окне оболочки.

Вы можете объединить две команды, сгруппировав их с < >:

если вы хотите разделить STDOUT и STDERR на два файла:

Не важно, что они программы не заканчивают. 'tail -f' также не «завершает», но это все еще работает и объединяет результаты обеих программ. Работает для более чем двух команд. ^ c, чтобы выйти, убивает только одну из сгруппированных команд. Вы должны будете убить другого вручную. Похоже , у вас не хватает последнего ; перед тем > , это обязательно! Имейте в виду: это не сохраняет целые строки! Вы получите ненадежные результаты, так как линии разделены частично и перепутаны между собой. Вы можете попробовать это, в < yes <1..20>& yes <1..20>; > | grep -v '^1 2 3' идеале, ничего не печатать, если строки не разбиты. Я бы предпочел использовать && вместо & ! command1 & command2 - это запускает команду 1 в фоновом режиме и немедленно запускает команду 2, таким образом выполняя обе команды параллельно и портя вывод. command1 && command2 - при этом запускается команда 1 (на переднем плане), а затем, если команда 1 выполнена, запускается команда 2. @DUzun OP сказал, что ни одна из команд не завершается, поэтому с вашим решением вторая команда никогда не запустится

( command1 ; command2 ; command3 ) | cat

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

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

Возможность объединения нескольких команд Linux в терминале и использования их в качестве конвейера, когда каждая следующая команда получает вывод предыдущей - очень мощный и гибкий инструмент. Но команды можно объединять не только так. С помощью утилиты xargs вывод предыдущей команды можно передать в аргументы следующей.

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

Команда xargs в Linux

Синтаксис команды немного запутанный, но в нём можно разобраться:

$ первая_команда | xargs опции вторая_команда аргументы

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

Для настройки поведения утилиты xargs можно использовать опции. Давайте их рассмотрим:

  • -0, --null - использовать в качестве разделителя нулевой символ. Обычно он находится в конце строки. По умолчанию, в качестве разделителя используется пробел, перевод строки или табуляция;
  • -a, --arg-file - прочитать аргументы, которые надо передать команде из файла;
  • -d, --delimiter - использовать нестандартный разделитель строк;
  • -E, -e, --eof - индикатор конца файла, все символы после вхождения этой строки игнорируются;
  • -l, --max-lines - количество строк, передающихся в одну команду по умолчанию все;
  • -n, --max-args - количество параметров, которые передаются в одну команду, по умолчанию все;
  • -o, --open-tty - открывать новый терминал для дочерних процессов;
  • -p, --interactive - спрашивать пользователя о запуске каждой следующей команды;
  • -r, --no-run-if-empty - если входной поток данных пустой, команда не будет выполнена;
  • --show-limits - посмотреть ограничения на длину параметров в командной строке;
  • -t, --verbose - максимально подробный вывод утилиты.

Это далеко не все опции утилиты. Если вам нужны опции, которых нет в данной статье выполните команду:

А теперь давайте рассмотрим примеры использования xargs в Linux. Программа ожидает данные из входного потока. Поэтому если мы запустим её без параметров и наберем несколько слов, она просто их выведет. Например:


Чтобы подробнее понять что происходит можно использовать опцию -t:


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

/Изображения и отдадим их утилите xargs:

/Изображения | xargs -t echo


Как видите, одной команде echo были переданы все имена изображений. Если вам надо чтобы каждое изображение обрабатывала отдельно вызванная команда, надо использовать опцию -L или -n, Первая позволяет указать сколько строк надо передавать в одну команду. Вторая - сколько параметров. Различие существенное. В строке может быть несколько параметров разделённых пробелом. Сначала попробуем ограничиться одной строкой:

/Изображения | xargs -t -L1 echo


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

/Изображения | xargs -t -n1 echo


По умолчанию строки разбиваются по символу перевода строки. С помощью опции -d можно указать свой разделитель. Разделитель должен быть совместим с функцией printf из Си. Это означает, что допускаются все символы, а также Escape последовательности вроде \n, \r, \t и других. Например, давайте сделаем разделителем дефис:

/Изображения | xargs -t -L1 -d - echo

Как и ожидалось, теперь команда будет дробить строки по дефису и выводить каждую полученную строку с помощью echo. В качестве разделителя строк можно использовать нулевой символ. Это символ, который обозначается последовательностью \0. Он используется в программах на Си для обозначения завершения строки, и необходим практически для всех встроенных функций работающих со строками.

Для того чтобы разбивать строки по этому символу используется опция -0 или --null. Но для того чтобы всё сработало надо чтобы и поставляющая данные команда тоже выводила этот символ. Такая функция есть у команды find, она имеет опцию -print0. Например, выведем снова все имена изображений из папки

/Изображения с названием Рабочее место:

/Изображения -name "Рабочее место*" -type f -print0 | xargs -t -0 -L1 echo


А если такой параметр не передать, то вы получите ошибку:

/Изображения -name "Рабочее место*" -type f -print0 | xargs -t -L1 echo

Если команда выполняет какие-нибудь опасные действия, вы можете попросить утилиту спрашивать разрешения перед выполнением следующей команды:

/Изображения -name "Рабочее место*" -type f | xargs -p -t -L1 echo


Выводы

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

Нет похожих записей


Статья распространяется под лицензией Creative Commons ShareAlike 4.0 при копировании материала ссылка на источник обязательна.

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

Поэтому в Linux имеются специальные символы, которые позволяют проводить данные операции над несколькими файлами.

Символ “ * ” указывает на любой символ в любом количестве. Поясним на примере.

У нас имеются следующие файлы:

Список файлов

Необходимо скопировать файлы image в каталог photo/. Для этого достаточно выполнить:

cp image* photo/

Символ " * " в данном случае означает любые символы после слова image , то есть image1.jpg , image2.jpg и так далее, включая image1.txt , image2.txt и image.pdf .

Чтобы лучше понять данный принцип рассмотрим возможные применения данного символа в таблице:

cp image* photo/

Все файлы image1.jpg - image15.jpg, image1.txt, image2.txt image.pdf

cp image*.jpg photo/

Только файлы image1.jpg - image15.jpg

cp *.jpg photo/

Все файлы jpg : image1.jpg - image15.jpg

Абсолютно все файлы в текущем каталоге

Символ “ ? ” указывает на любой одиночный символ. Например, команда cp image?.jpg photo/ скопирует файлы с image1.jpg по image9.jpg , то есть между image и .jpg рассматривается только один знак.

Рассмотрим возможные применения данного символа:

cp image?.jpg photo/

Только с image1.jpg по image9.jpg

cp image1?.jpg photo/

Только с image10.jpg по image15.jpg

cp image? photo/

В нашем случае выдаст ошибку, так такого файла или каталога не существует

cp image?.* photo/

image1.jpg - image9.jpg, image1.txt и image2.txt


Следующий символ групповых операций - это квадратные скобки []. Внутри скобок помещается определенной выражение.

Например, [12] означает, что в условии должен совпасть один из символов, указанных в скобках, то есть либо 1 либо 2. Также можно задать целый диапазон значений - 1, то есть любой символ из указанного диапазона.

Поясним сказанное на примерах:

cp image[12].jpg photo/

Только файлы image1.jpg и image2.jpg

cp image14.jpg photo/

Только файлы image11.jpg - image15.jpg

cp image[12]* photo/

image1.jpg, image2.jpg, image1.txt, image2.txt

cp image?[13].jpg photo/

Только файлы image11.jpg - image13.jpg

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

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

Последовательное безусловное выполнение

Иногда при выполнении задач в Linux может потребоваться выполнить сразу целую последовательность команд.

Например, нам нужно создать каталог, скопировать группу файлов student*.jpg в данный каталог и проверить размер данного каталога в килобайтах.

Для этого мы выполним следующее:

mkdir photo

cp student*.jpg photo/

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

mkdir photo; cp student*.jpg photo/; ls -lh

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

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

Последовательное выполнение при соблюдении условия

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

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

cp student*.jpg /media/StudentFlash; rm student*.jpg

И все будет работать, однако здесь одно НО. Если мы забудем подключить флэшку и выполним вышеуказанную последовательность, то потеряем все файлы student*.jpg , так как несмотря на ошибку система все равно выполнит вторую команду.

Чтобы избежать подобных ситуаций можно воспользоваться конструкцией:

cp student*.jpg /media/StudentFlash && rm student*.jpg

То есть вместо символа " ; " (точка с запятой) мы используем " && " . Проще говоря, если первая команда завершилась с ошибкой (неважно какая), то система не выполнит вторую команду. Если первая команда завершилась успешно, то будет выполнена вторая команда.

Следующий символ объединения " || " используется, когда из 2-х (или более) команд нужно выполнить либо первую либо вторую команду. То есть, если первая команда завершилась с ошибкой, то ход переходит следующей команде, а если первая команда все же успешно завершилась, то вторая команда выполняться не будет.

Например, перейдем в каталог music/ , если его не существует то создадим его:

cd music/ || mkdir music

В нашем случае каталог music/ не существует и система выдала ошибку, поэтому будет выполнена команда mkdir music .

Передача выхода одной команды на вход другой команды

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

Большой список файлов в выводе команды ls


Теперь попробуем в системе найти файл под названием icon с помощью команды locate (рассмотрим подробнее позже):

locate * icon

Система может выдать сотни и тысячи значений. Здесь тоже можем воспользоваться символом " | ":

locate * icon | less

Разумеется все это частные случаи. В процессе изучения новых команд и работе в Linux тебе придется не раз встречаться с ситуациями, когда будет необходимо воспользоваться символом " | " .

А можно результат работы locate записать в файл и уже потом просмотреть его содержимое с помощью less ?

Конечно можно. Для этого существует другой символ объединения команд " > " :

locate * icon > search_result.txt

В данном случаем символом " > " мы записываем результат выполнения locate в файл search_result.txt .

А файл search_result.txt нужно предварительно создавать?

Нет, если его нет, то система создаст его автоматически. А если он есть, то система перезапишет все его содержимое.

То есть мы можем все потерять по неосторожности?

Совершенно верно, но есть 2 способа избежать этого.

1-й способ. Можно установить специальную опцию noclobber :

set -o noclobber

Чтобы отключить эту опцию выполни

set +o noclobber

2-й способ. Вместо символа " > " воспользуйся символом " >> " . Когда указан этот символ система запишет данные в конец файла, тем самым сохранив предыдущие данные.

Подстановка вывода одной команды под аргумент второй команды

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

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

К примеру, у нас имеется текстовый файл links.txt , где хранится следующая запись

Содержимое файла links.txt

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

mkdir $(cat links.txt)

Вывод команды cat был подставлен под аргумент команды mkdir .

Читайте также: