Программы
Bash < потоки ввода > вывода && управляющие конструкции || коротко о главном

Bash < потоки ввода > вывода && управляющие конструкции || коротко о главном

Небольшая заметка о конструкциях Bash, в которых путается большинство новичков. А именно: >, <, &, &&, |, ||

В Bash, да и других командных оболочках (даже в cmd.exe) есть довольно странные символы для управления потоками ввода/вывода, управления потоком исполнения. Одни из наиболее часто используемых это: >, <, &, &&, |, ||. Выглядят они похоже, да и иногда имеют схожий смысл, но, само собой – всё это разные операции.

Изображение Выучи 10 хороших привычек для работы в UNIX от IBM

Потоки вывода / ввода и файлы

Для начала разберёмся с первыми двумя. Во-первых, >, < – это всё про файлы или про дескрипторы файлов (некоторые их представления). И это действительно базовые вещи, например, если мы хотим сохранить вывод команды в файл, то мы пишем:

help > help_info.txt

– где help – это команда, вывод которой мы хотим сохранить, а help_info.txt – это файл, куда сохранить. То есть обычный вывод мы можем сохранить в файл с помощью перенаправления потока стандартного вывода (>).

Если вы повторите это действие несколько раз, то всё равно увидите в результирующем файле всего одну "копию" вывода help. Когда мы перенаправляем поток вывода с помощью >, он пишет с начала файла, затирая его. Если же нужно дописывать, используют его "брата" – >> – он продолжит записать с конца файла.

Но если есть вывод, значит можно сделать аналогично на ввод. Например, команда wc умеет считать слова/строки/байты, переданные на стандартный поток ввода. Передадим и мы из нашего файла help_info.txt:

wc -l < help_info.txt

И узнаем, сколько же строк было в выводе help. Тут важно понять, что это не из-за того, что в одном случае "стрелка" была от программы, а в другом – в программу, хотя так и выглядит. Просто > всегда пытается записать в файл, а < – прочитать из файла. К примеру, мы можем попытаться сразу посчитать строки, которые выдаёт help:

help > wc

Но вместо этого мы создадим файл "wc" и запишем в него вывод команды help.

Потоки вывода / ввода и программы

А вот что реально поможет с переводом стандартного потока вывода на вход другой программы – так это "труба" – |, также известная как конструкция "пайп" (калька с английского "pipe"). По трубе можно слева направо лить данные, изменяя их как на конвейере:

help | wc -l

– сработает уже ожидаемо. Можно делать более сложные "каскады" из труб:

ifconfig | grep inet | grep -v inet6 | cut -d ' ' -f 2

Работает это следующим образом:

  1. Получаем вывод команды ifconfig с подробной информацией о сетевых интерфейсах.
  2. Отфильтровываем строки, оставляя те, что с подстрокой "inet" командой grep.
  3. Из результата убираем строки с подстрокой "inet6" (не интересуемся IPv6).
  4. Разделяем строки на столбцы по пробелу и берём 2-ой столбец с помощью cut.

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

Управляющие конструкции в Bash

Хорошо, поняли, что | перенаправляет данные из одной программы в другую... Но есть ещё и || – может он делает это как-то лучше? На самом деле, || вообще никак не связан с |, хоть и похож. Он нужен для управления последовательностью действий.

Как в языках программирования есть условные операторы (в Bash они тоже есть), так и в терминале иногда удобно задать последовательность действий, исходя и того, получилось ли предыдущее. Например, перейти в директорию "asd", а если не удалось (скорее всего её нет) – создать её:

cd asd || mkdir asd

|| проверит код завершения предыдущей команды, и если он не 0 (а именно 0 считается корректным и правильным), то выполнится. Сам код завершения последней программы можно посмотреть в переменной $? (в Windows – %errorlevel%):

$ cd asd
-bash: cd: asd: No such file or directory

$ echo $?
1

– код не 0, значит в примере выше, мы бы попытались создать директорию... И было бы логично в неё заодно так и перейти. То есть "перейти в директорию "asd", а если не удалось (скорее всего её нет) – создать её и, если создали, перейти в неё". Для этого есть противоположность ||&&. Он выполнит следующую команду, если предыдущая завершилась хорошо. Если || называют "логическим или" (исключающим), то && – "логическое и". А наш пример превращается в:

cd asd || mkdir asd && cd asd

Мы можем таким образом описывать условия, как и с if.

Запуск программ в фоне на Bash

Казалось бы, как мы пришли к этому подзаголовку? Ровно также, как и к "управляющим конструкциям": у && есть довольно похожий на него "близнец" – &. И опять у них нет ничего общего. Если первый позволяет управлять потоком исполнения команд, то второй запускает их в фоне!

Например, давайте запустим простую, но долгую программу в фоне, выполним другую программу, а потом вернём из фона первую программу.

$ sleep 100 &
[1] 4925
$ echo "Hello"
Hello
$ jobs
[1]+  Running                 sleep 100 &
$ fg 1
sleep 100
^C

С помощью команды jobs можно получить список запущенных в сессии терминала задач. Задачи пронумерованы – по этим номерам можно поднимать задачи из фонового режима командой fg.

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

Завершение, о чём не рассказал

Как можно было заметить, в Bash довольно много странных символов, имеющих важные значения. Более того – не обязательно похожие комбинации символов означают похожее. Понимание приходит с опытом / привычкой.

У того же амперсанда есть и другие смыслы, в зависимости от контекста: и "битовое и" и "запись в открытый дескриптор". Да и <<, и даже <<< здесь не были упомянуты. В заметке скорее обзор самых первых удивлений, которые возникают у студента при знакомстве с терминалом / Bash-ем.

Если также часто придётся рассказывать про иные особенности - появится и их описание, как это было с "Как запустить программу в терминале в фоне, без вывода какого либо текста".

Ну и в целом, если заинтересовались – "Трюки Bash" для вас!