| Текстовые замены и подстановки в Bash |
|
Автор статьи: Чувак с гранатой
Замены и подстановки - это очень полезный механизм, значительно упрощающий работу в оболочке Bash. Всего существует восемь их видов, и в этой статье мы по порядку познакомимся с каждым из них. Внимание! Предполагается, что вы уже знакомы с основами Bash.
Замена тильдыСкорее всего, вы уже встречались с заменой тильды (символ "~"), которую часто используют для быстрого перехода в домашний каталог: cd ~ Или для указания пути к файлу в домашнем каталоге: rm ~/test_dir/text_file В этих примерах интерпретатор Bash заменяет символ тильды на ваш домашний каталог. С помощью этого типа замены можно также подставлять домашний каталог другого пользователя, для чего после тильды надо дописать его имя. Следующая команда выведет домашнюю папку пользователя root: echo ~root
Ещё одно применение тильды - вывод текущей (аналог команды pwd) и предыдущей посещённой папок, для чего требуется ввести после неё знак "+" или "-": cd / && cd ~
Если текст после символа тильды не является именем пользователя или знаками "+" и "-", то замены не происходит: echo ~test
Подстановка имён файловДумаю, с этой видом подстановки вы тоже не раз сталкивались в повседневной работе. Легче всего будет разобраться на примере: создадим временную папку и несколько файлов в ней: touch бок бот ботинок вот кот лот нота рот
Теперь с помощью разных масок будем выводить только нужные нам файлы. Начнём с символа "*", означающего ноль или больше произвольных символов: ls бот*
Срабатывает подстановка, и выводятся имена файлов, начинающиеся с букв "бот". Вопросительный знак в маске - это один произвольный символ: ls ?от
Команда выводит названия файлов, состоящие из трёх букв и заканчивающиеся на "от". А сейчас выберем только те файлы, первая буква в названии которых "б", "в", "к" или "н", а после неё идёт "от": ls [б,в,к,н]от*
То же самое можно написать и так (ведь буква "в" находится между "б" и "к"): ls [б-к,н]от* Или использовав символ "^" или восклицательный знак, обозначающие отрицание: ls [^л,р]от*
Замена выражений в фигурных скобкахТеперь перейдем к более сложному типу замены - раскрытию скобок. Предположим, что нам нужно создать несколько папок для фотографий, названных по месяцам и годам: "фото_мм_гг". Можно сделать это так: mkdir фото_01_09 фото_02_09 фото_03_09 фото_04_09 фото_05_09 ... фото_12_09 И вместо многоточия перечислить все 12 (по количеству месяцев) папок. Легко заметить, что в названии меняется только номер месяца, остальная же часть везде одинаковая. А сейчас сделаем то же самое, но применив раскрытие скобок: mkdir фото_{01..12}_09
После выполнения этой команды в текущем каталоге будут созданы 12 папок с названиями желаемого нами формата. Итак, мы видим, что вторая команда получилась намного короче первой. Примечание: в третьей и более ранних версиях Bash (то есть в релизах Ubuntu, вышедших до 9.10) открывающие нули в случае с двоеточием игнорируются, и мы получим папки с названиями: "фото_1_09", "фото_2_09" и т.д. Чтобы обойти это, придётся немного усложнить команду: mkdir фото_{0{1..9},10,11,12}_09 Разберём, как это работает. Внутри скобок написана последовательность чисел от 1 до 12, к каждому из которых после замены добавляются префикс "фото_" и суффикс "_09", в результате чего мы и получаем названия папок соответственно месяцам. Префикс и суффикс являются необязательными частями замены, можно, например, создать папки только по названиям месяцев: mkdir {январь,февраль,март} Обратите внимание на то, что слова в скобках не разделены пробелами, иначе получится совсем не то, чего мы хотели. Если же нам понадобится создать папки для всех месяцев, кроме июня и августа, то надо будет выполнить такую команду: mkdir фото_{{01..05},07,{09..12}}_09 Или, если у вас старая версия Bash: mkdir фото_{0{1..5},07,09,{10..12}}_09 Здесь уже происходит тройная замена. Сначала раскрываются скобки {01..05} и {09..12}, после чего получается {01,02,03,04,05,07,09,10,11,12}. А потом заменяются оставшиеся скобки, и в результате мы получаем 10 нужных нам папок. Кроме числовых в фигурных скобках можно использовать и буквенные последовательности (буквы должны быть латинскими). Несколько примеров: echo {1..12}
При этом шаг итерации всегда равен одной единице - нельзя вывести, например, только нечётные числа. Другой практический пример: мы хотим кое-что подправить в файле xorg.conf, но перед этим нам нужно сделать его резервную копию. Раньше мы выполняли такую команду: sudo cp /etc/X11/xorg.conf /etc/X11/xorg.conf.bak Теперь же воспользуемся заменой скобок: sudo cp /etc/X11/xorg.conf{,.bak} Так как первый элемент в скобках пустой, в роли копируемого файла берётся сам xorg.conf. Замечу, что раскрытие скобок идёт слева направо, и его результаты не сортируются. То есть если бы мы сделали пустым не первый элемент, а второй, то наша команда стала бы равнозначна следующей: sudo cp /etc/X11/xorg.conf.bak /etc/X11/xorg.conf В этом случае мы либо перезаписали бы файл xorg.conf его копией, либо получили бы уведомление о том, что файла xorg.conf.bak не существует. Также стоит отметить, что в фигурных скобках можно использовать другие замены и подстановки. Приведу пример с подстановкой имён файлов по маске: touch file{1,2,_test} Эта команда удалит все созданные файлы. И последнее: в элементах в фигурных скобках нужно с помощью бэкслеша ("\") экранизировать, то есть маскировать, следующие символы: пробел, запятая, точка с запятой, восклицательный знак, вертикальная черта ("|"), амперсанд ("&"), угловые скобки ("<" и ">"), фигурные скобки и сам бэкслеш. Пример: echo {пер\ вый,вто\,рой,тре\\тий}
Подстановка значений переменныхС помощью символа "$" в Bash можно подставлять значения переменных окружения, таких как USER, HOME, PWD и др. Имя переменной можно писать либо сразу после "$", либо заключать в фигурные скобки. echo "Текущий пользователь:" $USER
Список всех действующих переменных можно посмотреть командой: env Кроме этого, существует так называемая косвенная подстановка, при которой значение написанной переменной используется как имя другой переменной. Например, создадим переменную TEST, присвоим ей значение "USER" и выполним обычную и косвенную подстановки: TEST=USER
Также стоит обратить внимание на одну особенность, связанную с заменой выражений в фигурных скобках. Допустим, нам надо вывести пути к текущей и предыдущей папкам: cd / && cd ~
Возникает желание переписать эту команду одним из следующих способов: echo ${,OLD}PWD
Но в первом случае Bash выдаёт ошибку, а во втором - выводится только значение первой переменной (или тоже ошибка, если установлена старая версия Bash). Правильной же будет такая команда: echo {$,$OLD}PWD
Таким образом, при замене фигурных скобок нельзя использовать префикс "$". Подстановка результатов выполнения командЭто ещё один тип замены в Bash, использующий символ "$". Она имеет две формы написания: $(команда) Приведу пример с выводом результата команды uptime: echo $(uptime)
Обратите внимание, что по умолчанию результаты подстановки выводятся без переносов строк: echo $(ls -l)
Чтобы этого избежать, надо заключить подстановку в двойные кавычки: echo "$(ls -l)"
Ну и расскажу про небольшую хитрость при подстановке вывода команды cat. Её можно осуществить так: echo $(cat text_file) Но то же самое можно выполнить быстрее (как в смысле набора команды, так и времени выполнения): echo $(< text_file)
Подстановка значений арифметических выраженийИ снова замена с использованием символа "$" - вместо арифметических выражений в Bash можно подставлять их результаты (здесь тоже две формы ввода): $((выражение)) В выражениях можно использовать следующие операции (перечислено по убыванию приоритета):
пре-инкремент ("++имя") и пре-декремент ("--имя"); унарные плюс ("+") и минус ("-"); логическое ("!") и побитовое ("~") отрицания; возведение в степень ("**"); умножение ("*"), целочисленное деление ("/") и остаток от него ("%"); сложение ("+") и вычитание ("-"); побитовые сдвиги влево ("<<") и вправо (">>"); сравнения (">=", "<=", ">", "<"); равенство ("==") и неравенство ("!="); побитовое "И" ("&"); побитовое исключающее "ИЛИ" ("^"); побитовое "ИЛИ" ("|"); логическое "И" ("&&"); логическое "ИЛИ" ("||"); условное выражение ("условие?значение1:значение2"); присваивания ("=", "*=", "/=", "%=", "+=", "-="); запятая ("выражение1,выражение2"). Кроме этого в выражениях можно использовать круглые скобки, и разрешена вставка вложенных выражений. Приведу несколько примеров: echo $((3+4))
Операцию логического отрицания (шестой пример) лучше всегда заключать в отдельное выражение, так как иначе Bash может неправильно интерпретировать символ "!" и посчитать это за ошибку. Для операций с числами, представленных в системах счисления, отличных от десятичной, используется такая запись: основание#число Основание может быть любым десятичным числом в диапазоне от 2 до 64. К примеру, логическое "И" чисел 6 и 3 в этом случае будет выглядеть так: echo $[ 2#110 & 2#11 ]
Для шестнадцетиричных и восьмеричных чисел существует собственная запись: echo "Пример с шестнадцетиричными числами: " $[ 0xA + 0X5 ]
В первом примере не имеет значения, используется ли строчный символ "x" или прописной "X". Также в выражения можно подставлять значения переменных: A=1 && B=2
И результаты выполнения команд: date +%Y
Подстановка процессовВ следующем примере нам нужно вывести все файлы и папки, содержащие подстроку "doc": ls -l | grep doc
Это же можно сделать, применив подстановку процессов: grep doc <(ls -l)
Здесь символ "<" означает создание временного файла типа "/dev/fd/xxx", в который будет перенаправлен вывод команды ls -l, и подстановку его имени вместо этой команды. После этого grep doc принимает имя файла и отбирает из его содержимого нужные строки. Теперь выполним предыдущую команду наоборот, создав временный файл для перенаправления ввода: ls -l > >(grep doc)
В этом примере вывод ls -l перенаправляется в созданный временный файл, а команда grep doc принимает данные из этого файла. И практический пример: нам нужно вывести отсортированное содержимое двух папок (только имена файлов) и одного текстового файла. Для начала создадим их (советую сделать это в отделной пустой папке): mkdir documents && touch documents/file{1,3,5} Дальше можно поступить и так: echo -e "$(ls documents)" "\n$(ls images)" "\n$(less words)" | sort Но вариант с применением подстановки процессов проще: sort <(ls documents) <(ls images) <(less words)
Разбиение словЭтот вид замены происходит при подстановке значений переменных и результатов выражений и выполнения команд. Создадим переменную TEST: TEST=1@2_3@4_5@6
После этого присвоим следующее значение переменной IFS (Internal Field Separator), которая отвечает за разделение слов на части, и выведем значение переменной TEST: IFS="@_"
Как мы видим, символы "@" и "_" стали разделителями, и теперь заменяются пробелами. Чтобы вывести значение TEST без разбиения, заключим её имя в двойные кавычки: echo "$TEST"
И наконец, восстановим значение переменной IFS по умолчанию: IFS=""
Порядок выполнения замен и подстановокНу и последнее, про что надо знать - замены и подстановки тоже имеют свой порядок приоритетов:
затем замены тильды; подстановки значений переменных, результатов выражений и выполнения команд и подстановки процессов; разбиения слов; и последними - подстановки имён файлов. ----- Спасибо sKwa за примеры и замечания. |



































