В командной строке или в сценариях оболочки есть три основных способа взаимодействия команд друг с другом. Первый и второй способ – через файловый ввод-вывод через каналы и среду. Третий способ – через параметр команды. Однако, чтобы команда могла взаимодействовать с другой через параметры, она или ее результат должны быть включены в список параметров. Вот где в игру вступает расширение команд или подстановка команд. Здесь мы рассмотрим все, что вам нужно знать о подстановке команд, чтобы писать сценарии bash, как босс!
Подстановка команд
Подстановка команд – это основная функция оболочки, которая позволяет выводить одну или несколько команд на месте и использовать их, как расширение переменных, в качестве аргументов для расширения другой команды. Другими словами, результат выполнения команд помещается в недолговечную анонимную переменную и подставляется в окружающую команду.
Синтаксис
Есть два приемлемых синтаксиса или способов выполнения подстановки команд в bash:
- синтаксис знака доллара; и
- синтаксис обратной кавычки.
На данный момент оба пути представлены без нашего мнения.
В дикой природе, когда разработчиков заставляют писать сценарии bash, по нашему опыту, тот или иной синтаксис используется в зависимости от личных предпочтений.
Синтаксис знака доллара
$( command )
На наш взгляд, этот синтаксис легче читать, особенно при вложенных подстановках команд, не говоря уже о том, что он менее подвержен ошибкам.
Пример 1: подстановка команд с использованием синтаксиса знака доллара для проверки строк в файле
Большинство сред Linux с такими командами Coreutils, как cat и shuf, также оснащены командой wc, которая позволяет вам подсчитывать байты, слова и строки в файле. Здесь мы будем использовать его, чтобы просто проверить, содержит ли файл больше определенного количества строк, а затем что-нибудь предпринять.
test ! $( seq 101 | wc -l ) -gt 100 || { echo do something }
Примечание
Выражение $( seq 101 | wc -l ) оценивается как целое число 101. В результате выражение test принимает вид test! 101 -gt 100. Кроме того, мы можем вывезти! оператор конвейера и вычисление оставшегося тестового выражения. То есть. Надеюсь, вы согласитесь, что test 101 -gt 100 действительно верен. Затем мы остались с! true в левой части оператора списка ||. ! истина становится ложью; и ложь || становится истинным &&.
Синтаксис обратной кавычки
`command`
Если вам нравятся обратные кавычки больше, чем деньги, отлично! Как и в природе кодирования, вы можете писать код любым удобным для вас способом, если только вы не должны соблюдать некоторые строгие правила стиля. Мы просто скажем, что у вас могут возникнуть трудности с подстановкой вложенных команд.
Пример 2: подстановка команды с использованием синтаксиса обратной кавычки для встраивания вывода вложенной команды в команду echo
Давайте упростим и выведем сообщение с вашим именем пользователя.
echo my username is `whoami`
Примечание
Если ваше имя пользователя «andreyex», приведенная выше команда оценивается как «my username is andreyex».
Теперь, когда вы знаете, как использовать подстановку команд, давайте рассмотрим способы ее использования.
Развлечение с заданиями и подстановкой команд
Часто мы хотим присвоить переменной результат выполнения команды. Это можно сделать с помощью подстановки команд.
variable=$( command args... )
Например, при сопоставлении с шаблоном bash мы присвоили переменной подчиненные буквы алфавита следующим образом.
Команды
subject=$( echo {z..a} | tr -d ' ' ) echo ${subject}
Вывод
zyxwvutsrqponmlkjihgfedcba
Удобный! Разве вы не рады, что сейчас произошла подмена команды!
Развлечения с функциями и подстановкой команд
Давайте свернем нашу собственную функцию карты, которая подсчитывает количество слов, содержащих букву a.
Во-первых, нам нужна функция, которая проверяет, содержит ли какое-то слово букву a. В следующем фрагменте мы будем использовать замену шаблона с помощью расширения параметра и целочисленного атрибута в назначении.
Команды
has_a() { local instr="${1}" local -i match=$( test ! "${instr//a}" != "${instr}" || echo 1 ) echo ${match} }
Если результат замены a из входной строки не является самим собой до замены, мы говорим, что входная строка содержит букву a. В этом случае мы повторяем 1. Результирующая подстановка команды затем подлежит присвоению с целочисленным атрибутом. В случае присвоения пустого значения присвоенное значение принимается равным 0. То есть функция has_a возвращает 0 или 1 в зависимости от наличия буквы a во входной строке.
Вот краткий обзор нашей функции has_a в действии.
Команды
has_a asdf has_a sdf has_a df has_a f has_a a
Вывод
1 0 0 0 1
Затем нам нужна функция для циклического перебора слов в предложении с применением функции has_a, которую мы просто назовем map.
Команды
map() { test ! ${#} -eq 1 || { true ; return ; } local function_name="${1}" local first=${2} local rest=${@:3} echo "$( ${function_name} ${first} ) $( map ${function_name} ${rest} )" }
Вот краткий обзор нашей функции карты в действии.
Команды
map has_a a b c map has_a {a..z}{a..z} map has_a {a..b}{a..b}{a..b}
Вывод
1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0
Теперь вы в матрице!
Все, что нам нужно сделать сейчас, это подсчитать единицы, которые мы назовем суммой.
sum() { test ! ${#} -eq 1 || { echo 0 ; return ; } local -i first="${1}" local rest=$( sum ${@:2} ) first+=rest echo ${first} }
Это должно сработать!
Вот краткий обзор нашей функции суммы в действии.
Команды
sum $( map has_a {a..b}{a..b}{a..b} ) sum $( map has_a {a..z}{a..z} ) sum $( map has_a {a..c}{a..c} )
Вывод
7 51 5
Больше удовольствия с заданиями: функция настройки
Пока вы здесь, давайте еще немного повеселимся с присваиваниями, исследуя то, что я люблю называть функциями настройки, т.е. мы собираемся создать специализированную функцию для присвоения значения переменной. Как вы уже знаете, нам может потребоваться подстановка команд. Вот как:
Команды
variable() { echo 1 } setup-variable() { variable=$( variable ) } setup() { setup-variable } main() { local variable=0 setup echo ${variable} } main echo ${variable:-empty}
Вывод
1 empty
Упражнения
- Перепишите команду из примера 1 без использования оператора конвейера!
- Перепишите команду из примера 2, используя синтаксис знака доллара.
- Напишите функцию для подсчета слов без использования sum, map и has_a
- Напишите “Он / она любит меня”, а не программировать этот цикл навсегда
- Напишите строку, присваивающую переменной значение второй строки и третьего столбца файла CSV
- Напишите строку, назначающую переменной согласие скрипта (Подсказка: используйте xxd)
TL; DR;
Круто! Теперь вы можете использовать расширение команды bash! Как и следовало ожидать, возможность расширять код в команды по своему усмотрению дает вам преимущество при попытке решить реальные проблемы с помощью программирования на bash в дополнение к созданию повторно используемого кода. Кодируйте ответственно.