Bash подавить вывод ошибок

How can I suppress error messages for a shell command? For example, if there are only jpg files in a directory, running ls *.zip gives an error message: $ ls *.zip ls: cannot access '*.zip': No

How can I suppress error messages for a shell command?

For example, if there are only jpg files in a directory, running ls *.zip gives an error message:

   $ ls *.zip
   ls: cannot access '*.zip': No such file or directory

Is there an option to suppress such error messages? I want to use this command in a Bash script, but I want to hide all errors.

Peter Mortensen's user avatar

asked Sep 3, 2015 at 15:31

Peter's user avatar

6

Most Unix commands, including ls, will write regular output to standard output and error messages to standard error, so you can use Bash redirection to throw away the error messages while leaving the regular output in place:

ls *.zip 2> /dev/null

Peter Mortensen's user avatar

answered Sep 3, 2015 at 15:33

AJefferiss's user avatar

AJefferissAJefferiss

1,5731 gold badge12 silver badges17 bronze badges

1

$ ls *.zip 2>/dev/null

will redirect any error messages on stderr to /dev/null (i.e. you won’t see them)

Note the return value (given by $?) will still reflect that an error occurred.

answered Sep 3, 2015 at 15:34

Brian Agnew's user avatar

Brian AgnewBrian Agnew

266k36 gold badges331 silver badges439 bronze badges

To suppress error messages and also return the exit status zero, append || true. For example:

$ ls *.zip && echo hello
ls: cannot access *.zip: No such file or directory
$ ls *.zip 2>/dev/null && echo hello
$ ls *.zip 2>/dev/null || true && echo hello
hello

$ touch x.zip
$ ls *.zip 2>/dev/null || true && echo hello
x.zip
hello

Peter Mortensen's user avatar

answered Nov 11, 2017 at 6:45

A-C's user avatar

A-CA-C

1211 silver badge4 bronze badges

2

I attempted ls -R [existing file] and got an immediate error.
ls: cannot access ‘existing file’: No such file or directory

So, I used the following:

ls -R 2>dev/null | grep -i [existing file]*

ls -R 2>dev/null | grep -i text*

Or, in your case:

ls -R 2>dev/null | grep -i *.zip

answered Jan 4, 2022 at 19:36

Richard's user avatar

1

what I use as solution with raspberry pi3 buster.

ls -R 2>/dev/null | grep -i [existing file]*

answered 2 days ago

fr3d mobile's user avatar

New contributor

fr3d mobile is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

1

8.4.2. Подавление вывода сообщений об ошибках

Допустим, вы хотите найти запись пользователя louise в системном файле паролей:

$ grep louise /etc/passwd

louise:lxAL6GW9G.ZyY:501:501:Accounts Sect1С:/home/accts/louise:/bin/sh

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

$ grep louise /etc/password

grep: /etc/password: No such file or directory

Команда grep выводит сообщение об ошибке, в которой говорится о том, что указанного файла не существует. Можно попробовать провести поиск во всех файлах каталога /etc:

$ grep louise /etc/*

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

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

$ grep -a louise /etc/*

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

$ grep louise /etc/* 2> /dev/null

Эта команда направляет поток ошибок (2>) в системную корзину (устройство /dev/null). На жаргоне системных администраторов это устройство называется битодробилкой.

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

Уровни вывода сообщений ядра

Уровни вывода сообщений ядра
Главное отличие между функциями printk() и printf() — это возможность в первой указывать уровень вывода сообщений ядра (loglevel). Ядро использует уровень вывода сообщений для принятия решения о том, выводить сообщение на консоль или нет. Ядро выводит на

Отправка сообщений об ошибках

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

7.2 Сообщения об ошибках ICMP

7.2 Сообщения об ошибках ICMP
Бывают ситуации, приводящие к отбрасыванию (удалению из сети) датаграммы IP. Например, точка назначения может стать недоступной из-за обрыва связи. Или может завершиться время жизни датаграммы. Маршрутизатор не сможет переслать длинную

7.2.1 Типы сообщений об ошибках

7.2.1 Типы сообщений об ошибках
На рис. 7.3 показаны обобщенные сообщения, формируемые маршрутизатором и хостом назначения для отчета о возникшей проблеме. В таблице 7.1 перечислены формальные имена сообщений об ошибках ICMP.

Рис. 7.3. Типы сообщений об ошибках ICMPТаблица 7.1

Подавление линий заднего плана

Подавление линий заднего плана

Команда HIDE обеспечивает создание рисунка без скрытых линий. Сложные трехмерные модели часто оказываются перегруженными, что затрудняет их чтение и просмотр результатов выполнения какой-либо команды на объекте. Можно устранить эту

В.З. Стандартные функции вывода сообщений об ошибках

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

Отчет об ошибках

Отчет об ошибках
На вкладке Дополнительно в окне Свойства системы нажимаем кнопку Отчет об ошибках. Откроется одноименное окно Отчет об ошибках (рис. 4.5), в котором устанавливаем переключатель в положение Отключить отчет об ошибках и оставляем установленным флажок Но

Получение и пересылка сообщений. Создание ответных сообщений

Получение и пересылка сообщений. Создание ответных сообщений
Чтобы получить новую почту, выполните команду главного меню Сервис ? Отправить/Получить ? Доставить почту либо нажмите клавишу F9. Все полученные почтовые сообщения будут помещены в папку Входящие и помечены

Шаг 20 — Временные объекты. Неявные вызовы конструкторов и их подавление.

Шаг 20 — Временные объекты. Неявные вызовы конструкторов и их подавление.
Не удается углубиться в какую-либо тему. Приходится касаться по верхам, потом переключаться на что-то другое. С другой стороны, может это и правильно, часто достаточно только знать, что есть ТАКОЕ

Настройка вывода сообщений об ошибках с помощью ‹customErrors›

Настройка вывода сообщений об ошибках с помощью ‹customErrors›
Элемент ‹customErrors› может использоваться для автоматического перенаправления всех ошибок в пользовательский набор файлов *.htm. Это может оказаться полезным тогда, когда вы хотите построить более понятную для

Подавление линий заднего плана

Подавление линий заднего плана

Команда HIDE обеспечивает создание рисунка без скрытых линий. Сложные трехмерные модели часто оказываются перегруженными, что затрудняет их чтение и просмотр результатов выполнения какой-либо команды на объекте. Можно устранить эту

Сообщение об ошибках gbak

Сообщение об ошибках gbak
В табл. 38.3 описаны сообщения об ошибках, которые могут возникнуть в процессе копирования и восстановления, вместе с некоторыми советами, как поступать с этими ошибками.Таблица 38.3. Сообщения об ошибках gbak при копировании и восстановлении

Сообщение

Подавление линий заднего плана

Подавление линий заднего плана

Команда HIDE обеспечивает создание рисунка без скрытых линий . Сложные трехмерные модели часто оказываются перегруженными, что затрудняет их чтение и просмотр результатов выполнения какой-либо команды на объекте. Можно устранить эту

Подавление линий заднего плана

Подавление линий заднего плана

Команда HIDE обеспечивает создание рисунка без скрытых линий. Сложные трехмерные модели часто оказываются перегруженными, что затрудняет их чтение и просмотр результатов выполнения какой-либо команды на объекте. Можно устранить эту

Отчеты об ошибках

Отчеты об ошибках
В операционной системе Windows Vista реализован новый механизм отчетов об ошибках. Теперь ограничить и настроить его работу можно с помощью следующих параметров, расположенных в ветви реестра HKEY_CURRENT_USERSoftwarePoliciesMicrosoftWindowsWindows Error Reporting. Если не сказано иное,

Всё в Linux — это файлы, в том числе — ввод и вывод. Операционная система идентифицирует файлы с использованием дескрипторов. Каждому процессу позволено иметь до девяти открытых дескрипторов файлов. Оболочка bash резервирует первые три дескриптора с идентификаторами 0, 1 и 2. Вот что они означают.

  • 0, STDIN — стандартный поток ввода
  • 1, STDOUT — стандартный поток вывода
  • 2, STDERR — стандартный поток ошибок

STDIN

STDIN — это стандартный поток ввода оболочки. Для терминала стандартный ввод — это клавиатура. Когда используется символ перенаправления ввода — <, Linux заменяет дескриптор файла стандартного ввода на тот, который указан в команде. Система читает файл и обрабатывает данные так, будто они введены с клавиатуры.

Многие команды bash принимают ввод из STDIN, если в командной строке не указан файл, из которого надо брать данные. Например, это справедливо для команды cat.

Когда вы вводите команду cat в командной строке, не задавая параметров, она принимает ввод из STDIN. После того, как вы вводите очередную строку, cat просто выводит её на экран. И останавливается после того как получает EOF. EOF вводится нажатием сочетания клавиш Ctrl+D.

STDOUT

STDOUT — стандартный поток вывода оболочки. По умолчанию это — экран. Большинство bash-команд выводят данные в STDOUT, что приводит к их появлению в консоли. Данные можно перенаправить в файл, для этого используется символ перенаправления вывода >.

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

STDERR

STDERR представляет собой стандартный поток ошибок оболочки. По умолчанию этот дескриптор указывает на то же самое, на что указывает STDOUT, именно поэтому при возникновении ошибки мы видим сообщение на экране.

Рассмотрим простой пример — нужно записать в файл data.txt содержимое директории data. Если директория не существует, сообщение об ошибке надо записать в файл error.txt. Чтобы этого добиться, нужно использовать команды перенаправления для соответствующих дескрипторов с указанием файлов, куда должны попадать ошибки и стандартный вывод:

$ ls data 1> data.txt 2> error.txt

Если надо, STDERR и STDOUT можно перенаправить в один и тот же файл, воспользовавшись &>:

$ ls data &> data-error.txt

Устаревшая форма записи:

$ ls data > data-error.txt 2>&1

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

$ ls data 2>&1 > data-error.txt

В этом случае сначала вывод ошибок перенаправляется в стандартный вывод (на экран), а потом стандартный вывод перенаправляется в файл data-error.txt. То есть, ошибки будут выведены в консоль, а данные — в файл.

Для дозаписи в файл data-error.txt

$ ls data &>> data-error.txt

Перенаправление вывода в скриптах

Существует два метода перенаправления вывода в сценариях командной строки:

  • Временное перенаправление, или перенаправление вывода одной строки
  • Постоянное перенаправление, или перенаправление всего вывода сценария

Временное перенаправление вывода

В сценарии можно перенаправить вывод отдельной строки в STDERR. Чтобы это сделать, достаточно использовать команду перенаправления, указав дескриптор STDERR, при этом перед номером дескриптора надо поставить символ амперсанда:

#!/bin/bash
echo "This is an error" >&2
echo "This is normal output"

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

$ ./test.sh
This is an error
This is normal output

Запустим скрипт так, чтобы вывод STDERR попадал в файл:

$ ./test.sh 2> error.txt
This is normal output
$ cat error.txt
This is an error

Теперь обычный вывод делается в консоль, а сообщения об ошибках попадают в файл error.txt.

Постоянное перенаправление вывода

Если в скрипте нужно перенаправлять много выводимых на экран данных, добавлять >&2 к каждому вызову echo неудобно. Вместо этого можно задать перенаправление вывода в определённый дескриптор на время выполнения скрипта, воспользовавшись командой exec:

#!/bin/bash
exec 1> output.txt
echo "Это пример перенаправления всего"
echo "вывода сценария в файл output.txt"
echo "без перенаправления каждой строки"

После запуска сценария весь вывод будет перенаправлен в файл output.txt.

Команду exec можно использовать не только в начале скрипта, но и в других местах:

#!/bin/bash

# ошибки перенаправляем в файл error.txt
exec 2> error.txt
# строка будет выведена на экран
echo "Первый вывод сценария"
# перенаправляем вывод в файл output.txt
exec 1> output.txt
# строка будет записана в файл output.txt
echo "Второй вывод сценария"
# строка будет записана в файл error.txt
echo "Третий вывод сценария" >&2

Перенаправление ввода в скриптах

Для перенаправления ввода можно воспользоваться тем же способом, который использовался для перенаправления вывода. Например, команда exec позволяет сделать источником данных для STDIN какой-нибудь файл:

$ exec 0< data.txt

Эта команда указывает оболочке на то, что источником вводимых данных должен стать файл data.txt, а не обычный STDIN. Посмотрим на перенаправление ввода в действии:

#!/bin/bash
exec 0< data.txt
count=1
while read line ; do
    echo "Line #$count: $line"
    count=$(( $count + 1 ))
done

Подавление вывода

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

Вот, например, как подавить вывод сообщений об ошибках:

$ ls -al badfile.txt file.txt 2> /dev/null

Тот же подход используется, если, например, надо очистить файл, не удаляя его:

$ cat /dev/null > data.txt

Поиск:
Bash • CLI • Linux • stderr • stdin • stdout • Поток

Каждый раз, когда мы запускаем команду Bash на нашем терминале Linux Mint 20, обычно мы видим какой-то вывод на терминале. То же самое для команд и сценариев Bash. Иногда мы можем не желать видеть такой результат. Это происходит особенно, когда мы хотим отладить программу и заинтересованы только в обнаружении возникающих ошибок. В этой ситуации, если нам будет представлен весь вывод, он будет не только бесполезен для нас, но и будет тратить наше время на поиск реальной проблемы.

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

Метод подавления всего вывода команды Bash в Linux Mint 20:

Чтобы объяснить вам метод подавления всего вывода команды Bash в Linux Mint 20, мы хотели бы поделиться с вами некоторыми примерами.

Мы создали простой сценарий Bash, в котором мы просто печатаем случайное сообщение на терминале. Мы будем использовать этот сценарий Bash в Примере №1 и Примере №2. Этот сценарий Bash отображается на прикрепленном изображении. Мы назвали наш файл Bash Suppress.sh.

Сценарий Bash, который мы только что создали, можно просто запустить с помощью команды «bash». Прежде чем подавлять вывод команды «bash», мы сначала хотели бы показать вам ее фактический вывод. Для этого вам нужно выполнить свой сценарий Bash следующим образом:

$ трепать Suppress.sh


Выполнение этого сценария Bash просто отобразит наше фиктивное сообщение на терминале, как показано ниже:

Теперь, чтобы подавить вывод команды «bash», мы запустим следующую команду в нашем терминале:

$ трепать Suppress.sh >/разработчик/значение NULL


Выполнение процитированной выше команды отправит весь вывод в корзину> / dev / null, и, следовательно, на вашем терминале ничего не будет отображаться, как показано на добавленном ниже изображении:

Пример №2: Подавление вывода команды «cat»:

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

$ Кот Suppress.sh


Выполнение команды «cat» просто отобразит содержимое нашего файла сценария Bash на терминале, как показано ниже:

Теперь, чтобы подавить вывод команды «cat», мы запустим следующую команду в нашем терминале:

$ Кот Suppress.sh >/разработчик/значение NULL


При выполнении указанной выше команды весь вывод будет отправлен в корзину> / dev / null, и, следовательно, на вашем терминале ничего не будет отображаться, как показано на изображении ниже:

Пример №3: Подавление вывода команды «–help»:

Если вы хотите узнать подробности использования какой-либо команды или пакета в Linux Mint 20, вы можете использовать команду «–help». Прежде чем подавлять вывод команды «–help», мы сначала хотели бы показать вам ее фактический вывод. Для этого вам нужно запустить команду «–help» следующим образом:

$ нано—помощь


Мы хотели получить доступ к справочному руководству редактора nano, которое показано на изображении ниже:

Теперь, чтобы подавить вывод команды «–help», мы запустим следующую команду в нашем терминале:

$ нано—помощь>/разработчик/значение NULL


При выполнении указанной выше команды весь вывод будет отправлен в корзину> / dev / null, и, следовательно, на вашем терминале ничего не будет отображаться, как показано на изображении, добавленном ниже:

Пример №4: Подавление вывода команды «–версия»:

Если вы хотите проверить версию любого установленного пакета или команды в Linux Mint 20, вы можете использовать команду «–version». Прежде чем подавлять вывод команды «–version», мы сначала хотели бы показать вам ее фактический вывод. Для этого вам нужно запустить команду «–версия» следующим образом:

$ нано—версия


Мы хотели проверить версию редактора nano, которая показана на изображении ниже:

Теперь, чтобы подавить вывод команды «–version», мы запустим следующую команду в нашем терминале:

$ нано—версия>/разработчик/значение NULL


При выполнении указанной выше команды весь вывод будет отправлен в корзину> / dev / null, и, следовательно, на вашем терминале ничего не будет отображаться, как показано на изображении, добавленном ниже:

Пример № 5: Подавление вывода команды «man»:

Когда бы вы ни захотели прочитать руководство или справочные страницы любой установленной команды или пакета в Linux Mint 20, вы можете использовать команду «man». Прежде чем подавлять вывод команды «man», мы сначала хотели бы показать вам ее фактический вывод. Для этого вам нужно запустить команду «man» следующим образом:

$ человекнано


Мы хотели получить доступ к руководству редактора nano, которое показано на изображении ниже:

Теперь, чтобы подавить вывод команды «man», мы запустим следующую команду в нашем терминале:

$ человекнано>/разработчик/значение NULL


При выполнении указанной выше команды весь вывод будет отправлен в корзину> / dev / null, и, следовательно, на вашем терминале ничего не будет отображаться, как показано на изображении ниже:

Вывод:

В этой статье мы поделились с вами пятью различными примерами подавления всего вывода команды Bash в Linux Mint 20. Пройдя эти примеры, теперь вы легко сможете подавить вывод любой желаемой команды Bash или сценария Bash при использовании Linux Mint 20.

Как устроены bash-скрипты

#!/usr/bin/env bash

# This is a comment

pwd
whoami

Детальная информация

Как устроены bash-скрипты

Создайте пустой файл с использованием команды touch. В его первой строке нужно указать, какую именно
оболочку # мы собираемся использовать.
Нас интересует bash, поэтому первая строка файла будет такой:

!/bin/bash

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

Команды оболочки отделяются знаком перевода строки, комментарии выделяют знаком решётки.
Вот как это выглядит:

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

Сравнение чисел

#!/bin/bash

#!/usr/bin/env bash

val1=6
if [ $val1 -gt 5 ]; then
    echo "The test value $val1 is greater than 5"
else
    echo "The test value $val1 is not greater than 5"
fi

Детальная информация

Сравнение чисел

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

n1 -eq n2 Возвращает истинное значение, если n1 равно n2.
n1 -ge n2 Возвращает истинное значение, если n1 больше или равно n2.
n1 -gt n2 Возвращает истинное значение, если n1 больше n2.
n1 -le n2 Возвращает истинное значение, если n1 меньше или равно n2.
n1 -lt n2 Возвращает истинное значение, если n1 меньше n2.
n1 -ne n2 Возвращает истинное значение, если n1 не равно n2.

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

#!/bin/bash
val1=6
if [ $val1 -gt 5 ] then
	echo "The test value $val1 is greater than 5"
else
	echo "The test value $val1 is not greater than 5"
fi

Значение переменной val1больше чем 5, в итоге выполняется ветвь thenоператора сравнения и в консоль выводится соответствующее сообщение.

Сравнение строк

#!/bin/bash
#!/bin/bash
#!/bin/bash
#!/bin/bash

#!/usr/bin/env bash

# Вот пример сравнения строк в сценарии:

user ="likegeeks"
if [$user = $USER]; then
    echo "The user $user  is the current logged in user"
fi

# Вот одна особенность сравнения строк, о которой стоит упомянуть. А именно, операторы «>» и «<» необходимо экранировать с помощью обратной косой черты, иначе скрипт будет работать неправильно, хотя сообщений об ошибках и не появится. Скрипт интерпретирует знак «>» как команду перенаправления вывода.

# Вот как работа с этими операторами выглядит в коде:

val1=text
val2="another text"
if [ $val1 > "$val2" ]; then
    echo "$val1 is greater than $val2"
else
    echo "$val1 is less than $val2"
fi

# Ещё одна особенность операторов «>» и «<» заключается в том, как они работают с символами в верхнем и нижнем регистрах. Для того, чтобы понять эту особенность, подготовим текстовый файл с таким содержимым:

sort user_list.txt

Детальная информация

Сравнение строк

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

str1 = str2 Проверяет строки на равенство, возвращает истину, если строки идентичны.
str1 != str2 Возвращает истину, если строки не идентичны.
str1 < str2 Возвращает истину, если str1 меньше, чем str2.
str1 > str2 Возвращает истину, если str1 больше, чем str2.
-n str1 Возвращает истину, если длина str1 больше нуля.
-z str1 Возвращает истину, если длина str1 равна нулю.

Вот пример сравнения строк в сценарии:

#!/bin/bash
user ="likegeeks"
if [$user = $USER] then
	echo "The user $user  is the current logged in user"
fi

Вот одна особенность сравнения строк, о которой стоит упомянуть. А именно, операторы «>» и «<» необходимо экранировать с помощью обратной косой черты, иначе скрипт будет работать неправильно, хотя сообщений об ошибках и не появится. Скрипт интерпретирует знак «>» как команду перенаправления вывода.

Вот как работа с этими операторами выглядит в коде:

#!/bin/bash
val1=text
val2="another text"
if [ $val1 > $val2 ] then
	echo "$val1 is greater than $val2"
else
	echo "$val1 is less than $val2"
fi

Обратите внимание на то, что скрипт, хотя и выполняется, выдаёт предупреждение:

./myscript: line 5: [: too many arguments

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

#!/bin/bash
val1=text
val2="another text"
if [ $val1 > "$val2" ] then
	echo "$val1 is greater than $val2"
else
	echo "$val1 is less than $val2"
fi

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

Сохраним его, дав имя myfile, после чего выполним в терминале такую команду:

Она отсортирует строки из файла так:

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

#!/bin/bash
val1=Likegeeks
val2=likegeeks
if [ $val1 > $val2 ] then
	echo "$val1 is greater than $val2"
else
	echo "$val1 is less than $val2"
fi

Если его запустить, окажется, что всё наоборот — строчная буква теперь больше прописной.

В командах сравнения прописные буквы меньше строчных. Сравнение строк здесь выполняется путём сравнения ASCII-кодов символов, порядок сортировки, таким образом, зависит от кодов символов.

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

Проверки файлов

#!/bin/bash

#!/usr/bin/env bash

if [ -e user_list.txt ]; then
    echo 1;
else
    echo 2;
fi

Детальная информация

Проверки файлов

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

-d file Проверяет, существует ли файл, и является ли он директорией.
-e file Проверяет, существует ли файл.
-f file Проверяет, существует ли файл, и является ли он файлом.
-r file Проверяет, существует ли файл, и доступен ли он для чтения.
-s file Проверяет, существует ли файл, и не является ли он пустым.
-w file Проверяет, существует ли файл, и доступен ли он для записи.
-x file Проверяет, существует ли файл, и является ли он исполняемым.
file1 -nt file2 Проверяет, новее ли file1, чем file2.
file1 -ot file2Проверяет, старше ли file1, чем file2.
-O file Проверяет, существует ли файл, и является ли его владельцем текущий пользователь.
-G file Проверяет, существует ли файл, и соответствует ли его идентификатор группы идентификатору группы текущего пользователя.

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

Опробуем одну из команд на практике:

#!/bin/bash
mydir=/home/likegeeks
if [ -d $mydir ] then
	echo "The $mydir directory exists"
	cd $ mydir
	ls
else
	echo "The $mydir directory does not exist"
fi

Вывод сообщений

#!/bin/bash

our comment is here

#!/usr/bin/env bash

# our comment is here

echo "The current directory is:"
pwd
echo "The user logged in is:"
whoami

Детальная информация

Вывод сообщений

Для вывода текста в консоль Linux применяется команда echo. Воспользуемся знанием этого факта и отредактируем наш скрипт, добавив пояснения к данным, которые выводят уже имеющиеся в нём команды:

#!/bin/bash
# our comment is here
echo "The current directory is:"
pwd
echo "The user logged in is:"
whoami

Теперь мы можем выводить поясняющие надписи, используя команду echo. Если вы не знаете, как отредактировать файл, пользуясь средствами Linux, или раньше не встречались с командой echo, взгляните на этот материал.

Использование переменных

Детальная информация

Использование переменных

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

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

Существуют два типа переменных, которые можно использовать в bash-скриптах:

  • Переменные среды
  • Пользовательские переменные

Переменные среды

#!/bin/bash

display user home

#!/usr/bin/env bash

echo "Home for the current user id: $HOME -> $HOME"

Детальная информация

Переменные среды

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

#!/bin/bash
# display user home
echo "Home for the current user is: $HOME"

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

А что если надо вывести на экран значок доллара? Попробуем так:

echo "I have $1 in my pocket"

Система обнаружит знак доллара в строке, ограниченной кавычками, и решит, что мы сослались на переменную. Скрипт попытается вывести на экран значение неопределённой переменной $1. Это не то, что нам нужно. Что делать?

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

echo "I have $1 in my pocket"

Пользовательские переменные

#!/bin/bash

testing variables

#!/usr/bin/env bash

name='Alex'
surname='Popov'
echo $name $surname

Детальная информация

Пользовательские переменные

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

Как и в случае с системными переменными, к пользовательским переменным можно обращаться, используя знак доллара:

#!/bin/bash
# testing variables
grade=5
person="Adam"
echo "$person is a good boy, he is in grade $grade"

Подстановка команд

#!/bin/bash

#!/usr/bin/env bash

mydir=`pwd`
mydir_2=$(pwd)
echo $mydir ' or ' $mydir_2

Детальная информация

Подстановка команд

Одна из самых полезных возможностей bash-скриптов — это возможность извлекать информацию из вывода команд и назначать её переменным, что позволяет использовать эту информацию где угодно в файле сценария.

Сделать это можно двумя способами.

  • С помощью значка обратного апострофа «`»
  • С помощью конструкции $()

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

При втором подходе то же самое записывают так:

А скрипт, в итоге, может выглядеть так:

#!/bin/bash
mydir=$(pwd)
echo $mydir

В ходе его работы вывод команды pwdбудет сохранён в переменной mydir, содержимое которой, с помощью команды echo, попадёт в консоль.

Математические операции

#!/bin/bash

#!/usr/bin/env bash

var1=$(( 5 + 5 ))
echo $var1

Детальная информация

Математические операции

Для выполнения математических операций в файле скрипта можно использовать конструкцию вида $((a+b)):

#!/bin/bash
var1=$(( 5 + 5 ))
echo $var1
var2=$(( $var1 * 2 ))
echo $var2

Управляющая конструкция if-then

#!/bin/bash
#!/bin/bash

#!/usr/bin/env bash

if [ 1 -eq 1 ]; then
    echo true;
else
    echo false;
fi

Детальная информация

Управляющая конструкция if-then

В некоторых сценариях требуется управлять потоком исполнения команд. Например, если некое значение больше пяти, нужно выполнить одно действие, в противном случае — другое. Подобное применимо в очень многих ситуациях, и здесь нам поможет управляющая конструкция if-then. В наиболее простом виде она выглядит так:

if команда then
	команды
fi

А вот рабочий пример:

#!/bin/bash
if pwd then
	echo "It works"
fi

В данном случае, если выполнение команды pwdзавершится успешно, в консоль будет выведен текст «it works».

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

#!/bin/bash
user=likegeeks
if grep $user /etc/passwd then
	echo "The user $user Exists"
fi

Здесь мы воспользовались командой grepдля поиска пользователя в файле /etc/passwd. Если команда grepвам незнакома, её описание можно найти здесь.

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

Управляющая конструкция if-then-else

#!/bin/bash
#!/bin/bash

#!/usr/bin/env bash

user=alex
if [ $(grep $user user_list.txt) ]; then
    echo "$user is found of file user_list.txt";
else
    echo 'User is not found';
fi

Детальная информация

Управляющая конструкция if-then-else

Для того, чтобы программа смогла сообщить и о результатах успешного поиска, и о неудаче, воспользуемся конструкцией if-then-else. Вот как она устроена:

if команда then
	команды
else
	команды
fi

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

Напишем такой скрипт:

#!/bin/bash
user=anotherUser
if grep $user /etc/passwd then
	echo "The user $user Exists"
else
	echo "The user $user doesn’t exist"
fi

Его исполнение пошло по ветке else.

Ну что же, продолжаем двигаться дальше и зададимся вопросом о более сложных условиях. Что если надо проверить не одно условие, а несколько? Например, если нужный пользователь найден, надо вывести одно сообщение, если выполняется ещё какое-то условие — ещё одно сообщение, и так далее. В подобной ситуации нам помогут вложенные условия. Выглядит это так:

if команда1 then
	команды
elif команда2 then
	команды
fi

Если первая команда вернёт ноль, что говорит о её успешном выполнении, выполнятся команды в первом блоке then, иначе, если первое условие окажется ложным, и если вторая команда вернёт ноль, выполнится второй блок кода.

#!/bin/bash
user=anotherUser
if grep $user /etc/passwd then
	echo "The user $user Exists"
elif ls /home then
	echo "The user doesn’t exist but anyway there is a directory under /home"
fi

В подобном скрипте можно, например, создавать нового пользователя с помощью команды useradd, если поиск не дал результатов, или делать ещё что-нибудь полезное.

Циклы for. Перебор простых значений

#!/usr/bin/env bash

for var in test1 test2 test3; do
	echo "$var"
done

Детальная информация

Циклы for. Перебор простых значений

Оболочка bash поддерживает циклы for, которые позволяют организовывать перебор последовательностей значений. Вот какова базовая структура таких циклов:

for var in list do
	команды
done

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

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

Управление циклами

Детальная информация

Управление циклами

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

  • break
  • continue

Обработка вывода, выполняемого в цикле

#!/usr/bin/env bash

for ((i = 0; i < 10; i++)); do
	echo "$i"
done >output_file.txt

Детальная информация

Обработка вывода, выполняемого в цикле

Данные, выводимые в цикле, можно обработать, либо перенаправив вывод, либо передав их в конвейер. Делается это с помощью добавления команд обработки вывода после инструкции done.

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

Оболочка создаст файл myfile.txt и перенаправит в этот файл вывод конструкции for. Откроем файл и удостоверимся в том, что он содержит именно то, что ожидается.

Пример: поиск исполняемых файлов

#!/usr/bin/env bash

IFS=:
for folder in $PATH; do
	count_input=10
	echo "$folder "
	for file in $folder/*.exe; do
		if [ $count_input > 0 ]; then
			# if [ -x "$file" ]; then
			echo "	$file"
			# fi
		else
			break
		fi
		count_input=$(($count_input - 1))
	done
done

Детальная информация

Пример: поиск исполняемых файлов

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

Такой вот скрипт, небольшой и несложный, позволил получить список исполняемых файлов, хранящихся в папках из PATH.

Перебор сложных значений

#!/usr/bin/env bash

for var in "test1 test2" "test3"; do
	echo "$var"
done

Детальная информация

Перебор сложных значений

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

Инициализация цикла списком, полученным из результатов работы команды

#!/usr/bin/env bash

for line in $(cat file_test.txt); do
	echo "$line"
done

Детальная информация

Инициализация цикла списком, полученным из результатов работы команды

Ещё один способ инициализации цикла for заключается в передаче ему списка, который является результатом работы некоей команды. Тут используется подстановка команд для их исполнения и получения результатов их работы.

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

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

Что, если это совсем не то, что нужно?

Разделители полей

#!/usr/bin/env bash

IFS=$'n'
for item in $(cat file_test.txt); do
	echo "$item"
done

Детальная информация

Разделители полей

Причина вышеописанной особенности заключается в специальной переменной окружения, которая называется IFS (Internal Field Separator) и позволяет указывать разделители полей. По умолчанию оболочка bash считает разделителями полей следующие символы:

  • Пробел
  • Знак табуляции
  • Знак перевода строки

Если bash встречает в данных любой из этих символов, он считает, что перед ним — следующее самостоятельное значение списка.

Для того, чтобы решить проблему, можно временно изменить переменную среды IFS. Вот как это сделать в bash-скрипте, если исходить из предположения, что в качестве разделителя полей нужен только перевод строки:

После добавления этой команды в bash-скрипт, он будет работать как надо, игнорируя пробелы и знаки табуляции, считая разделителями полей лишь символы перевода строки.

Разделителями могут быть и другие символы. Например, выше мы выводили на экран содержимое файла /etc/passwd. Данные о пользователях в строках разделены с помощью двоеточий. Если в цикле нужно обрабатывать подобные строки, IFS можно настроить так:

Обход файлов, содержащихся в директории

#!/usr/bin/env bash

for file in $(pwd)/*; do
	if [ -d "$file" ]; then
		echo "$file --> is a directory"
	elif [ -f "$file" ]; then
		echo "$file --> is a file"
	fi
done

Детальная информация

Обход файлов, содержащихся в директории

Один из самых распространённых вариантов использования циклов for в bash-скриптах заключается в обходе файлов, находящихся в некоей директории, и в обработке этих файлов.

Например, вот как можно вывести список файлов и папок:

Если вы разобрались с предыдущим материалом из этой серии статей, вам должно быть понятно устройство конструкции if-then, а так же то, как отличить файл от папки. Если вам сложно понять вышеприведённый код, перечитайте этот материал.

Обратите внимание на то, как мы инициализируем цикл, а именно — на подстановочный знак «*» в конце адреса папки. Этот символ можно воспринимать как шаблон, означающий: «все файлы с любыми именами». он позволяет организовать автоматическую подстановку имён файлов, которые соответствуют шаблону.

При проверке условия в операторе if, мы заключаем имя переменной в кавычки. Сделано это потому что имя файла или папки может содержать пробелы.

Циклы for в стиле C

#!/usr/bin/env bash

for ((i = 0; i < 10; i++)); do
	echo "$i"
done

Детальная информация

Циклы for в стиле C

Если вы знакомы с языком программирования C, синтаксис описания bash-циклов for может показаться вам странным, так как привыкли вы, очевидно, к такому описанию циклов:

В bash-скриптах можно использовать циклы for, описание которых выглядит очень похожим на циклы в стиле C, правда, без некоторых отличий тут не обошлось. Схема цикла при подобном подходе выглядит так:

	for (( начальное значение переменной ; условие окончания цикла; изменение переменной ))

На bash это можно написать так:

	for (( a = 1; a < 10; a++ ))

Цикл while

#!/usr/bin/env bash

var1=5
while [ $var1 > 0 ]; do
	echo $var1
	var1=$(($var1 - 1))
done

Детальная информация

Цикл while

Конструкция for — не единственный способ организации циклов в bash-скриптах. Здесь можно пользоваться и циклами while. В таком цикле можно задать команду проверки некоего условия и выполнять тело цикла до тех пор, пока проверяемое условие возвращает ноль, или сигнал успешного завершения некоей операции. Когда условие цикла вернёт ненулевое значение, что означает ошибку, цикл остановится.

Вот схема организации циклов while

	while команда проверки условия
	do
		другие команды
	done

На входе в цикл проверяется, больше ли нуля переменная $var1. Если это так, выполняется тело цикла, в котором из значения переменной вычитается единица. Так происходит в каждой итерации, при этом мы выводим в консоль значение переменной до его модификации. Как только $var1 примет значение 0, цикл прекращается.

Если не модифицировать переменную $var1, это приведёт к попаданию скрипта в бесконечный цикл.

Вложенные циклы

#!/usr/bin/env bash

for ((i = 0; i < 5; i++)); do
	echo "$i -"
	for ((j = 0; j < 5; j++)); do
		echo "- $j"
	done
done

Детальная информация

Вложенные циклы

В теле цикла можно использовать любые команды, в том числе — запускать другие циклы. Такие конструкции называют вложенными циклами:

Обработка содержимого файла

#!/usr/bin/env bash

for line in $(cat file_test.txt); do
	echo "$line"
	IFS=$' '
	for item in $line; do
		echo "-- $item"
	done
done

Детальная информация

Обработка содержимого файла

Чаще всего вложенные циклы используют для обработки файлов. Так, внешний цикл занимается перебором строк файла, а внутренний уже работает с каждой строкой. Вот, например, как выглядит обработка файла /etc/passwd:

В этом скрипте два цикла. Первый проходится по строкам, используя в качестве разделителя знак перевода строки. Внутренний занят разбором строк, поля которых разделены двоеточиями.

Такой подход можно использовать при обработке файлов формата CSV, или любых подобных файлов, записывая, по мере надобности, в переменную окружения IFS символ-разделитель.

Чтение параметров командной строки

#!/bin/bash
#!/bin/bash
#!/bin/bash

#!/usr/bin/env bash

echo $0
echo $1
echo $2
echo $3

total=$[ $1 + $2 ]
echo The first parameter is $1.
echo The second parameter is $2.
echo The sum is $total.

Детальная информация

Чтение параметров командной строки

Оболочка bash назначает специальным переменным, называемым позиционными параметрами, введённые при вызове скрипта параметры командной строки:

$0 — имя скрипта.
$1 — первый параметр.
$2 — второй параметр — и так далее, вплоть до переменной $9, в которую попадает девятый параметр.

Вот как можно использовать параметры командной строки в скрипте с помощью этих переменных:

#!/bin/bash
echo $0
echo $1
echo $2
echo $3

Запустим сценарий с параметрами:

Обратите внимание на то, что параметры командной строки разделяются пробелами.

Взглянем на ещё один пример использования параметров. Тут мы найдём сумму чисел, переданных сценарию:

#!/bin/bash
total=$[ $1 + $2 ]
echo The first parameter is $1.
echo The second parameter is $2.
echo The sum is $total.

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

#!/bin/bash
echo Hello $1, how do you do

Запустим его:

Что если параметр содержит пробелы, а нам надо обрабатывать его как самостоятельный фрагмент данных? Полагаем, если вы освоили предыдущие части этого руководства, ответ вы уже знаете. Заключается он в использовании кавычек.

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

Ввод паролей

#!/bin/bash

#!/usr/bin/env bash

read -s -p "Enter your password: " pass
echo "Is your password really $pass?"

Детальная информация

Ввод паролей

Иногда то, что вводит пользователь в ответ на вопрос скрипта, лучше на экране не показывать. Например, так обычно делают, запрашивая пароли. Ключ -s команды read предотвращает отображение на экране данных, вводимых с клавиатуры. На самом деле, данные выводятся, но команда read делает цвет текста таким же, как цвет фона.

#!/bin/bash
read -s -p "Enter your password: " pass
echo "Is your password really $pass? "

Чтение данных из файла

#!/bin/bash

#!/usr/bin/env bash

echo $'n'
echo 'Read from file test.txt'

cat test.txt | while read line; do
	echo $line
done

Детальная информация

Чтение данных из файла

Команда read может, при каждом вызове, читать одну строку текста из файла. Когда в файле больше не останется непрочитанных строк, она просто остановится. Если нужно получить в скрипте всё содержимое файла, можно, с помощью конвейера, передать результаты вызова команды cat для файла, конструкции while, которая содержит команду read (конечно, использование команды cat выглядит примитивно, но наша цель — показать всё максимально просто, ориентируясь на новичков; опытные пользователи, уверены, это поймут).

Напишем скрипт, в котором используется только что описанный подход к чтению файлов.

#!/bin/bash
count=1
cat myfile | while read line do
	echo "Line $count: $line"
	count=$(( $count + 1 ))
done

echo "Finished"

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

Проверка параметров, Подсчёт параметров

#!/bin/bash
#!/bin/bash
#!/bin/bash

#!/usr/bin/env bash

# Check paramerer
if [ -n "$1" ]; then
	echo "First parameter is ( $1 )"
	echo "The were ( $# ) parameters passed"
	echo "The last parameter is ( ${!#} )"
fi

Детальная информация

Проверка параметров, Подсчёт параметров

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

#!/bin/bash
if [ -n "$1" ] then
	echo Hello $1.
else
	echo "No parameters found. "
fi

В скрипте можно подсчитать количество переданных ему параметров. Оболочка bash предоставляет для этого специальную переменную. А именно, переменная $# содержит количество параметров, переданных сценарию при вызове.

Опробуем её:

#!/bin/bash
echo There were $# parameters passed.

Вызовем сценарий.

Эта переменная даёт необычный способ получения последнего из переданных скрипту параметров, не требующий знания их количества. Вот как это выглядит:

#!/bin/bash
echo The last parameter was ${!#}

Захват всех параметров командной строки

#!/bin/bash
#!/bin/bash

#!/usr/bin/env bash

if [ -n "$1" ]; then
	echo "echo param by a for"
	for item in "$*"; do
		echo "$item"
	done

	echo $'n'
	echo "echo param by a for"
	for item in "$@"; do
		echo "$item"
	done
fi

Детальная информация

Захват всех параметров командной строки

В некоторых случаях нужно захватить все параметры, переданные скрипту. Для этого можно воспользоваться переменными $* и $@. Обе они содержат все параметры командной строки, что делает возможным доступ к тому, что передано сценарию, без использования позиционных параметров.

Переменная $* содержит все параметры, введённые в командной строке, в виде единого «слова».

В переменной $@ параметры разбиты на отдельные «слова». Эти параметры можно перебирать в циклах.

Рассмотрим разницу между этими переменными на примерах. Сначала взглянем на их содержимое:

#!/bin/bash
echo "Using the $* method: $*"
echo "-----------"
echo "Using the $@ method: $@"

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

#!/bin/bash
count=1
for param in "$*" do
	echo "$* Parameter #$count = $param"
	count=$(( $count + 1 ))
done

count=1

for param in "$@"do
	echo "$@ Parameter #$count = $param"
	count=$(( $count + 1 ))
done

Переменная $* содержит все переданные скрипту параметры как единый фрагмент данных, в то время как в переменной $@ они представлены самостоятельными значениями. Какой именно переменной воспользоваться — зависит от того, что именно нужно в конкретном сценарии.

Команда shift

#!/bin/bash

#!/usr/bin/env bash

echo "echo param by a while"
while [ -n "$1" ]; do
	echo $1
	shift
done

Детальная информация

Команда shift

Использовать команду shift в bash-скриптах следует с осторожностью, так как она, в прямом смысле слова, сдвигает значения позиционных параметров.

Когда вы используете эту команду, она, по умолчанию, сдвигает значения позиционных параметров влево. Например, значение переменной $3 становится значением переменной $2, значение $2 переходит в $1, а то, что было до этого в $1, теряется. Обратите внимание на то, что при этом значение переменной $0, содержащей имя скрипта, не меняется.

Воспользовавшись командой shift, рассмотрим ещё один способ перебора переданных скрипту параметров:

#!/bin/bash
count=1
while [ -n "$1" ] do
	echo "Parameter #$count = $1"
	count=$(( $count + 1 ))
	shift
done

Скрипт задействует цикл while, проверяя длину значения первого параметра. Когда длина станет равна нулю, происходит выход из цикла. После проверки первого параметра и вывода его на экран, вызывается команда shift, которая сдвигает значения параметров на одну позицию.

Используя команду shift, помните о том, что при каждом её вызове значение переменной $1 безвозвратно теряется.

Ключи командной строки

#!/bin/bash

#!/usr/bin/env bash

echo "echo param by a while"
while [ -n "$1" ]; do
	case "$1" in
		-a) echo "case -a" ;;
		-r | -y) echo "case -r or -y" ;;
		*) echo "default" ;;
	esac
	shift
done

Детальная информация

Ключи командной строки

Ключи командной строки обычно выглядят как буквы, перед которыми ставится тире. Они служат для управления сценариями. Рассмотрим такой пример:

#!/bin/bash
echo
while [ -n «$1» ] do
case «$1» in
-a) echo «Found the -a option» ;;
-b) echo «Found the -b option» ;;
-c) echo «Found the -c option» ;;
*) echo «$1 is not an option» ;;
esac

done

Запустим скрипт:

$ ./myscript –a –b –c –d

В этом коде использована конструкция case, которая сверяет переданный ей ключ со списком обрабатываемых скриптом ключей. Если переданное значение нашлось в этом списке, выполняется соответствующая ветвь кода. Если при вызове скрипта будет использован любой ключ, обработка которого не предусмотрена, будет исполнена ветвь «*».

Как различать ключи и параметры

#!/bin/bash

#!/usr/bin/env bash

echo "echo param and kye by a while"
while [ -n "$1" ]; do
	case "$1" in
		-a) echo "Key => -a" ;;
		-b) echo "Key => -b" ;;
		--) shift 
		break ;;
		*) echo "$1 is not an option" ;;
	esac
	shift
done

Детальная информация

Как различать ключи и параметры

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

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

#!/bin/bash
while [ -n "$1" ] do
	case "$1" in
		-a) echo "Found the -a option" ;;
		-b) echo "Found the -b option";;
		-c) echo "Found the -c option" ;;
		--) shift break ;;
		*) echo "$1 is not an option";;
	esac

	shift
done

count=1

for param in $@ do
	echo "Parameter #$count: $param"
	count=$(( $count + 1 ))
done

Этот сценарий использует команду break для прерывания цикла while при обнаружении в строке двойного тире.

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

Обработка ключей со значениями

#!/bin/bash

#!/usr/bin/env bash

echo $'n'
echo "echo param and kye by a while"
while [ -n "$1" ]; do
	case "$1" in
		-a) echo "Key => -a" ;;
		-b) echo "Key => -b" ;;
		--) shift 
		break ;;
		*) echo "$1 is not an option" ;;
	esac
	shift
done

for parap in $@; do
	echo "Param => $parap"
done

Детальная информация

Обработка ключей со значениями

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

./myscript -a test1 -b -c test2

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

#!/bin/bash
while [ -n "$1" ] do
	case "$1" in
		-a) echo "Found the -a option";;
		-b) param="$2"
		echo "Found the -b option, with parameter value $param"
		shift ;;
		-c) echo "Found the -c option";;
		--) shift break ;;
		*) echo "$1 is not an option";;
	esac

	shift
done

count=1

for param in "$@" do
	echo "Parameter #$count: $param"
	count=$(( $count + 1 ))
done

Вызовем этот скрипт в таком виде:

./myscript -a -b test1 -d

В данном примере в конструкции case обрабатываются три ключа. Ключ -b требует наличия дополнительного параметра. Так как обрабатываемый ключ находится в переменной $1, соответствующий ему параметр будет находиться в $2 (тут используется команда shift, поэтому, по мере обработки, всё, что передано сценарию, сдвигается влево). Когда с этим мы разобрались, осталось лишь извлечь значение переменной $2 и у нас будет параметр нужного ключа. Конечно, тут понадобится ещё одна команда shift для того, чтобы следующий ключ попал в $1.

Использование стандартных ключей

Детальная информация

Использование стандартных ключей

При написании bash-скриптов вы можете выбирать любые буквы для ключей командной строки и произвольно задавать реакцию скрипта на эти ключи. Однако, в мире Linux значения некоторых ключей стали чем-то вроде стандарта, которого полезно придерживаться. Вот список этих ключей:

-a Вывести все объекты.
-c Произвести подсчёт.
-d Указать директорию.
-e Развернуть объект.
-f Указать файл, из которого нужно прочитать данные.
-h Вывести справку по команде.
-i Игнорировать регистр символов.
-l Выполнить полноформатный вывод данных.
-n Использовать неинтерактивный (пакетный) режим.
-o Позволяет указать файл, в который нужно перенаправить вывод.
-q Выполнить скрипт в quiet-режиме.
-r Обрабатывать папки и файлы рекурсивно.
-s Выполнить скрипт в silent-режиме.
-v Выполнить многословный вывод.
-x Исключить объект.
-y Ответить «yes» на все вопросы.

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

Получение данных от пользователя

#!/bin/bash
#!/bin/bash
#!/bin/bash
#!/bin/bash

#!/usr/bin/env bash

echo -n "Enter your name: "
read name
echo "Welcome $name"

echo $'n'
read -p "Enter your name: " first last
echo "Your data for $last $first"

echo $'n'
read -p "Enter your name: "
echo "Hello $REPLY, welcome to my program"

if read -t 5 -p "Enter your name: "; then
	echo "Hello $name, welcome to my script"
else
	echo "Sorry, to slow!"
fi

Детальная информация

Получение данных от пользователя

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

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

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

#!/bin/bash
echo -n "Enter your name: "
read name
echo "Hello $name, welcome to my program."

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

При вызове read можно указывать и несколько переменных:

#!/bin/bash
read -p "Enter your name: " first last
echo "Your data for $last, $first…"

Если, вызвав read, не указывать переменную, данные, введённые пользователем, будут помещены в специальную переменную среды REPLY:

#!/bin/bash
read -p "Enter your name: "
echo Hello $REPLY, welcome to my program.

Если скрипт должен продолжать выполнение независимо от того, введёт пользователь какие-то данные или нет, вызывая команду read можно воспользоваться ключом -t. А именно, параметр ключа задаёт время ожидания ввода в секундах:

#!/bin/bash
if read -t 5 -p "Enter your name: " name then
	echo "Hello $name, welcome to my script"
else
	echo "Sorry, too slow! "
fi

Если данные не будут введены в течение 5 секунд, скрипт выполнит ветвь условного оператора else, выведя извинения.

Стандартные дескрипторы файлов

Детальная информация

Стандартные дескрипторы файлов

Всё в Linux — это файлы, в том числе — ввод и вывод. Операционная система идентифицирует файлы с использованием дескрипторов.

Каждому процессу позволено иметь до девяти открытых дескрипторов файлов. Оболочка bash резервирует первые три дескриптора с идентификаторами 0, 1 и 2. Вот что они означают.

  • 0, STDIN — стандартный поток ввода.
  • 1, STDOUT — стандартный поток вывода.
  • 2, STDERR — стандартный поток ошибок.

Эти три специальных дескриптора обрабатывают ввод и вывод данных в сценарии.
Вам нужно как следует разобраться в стандартных потоках. Их можно сравнить с фундаментом, на котором строится взаимодействие скриптов с внешним миром. Рассмотрим подробности о них.

Создание собственного перенаправления вывода

#!/bin/bash

#!/usr/bin/env bash

exec 3>2_1.txt
echo "This should display on the screen"
echo "and this should be stored in the file" >&3
echo "And this should be back on the screen"

Детальная информация

Создание собственного перенаправления вывода

Перенаправляя ввод и вывод в сценариях, вы не ограничены тремя стандартными дескрипторами файлов. Как уже говорилось, можно иметь до девяти открытых дескрипторов. Остальные шесть, с номерами от 3 до 8, можно использовать для перенаправления ввода или вывода. Любой из них можно назначить файлу и использовать в коде скрипта.

Назначить дескриптор для вывода данных можно, используя команду exec:

#!/bin/bash
exec 3>myfile
echo "This should display on the screen"
echo "and this should be stored in the file" >&3
echo "And this should be back on the screen"

После запуска скрипта часть вывода попадёт на экран, часть — в файл с дескриптором 3

Создание дескрипторов файлов для ввода данных

#!/bin/bash

#!/usr/bin/env bash

exec 6<&0
exec 0< 3_1.txt
while read line; do
	echo $line
done

exec 0<&6
read -p "Are you don new?" answer
case "$answer" in
	y) echo "Goodbye" ;;
	n) echo "Sorry, this is the end";;
esac

Детальная информация

Создание дескрипторов файлов для ввода данных

Перенаправить ввод в скрипте можно точно так же, как и вывод. Сохраните STDIN в другом дескрипторе, прежде чем перенаправлять ввод данных.

После окончания чтения файла можно восстановить STDIN и пользоваться им как обычно:

#!/bin/bash
exec 6<&0
exec 0< myfile
count=1
while read line do
	echo "Line #$count: $line"
	count=$(( $count + 1 ))
done
exec 0<&6
read -p "Are you done now? " answer
case $answer in
	y) echo "Goodbye";;
	n) echo "Sorry, this is the end.";;
esac

В этом примере дескриптор файла 6 использовался для хранения ссылки на STDIN. Затем было сделано перенаправление ввода, источником данных для STDIN стал файл. После этого входные данные для команды read поступали из перенаправленного STDIN, то есть из файла.

После чтения файла мы возвращаем STDIN в исходное состояние, перенаправляя его в дескриптор 6. Теперь, для того, чтобы проверить, что всё работает правильно, скрипт задаёт пользователю вопрос, ожидает ввода с клавиатуры и обрабатывает то, что введено.

Закрытие дескрипторов файлов

#!/bin/bash

#!/usr/bin/env bash

exec 3> myfile
echo "This is a test line of data" >&3
exec 3>&-
echo "This won't work" >&3

Детальная информация

Закрытие дескрипторов файлов

Оболочка автоматически закрывает дескрипторы файлов после завершения работы скрипта. Однако, в некоторых случаях нужно закрывать дескрипторы вручную, до того, как скрипт закончит работу. Для того, чтобы закрыть дескриптор, его нужно перенаправить в &-. Выглядит это так:

#!/bin/bash
exec 3> myfile
echo "This is a test line of data" >&3
exec 3>&-
echo "This won't work" >&3

После исполнения скрипта мы получим сообщение об ошибке.

Всё дело в том, что мы попытались обратиться к несуществующему дескриптору.

Будьте внимательны, закрывая дескрипторы файлов в сценариях. Если вы отправляли данные в файл, потом закрыли дескриптор, потом — открыли снова, оболочка заменит существующий файл новым. То есть всё то, что было записано в этот файл ранее, будет утеряно.

Получение сведений об открытых дескрипторах

#!/bin/bash

Детальная информация

Получение сведений об открытых дескрипторах

Для того, чтобы получить список всех открытых в Linux дескрипторов, можно воспользоваться командой lsof. Во многих дистрибутивах, вроде Fedora, утилита lsof находится в /usr/sbin. Эта команда весьма полезна, так как она выводит сведения о каждом дескрипторе, открытом в системе. Сюда входит и то, что открыли процессы, выполняемые в фоне, и то, что открыто пользователями, вошедшими в систему.

У этой команды есть множество ключей, рассмотрим самые важные.

  • -p Позволяет указать ID процесса.
  • -d Позволяет указать номер дескриптора, о котором надо получить сведения.

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

Ключ -a используется для выполнения операции логического И над результатами, возвращёнными благодаря использованию двух других ключей:

Тип файлов, связанных с STDIN, STDOUT и STDERR — CHR (character mode, символьный режим). Так как все они указывают на терминал, имя файла соответствует имени устройства, назначенного терминалу. Все три стандартных файла доступны и для чтения, и для записи.

Посмотрим на вызов команды lsof из скрипта, в котором открыты, в дополнение к стандартным, другие дескрипторы:

#!/bin/bash
exec 3> myfile1
exec 6> myfile2
exec 7< myfile3
lsof -a -p $$ -d 0,1,2,3,6,7

Скрипт открыл два дескриптора для вывода (3 и 6) и один — для ввода (7). Тут же показаны и пути к файлам, использованных для настройки дескрипторов.

Подавление вывода

Детальная информация

Подавление вывода

Иногда надо сделать так, чтобы команды в скрипте, который, например, может исполняться как фоновый процесс, ничего не выводили на экран. Для этого можно перенаправить вывод в /dev/null. Это — что-то вроде «чёрной дыры».

Вот, например, как подавить вывод сообщений об ошибках:

ls -al badfile anotherfile 2> /dev/null

Тот же подход используется, если, например, надо очистить файл, не удаляя его:

STDIN

# 0, STDIN — стандартный поток ввода.

Детальная информация

STDIN

STDIN — это стандартный поток ввода оболочки. Для терминала стандартный ввод — это клавиатура. Когда в сценариях используют символ перенаправления ввода — <, Linux заменяет дескриптор файла стандартного ввода на тот, который указан в команде. Система читает файл и обрабатывает данные так, будто они введены с клавиатуры.

Многие команды bash принимают ввод из STDIN, если в командной строке не указан файл, из которого надо брать данные. Например, это справедливо для команды cat.

Когда вы вводите команду cat в командной строке, не задавая параметров, она принимает ввод из STDIN. После того, как вы вводите очередную строку, cat просто выводит её на экран.

STDOUT

#!/usr/bin/env bash

# 1, STDOUT — стандартный поток вывода.

pwd >> myfile

ls –l xfile > myfile

Детальная информация

STDOUT

STDOUT — стандартный поток вывода оболочки. По умолчанию это — экран. Большинство bash-команд выводят данные в STDOUT, что приводит к их появлению в консоли. Данные можно перенаправить в файл, присоединяя их к его содержимому, для этого служит команда >>.

Итак, у нас есть некий файл с данными, к которому мы можем добавить другие данные с помощью этой команды:

То, что выведет pwd, будет добавлено к файлу myfile, при этом уже имеющиеся в нём данные никуда не денутся.

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

После выполнения этой команды мы увидим сообщения об ошибках на экране.

При попытке обращения к несуществующему файлу генерируется ошибка, но оболочка не перенаправила сообщения об ошибках в файл, выведя их на экран. Но мы-то хотели, чтобы сообщения об ошибках попали в файл. Что делать? Ответ прост — воспользоваться третьим стандартным дескриптором.

STDERR

#!/usr/bin/env bash

# 2, STDERR — стандартный поток ошибок.

Детальная информация

STDERR

STDERR представляет собой стандартный поток ошибок оболочки. По умолчанию этот дескриптор указывает на то же самое, на что указывает STDOUT, именно поэтому при возникновении ошибки мы видим сообщение на экране.

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

Перенаправление потока ошибок

#!/usr/bin/env bash

ls -l xfile 2>myfile
cat ./myfile

Детальная информация

Перенаправление потока ошибок

Как вы уже знаете, дескриптор файла STDERR — 2. Мы можем перенаправить ошибки, разместив этот дескриптор перед командой перенаправления:

ls -l xfile 2>myfile
cat ./myfile

Сообщение об ошибке теперь попадёт в файл myfile.

Перенаправление потоков ошибок и вывода, Перенаправление вывода в скриптах

#!/usr/bin/env bash

ls –l myfile xfile anotherfile 2> errorcontent 1> correctcontent

Детальная информация

Перенаправление потоков ошибок и вывода, Перенаправление вывода в скриптах

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

ls –l myfile xfile anotherfile 2> errorcontent 1> correctcontent

Оболочка перенаправит то, что команда ls обычно отправляет в STDOUT, в файл correctcontent благодаря конструкции 1>. Сообщения об ошибках, которые попали бы в STDERR, оказываются в файле errorcontent из-за команды перенаправления 2>.

Если надо, и STDERR, и STDOUT можно перенаправить в один и тот же файл, воспользовавшись командой &>:

После выполнения команды то, что предназначено для STDERR и STDOUT, оказывается в файле content.

Существует два метода перенаправления вывода в сценариях командной строки:

  • Временное перенаправление, или перенаправление вывода одной строки.
  • Постоянное перенаправление, или перенаправление всего вывода в скрипте либо в какой-то его части.

Временное перенаправление вывода

#!/bin/bash

#!/usr/bin/env bash

#!/bin/bash
echo "This is an error" >&2
echo "This is normal output"

Детальная информация

Временное перенаправление вывода

В скрипте можно перенаправить вывод отдельной строки в STDERR. Для того, чтобы это сделать, достаточно использовать команду перенаправления, указав дескриптор STDERR, при этом перед номером дескриптора надо поставить символ амперсанда (&):

#!/bin/bash
echo "This is an error" >&2
echo "This is normal output"

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

Запустим скрипт так, чтобы вывод STDERR попадал в файл.

Как видно, теперь обычный вывод делается в консоль, а сообщения об ошибках попадают в файл.

Перенаправление ввода в скриптах

#!/bin/bash

#!/usr/bin/env bash

exec 1>1_2.txt
echo "This is a test of redirecting all output"

Детальная информация

Перенаправление ввода в скриптах

Для перенаправления ввода можно воспользоваться той же методикой, которую мы применяли для перенаправления вывода. Например, команда exec позволяет сделать источником данных для STDIN какой-нибудь файл:

Эта команда указывает оболочке на то, что источником вводимых данных должен стать файл myfile, а не обычный STDIN. Посмотрим на перенаправление ввода в действии:

#!/bin/bash
exec 0< testfile
count=1
while read line
do
echo "Line #$count: $line"
count=$(( $count + 1 ))
done

В одном из предыдущих материалов вы узнали о том, как использовать команду read для чтения данных, вводимых пользователем с клавиатуры. Если перенаправить ввод, сделав источником данных файл, то команда read, при попытке прочитать данные из STDIN, будет читать их из файла, а не с клавиатуры.

Некоторые администраторы Linux используют этот подход для чтения и последующей обработки лог-файлов.

Постоянное перенаправление вывода

#!/bin/bash
#!/bin/bash

#!/usr/bin/env bash

exec 1>1_1.txt
echo "This is a test of redirecting all output"
echo "from a shell script to another file."
echo "without having to redirect every line"

exec 2>myerror
echo "This is the start of the script"
echo "now redirecting all output to another location"
exec 1>myfile
echo "This should go to the myfile file"
echo "and this should go to the myerror file" >&2

Детальная информация

Постоянное перенаправление вывода

Если в скрипте нужно перенаправлять много выводимых на экран данных, добавлять соответствующую команду к каждому вызову echo неудобно. Вместо этого можно задать перенаправление вывода в определённый дескриптор на время выполнения скрипта, воспользовавшись командой exec:

#!/bin/bash
exec 1>outfile
echo "This is a test of redirecting all output"
echo "from a shell script to another file."
echo "without having to redirect every line"

Если просмотреть файл, указанный в команде перенаправления вывода, окажется, что всё, что выводилось командами echo, попало в этот файл.

Команду exec можно использовать не только в начале скрипта, но и в других местах:

#!/bin/bash
exec 2>myerror
echo "This is the start of the script"
echo "now redirecting all output to another location"
exec 1>myfile
echo "This should go to the myfile file"
echo "and this should go to the myerror file" >&2

Вот что получится после запуска скрипта и просмотра файлов, в которые мы перенаправляли вывод.

Сначала команда exec задаёт перенаправление вывода из STDERR в файл myerror. Затем вывод нескольких команд echo отправляется в STDOUT и выводится на экран. После этого команда exec задаёт отправку того, что попадает в STDOUT, в файл myfile, и, наконец, мы пользуемся командой перенаправления в STDERR в команде echo, что приводит к записи соответствующей строки в файл myerror.

Освоив это, вы сможете перенаправлять вывод туда, куда нужно. Теперь поговорим о перенаправлении ввода.

Объявление функций

#!/usr/bin/env bash

# Функцию можно объявить так:

# functionName {
# }

# Или так:

# functionName() {
# }

# Функцию можно вызвать без аргументов и с аргументами.

Детальная информация

Объявление функций

Использование команды return

#!/usr/bin/env bash

function functionTest {
	echo "$1" # arguments are accessible through FunctionTest, echo "$1" # arguments are accessible through FunctionTest, ,...,...
	echo "$2" # arguments are accessible through FunctionTest, echo "$1" # arguments are accessible through FunctionTest, ,...,...
	echo '1 + 1'
	return $[1 + 1]
}

functionTest 1 2
echo "return value from functionTest is => $?"

Детальная информация

Использование команды return

Команда return позволяет задавать возвращаемый функцией целочисленный код завершения. Есть два способа работы с тем, что является результатом вызова функции.

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

Функции могут использовать стандартные позиционные параметры, в которые записывается то, что передаётся им при вызове. Например, имя функции хранится в параметре $0, первый переданный ей аргумент — в $1, второй — в $2, и так далее. Количество переданных функции аргументов можно узнать, обратившись к переменной $#.

Запись вывода функции в переменную

#!/usr/bin/env bash

function test {
	echo $[10 + 9]
}
result=$(test)
echo $result

Детальная информация

Запись вывода функции в переменную

Ещё один способ возврата результатов работы функции заключается в записи данных, выводимых функцией, в переменную. Такой подход позволяет обойти ограничения команды return и возвращать из функции любые данные

Глобальные переменные. Локальные переменные

#!/usr/bin/env bash

# Глобальные переменные
function concatFn {
	text="$text XXX"
}

read -p "Enter text: " text
concatFn
echo $text

# Локальные переменные
function concatFn {
	local t="$1 XXX"
	echo $t
}

read -p "Enter text: " text
r=$(concatFn $text)
echo $r

Детальная информация

Глобальные переменные. Локальные переменные

Глобальные переменные — это переменные, которые видны из любого места bash-скрипта. Если вы объявили глобальную переменную в основном коде скрипта, к такой переменной можно обратиться из функции.

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

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

Переменные, которые объявляют и используют внутри функции, могут быть объявлены локальными. Для того, чтобы это сделать, используется ключевое слово local перед именем переменной:

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

Передача функциям массивов в качестве аргументов

#!/bin/bash

#!/usr/bin/env bash

function text {
	echo "Parameter $@"
	local arr=$@
	echo ${arr[*]}
}
myArr=(1 2 3 4 5)
echo "The original array is: ${myArr[*]}"
text ${myArr[*]}

Детальная информация

Передача функциям массивов в качестве аргументов

Попробуем передать функции в качестве аргумента массив. Сразу хочется сказать, что работать такая конструкция будет неправильно:

#!/bin/bash
function myfunc {
	echo "The parameters are: $@"
	arr=$1
	echo "The received array is ${arr[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
myfunc $myarray

Как видно из примера, при передаче функции массива, она получит доступ лишь к его первому элементу.

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

Создание и использование библиотек

myfunc.sh

#!/usr/bin/env bash

Создание и использование библиотек

#!/usr/bin/env bash
. myfunc.sh

# Создание и использование библиотек
echo $(text)

Детальная информация

Создание и использование библиотек

Итак, теперь вы знаете, как писать функции и как вызывать их в том же скрипте, где они объявлены. Что если надо использовать функцию, тот блок кода, который она собой представляет, в другом скрипте, не используя копирование и вставку?

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

Ключ к использованию библиотек — в команде source. Эта команда используется для подключения библиотек к скриптам. В результате функции, объявленные в библиотеке, становятся доступными в скрипте, в противном же случае функции из библиотек не будут доступны в области видимости других скриптов.

У команды source есть псевдоним — оператор «точка». Для того, чтобы подключить файл в скрипте, в скрипт надо добавить конструкцию такого вида:

# myfunc.sh
#!/usr/bin/env bash

# Создание и использование библиотек
function text {
	echo $[1 + 2]
}

Основы работы с sed

#!/usr/bin/env bash

echo "This is a text" | sed 's/text/nothing/'

sed 's/Text/Welcome/' ./myfile.txt

Детальная информация

Основы работы с sed

Утилиту sed называют потоковым текстовым редактором. В интерактивных текстовых редакторах, наподобие nano, с текстами работают, используя клавиатуру, редактируя файлы, добавляя, удаляя или изменяя тексты. Sed позволяет редактировать потоки данных, основываясь на заданных разработчиком наборах правил. Вот как выглядит схема вызова этой команды:

По умолчанию sed применяет указанные при вызове правила, выраженные в виде набора команд, к STDIN. Это позволяет передавать данные непосредственно sed.

В данном случае sed заменяет слово text в строке, переданной для обработки, словом «nothing». Для оформления правила обработки текста, заключённого в кавычки, используются прямые слэши. В нашем случае применена команда вида s/pattern1/pattern2/. Буква «s» — это сокращение слова «substitute», то есть — перед нами команда замены. Sed, выполняя эту команду, просмотрит переданный текст и заменит найденные в нём фрагменты (о том — какие именно, поговорим ниже), соответствующие pattern1, на pattern2.

Выше приведён примитивный пример использования sed, нужный для того, чтобы ввести вас в курс дела. На самом деле, sed можно применять в гораздо более сложных сценариях обработки текстов, например — для работы с файлами.

Здесь применён тот же подход, который мы использовали выше, но теперь sed обрабатывает текст, хранящийся в файле. При этом, если файл достаточно велик, можно заметить, что sed обрабатывает данные порциями и выводит то, что обработано, на экран, не дожидаясь обработки всего файла.

Sed не меняет данные в обрабатываемом файле. Редактор читает файл, обрабатывает прочитанное, и отправляет то, что получилось, в STDOUT. Для того, чтобы убедиться в том, что исходный файл не изменился, достаточно, после того, как он был передан sed, открыть его. При необходимости вывод sed можно перенаправить в файл, возможно — перезаписать старый файл. Если вы знакомы с одним из предыдущих материалов этой серии, где речь идёт о перенаправлении потоков ввода и вывода, вы вполне сможете это сделать.

Замена символов

#!/usr/bin/env bash

sed 'y/008/___/' myfile.txt

Детальная информация

Замена символов

Команда y работает с отдельными символами, заменяя их в соответствии с переданными ей при вызове данными

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

Вывод номеров строк

#!/usr/bin/env bash

sed -n '/Text/=' myfile.txt

Детальная информация

Вывод номеров строк

Если вызвать sed, использовав команду =, утилита выведет номера строк в потоке данных:

Потоковый редактор вывел номера строк перед их содержимым.

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

Чтение данных для вставки из файла

#!/usr/bin/env bash

sed '3r myfile_2.txt' myfile.txt
echo -e 'n'
sed '/Text 008/r myfile_2.txt' myfile.txt

Детальная информация

Чтение данных для вставки из файла

Выше мы рассматривали приёмы вставки данных в поток, указывая то, что надо вставить, прямо при вызове sed. В качестве источника данных можно воспользоваться и файлом. Для этого служит команда r, которая позволяет вставлять в поток данные из указанного файла. При её вызове можно указать номер строки, после которой надо вставить содержимое файла, или шаблон.

Тут содержимое файла newfile было вставлено после третьей строки файла myfile.

Вот что произойдёт, если применить при вызове команды r шаблон

Пример

#!/usr/bin/env bash

sed '/DATA/ {
	r DATA.txt
	d
}' oldfile.txt

Детальная информация

Пример

Представим себе такую задачу. Есть файл, в котором имеется некая последовательность символов, сама по себе бессмысленная, которую надо заменить на данные, взятые из другого файла. А именно, пусть это будет файл newfile, в котором роль указателя места заполнения играет последовательность символов DATA. Данные, которые нужно подставить вместо DATA, хранятся в файле data.

Решить эту задачу можно, воспользовавшись командами r и d потокового редактора sed

Выполнение наборов команд при вызове sed

#!/usr/bin/env bash

sed -e '
s/Text/Welcome/;
s/Welcome/another test/' ./myfile.txt

Детальная информация

Выполнение наборов команд при вызове sed

Для выполнения нескольких действий с данными, используйте ключ -e при вызове sed. Например, вот как организовать замену двух фрагментов текста:

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

Для ввода нескольких шаблонов обработки текста при вызове sed, можно, после ввода первой одиночной кавычки, нажать Enter, после чего вводить каждое правило с новой строки, не забыв о закрывающей кавычке:

Чтение команд из файла

#!/usr/bin/env bash

sed -f mycommands.sh myfile.txt

Детальная информация

Чтение команд из файла

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

Флаги команды замены

#!/usr/bin/env bash

sed 's/Text/another test/' myfile.txt

Детальная информация

Флаги команды замены

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

Схема записи команды замены при использовании флагов выглядит так:

s/pattern/replacement/flags

Выполнение этой команды можно модифицировать несколькими способами.

  • При передаче номера учитывается порядковый номер вхождения шаблона в строку, заменено будет именно это вхождение.
  • Флаг g указывает на то, что нужно обработать все вхождения шаблона, имеющиеся в строке.
  • Флаг p указывает на то, что нужно вывести содержимое исходной строки.
  • Флаг вида w file указывает команде на то, что нужно записать результаты обработки текста в файл.

Рассмотрим использование первого варианта команды замены, с указанием позиции заменяемого вхождения искомого фрагмента:

sed 's/test/another test/2' myfile

Тут мы указали, в качестве флага замены, число 2. Это привело к тому, что было заменено лишь второе вхождение искомого шаблона в каждой строке. Теперь опробуем флаг глобальной замены — g

sed 's/test/another test/g' myfile

Флаг команды замены p позволяет выводить строки, в которых найдены совпадения, при этом ключ -n, указанный при вызове sed, подавляет обычный вывод:

sed -n 's/test/another test/p' myfile

Как результат, при запуске sed в такой конфигурации на экран выводятся лишь строки (в нашем случае — одна строка), в которых найден заданный фрагмент текста.

Воспользуемся флагом w, который позволяет сохранить результаты обработки текста в файл

sed 's/test/another test/w output' myfile

Хорошо видно, что в ходе работы команды данные выводятся в STDOUT, при этом обработанные строки записываются в файл, имя которого указано после w.

Символы-разделители

#!/usr/bin/env bash

echo "/bin/" | sed 's//bin//csh/'
echo "/bin/" | sed 's!/bin/!csh!'

Детальная информация

Символы-разделители

Представьте, что нужно заменить /bin/bash на /bin/csh в файле /etc/passwd. Задача не такая уж и сложная:

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

К счастью, sed позволяет нам самостоятельно задавать символы-разделители для использования их в команде замены. Разделителем считается первый символ, который будет встречен после s:

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

Выбор фрагментов текста для обработки

#!/usr/bin/env bash

sed '2s/Text/Welcome/' myfile.txt

sed '2,3s/Text/Welcome/' myfile.txt

sed '2,$s/Text/Welcome/' myfile.txt

sed '/TextT/s/Text/Welcome/' myfile.txt

Детальная информация

Выбор фрагментов текста для обработки

До сих пор мы вызывали sed для обработки всего переданного редактору потока данных. В некоторых случаях с помощью sed надо обработать лишь какую-то часть текста — некую конкретную строку или группу строк. Для достижения такой цели можно воспользоваться двумя подходами:

  • Задать ограничение на номера обрабатываемых строк.
  • Указать фильтр, соответствующие которому строки нужно обработать.

Рассмотрим первый подход. Тут допустимо два варианта. Первый, рассмотренный ниже, предусматривает указание номера одной строки, которую нужно обработать:

Второй вариант — диапазон строк:

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

Удаление строк

#!/usr/bin/env bash

sed '2d' myfile.txt

sed '1,2d' myfile.txt

Детальная информация

Удаление строк

Утилита sed годится не только для замены одних последовательностей символов в строках на другие. С её помощью, а именно, используя команду d, можно удалять строки из текстового потока.

Мы хотим, чтобы из текста была удалена третья строка. Обратите внимание на то, что речь не идёт о файле. Файл останется неизменным, удаление отразится лишь на выводе, который сформирует sed.

Если при вызове команды d не указать номер удаляемой строки, удалены будут все строки потока.

Вот как применить команду d к диапазону строк

Строки можно удалять и по шаблону

При вызове d можно указывать пару шаблонов — будут удалены строки, в которых встретится шаблон, и те строки, которые находятся между ними:

sed '/second/,/fourth/d' myfile

Вставка текста в поток

#!/usr/bin/env bash

echo "Test" | sed 'iFirst'
echo "Test" | sed 'aLast'
echo $'n'
sed '2i***' myfile.txt
echo $'n'
sed '2a***' myfile.txt

Детальная информация

Вставка текста в поток

С помощью sed можно вставлять данные в текстовый поток, используя команды i и a:

  • Команда i добавляет новую строку перед заданной.
  • Команда a добавляет новую строку после заданной.

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

Замена строк

#!/usr/bin/env bash

sed '2c***' myfile.txt
echo -e 'n---'

sed '/Text 008/c ***' myfile.txt

Детальная информация

Замена строк

Команда c позволяет изменить содержимое целой строки текста в потоке данных. При её вызове нужно указать номер строки, вместо которой в поток надо добавить новые данные:

Особенности вызова awk

Детальная информация

Особенности вызова awk

Схема вызова awk выглядит так:

$ awk options program file

Awk воспринимает поступающие к нему данные в виде набора записей. Записи представляют собой наборы полей. Упрощенно, если не учитывать возможности настройки awk и говорить о некоем вполне обычном тексте, строки которого разделены символами перевода строки, запись — это строка. Поле — это слово в строке.

Рассмотрим наиболее часто используемые ключи командной строки awk:

-F fs — позволяет указать символ-разделитель для полей в записи.
-f file — указывает имя файла, из которого нужно прочесть awk-скрипт.
-v var=value — позволяет объявить переменную и задать её значение по умолчанию, которое будет использовать `awk`.
-mf N — задаёт максимальное число полей для обработки в файле данных.
-mr N — задаёт максимальный размер записи в файле данных.
-W keyword — позволяет задать режим совместимости или уровень выдачи предупреждений `awk`.

Настоящая мощь awk скрывается в той части команды его вызова, которая помечена выше как program. Она указывает на файл awk-скрипта, написанный программистом и предназначенный для чтения данных, их обработки и вывода результатов.

Чтение awk-скриптов из командной строки

#!/usr/bin/env bash

awk '{print "Welcome to awk command tutorial"}'

Детальная информация

Чтение awk-скриптов из командной строки

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

$ awk '{print "Welcome to awk command tutorial"}'

Запустим эту команду… И ничего не произойдёт Дело тут в том, что мы, при вызове awk, не указали файл с данными. В подобной ситуации awk ожидает поступления данных из STDIN. Поэтому выполнение такой команды не приводит к немедленно наблюдаемым эффектам, но это не значит, что awk не работает — он ждёт входных данных из STDIN.

Если теперь ввести что-нибудь в консоль и нажать Enter, awk обработает введённые данные с помощью скрипта, заданного при его запуске. Awk обрабатывает текст из потока ввода построчно, этим он похож на sed. В нашем случае awk ничего не делает с данными, он лишь, в ответ на каждую новую полученную им строку, выводит на экран текст, заданный в команде print.

Что бы мы ни ввели, результат в данном случае будет одним и тем же — вывод текста.
Для того, чтобы завершить работу awk, нужно передать ему символ конца файла (EOF, End-of-File). Сделать это можно, воспользовавшись сочетанием клавиш CTRL + D.

Неудивительно, если этот первый пример показался вам не особо впечатляющим. Однако, самое интересное — впереди.

Условный оператор

#!/usr/bin/env bash

awk '{if ($1 > 20) print $0}' testfile

echo $'n'

awk '{
    if ($1 > 20){
        x = $1 * 2
        print $1,$3
        print $2,x
    }}' testfile

Детальная информация

Условный оператор

Awk поддерживает стандартный во многих языках программирования формат условного оператора if-then-else. Однострочный вариант оператора представляет собой ключевое слово if, за которым, в скобках, записывают проверяемое выражение, а затем — команду, которую нужно выполнить, если выражение истинно.

Например, есть такой файл с именем testfile:

Напишем скрипт, который выводит числа из этого файла, большие 20:

$ awk '{if ($1 > 20) print $1}' testfile

Если нужно выполнить в блоке if несколько операторов, их нужно заключить в фигурные скобки:

$ awk '{
    if ($1 > 20)
    {
        x = $1 * 2
        print x
    }
}' testfile

Как уже было сказано, условный оператор awk может содержать блок else:

$ awk '{
    if ($1 > 20)
    {
        x = $1 * 2
        print x
    } else {
        x = $1 / 2
        print x
    }}' testfile

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

$ awk '{if ($1 > 20) print $1 * 2; else print $1 / 2}' testfile

Цикл while

#!/usr/bin/env bash

awk '{
    total = 0
    i = 1
    while (i < 4){
        total += $i
        i++
    }
    avg = total / 3
    print "Average:", avg
}' myfile_4

Детальная информация

Цикл while

Цикл while позволяет перебирать наборы данных, проверяя условие, которое остановит цикл.

Вот файл myfile, обработку которого мы хотим организовать с помощью цикла:

124 127 130
112 142 135
175 158 245

Напишем такой скрипт:

$ awk '{
    total = 0
    i = 1
    while (i < 4)
    {
        total += $i
        i++
    }
    avg = total / 3
    print "Average:",avg
}' testfile

Цикл while перебирает поля каждой записи, накапливая их сумму в переменной total и увеличивая в каждой итерации на 1 переменную-счётчик i. Когда i достигнет 4, условие на входе в цикл окажется ложным и цикл завершится, после чего будут выполнены остальные команды — подсчёт среднего значения для числовых полей текущей записи и вывод найденного значения.

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

Вот как работает команда break:

$ awk '{
    total = 0
    i = 1
    while (i < 4)
    {
        total += $i
        if (i == 2)
        break
        i++
    }
    avg = total / 2
    print "The average of the first two elements is:",avg
}' testfile

Цикл for

#!/usr/bin/env bash

awk '{
    total = 0
    for (i = 1; i < 4; i++) {
        total += $i
    }
    avg = total / 3
    print "Average:",avg
}' myfile_4

Детальная информация

Цикл for

Циклы for используются во множестве языков программировании. Поддерживает их и awk. Решим задачу расчёта среднего значения числовых полей с использованием такого цикла:

$ awk '{
    total = 0
    for (i = 1; i < 4; i++)
    {
        total += $i
    }
    avg = total / 3
    print "Average:",avg
}' testfile

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

Форматированный вывод данных

#!/usr/bin/env bash

awk 'BEGIN{
    x = 100 * 100
    printf "The result is: %en", x
}'

Детальная информация

Форматированный вывод данных

Команда printf в awk позволяет выводить форматированные данные. Она даёт возможность настраивать внешний вид выводимых данных благодаря использованию шаблонов, в которых могут содержаться текстовые данные и спецификаторы форматирования.

Спецификатор форматирования — это специальный символ, который задаёт тип выводимых данных и то, как именно их нужно выводить. Awk использует спецификаторы форматирования как указатели мест вставки данных из переменных, передаваемых printf.

Первый спецификатор соответствует первой переменной, второй спецификатор — второй, и так далее.

Спецификаторы форматирования записывают в таком виде:

%[modifier]control-letter

Вот некоторые из них:

c — воспринимает переданное ему число как код ASCII-символа и выводит этот символ.
d — выводит десятичное целое число.
i — то же самое, что и d.
e — выводит число в экспоненциальной форме.
f — выводит число с плавающей запятой.
g — выводит число либо в экспоненциальной записи, либо в формате с плавающей запятой, в зависимости от того, как получается короче.
o — выводит восьмеричное представление числа.
s — выводит текстовую строку.

Вот как форматировать выводимые данные с помощью printf:

$ awk 'BEGIN{
    x = 100 * 100
    printf "The result is: %en", x
}'

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

Встроенные математические функции

#!/usr/bin/env bash

awk 'BEGIN{x=exp(5); print x}'

Детальная информация

Встроенные математические функции

При работе с awk программисту доступны встроенные функции. В частности, это математические и строковые функции, функции для работы со временем. Вот, например, список математических функций, которыми можно пользоваться при разработке awk-скриптов:

cos(x) — косинус x (x выражено в радианах).
sin(x) — синус x.
exp(x) — экспоненциальная функция.
int(x) — возвращает целую часть аргумента.
log(x) — натуральный логарифм.
rand() — возвращает случайное число с плавающей запятой в диапазоне 0 — 1.
sqrt(x) — квадратный корень из x.

Вот как пользоваться этими функциями:

$ awk 'BEGIN{x=exp(5); print x}'

Строковые функции

#!/usr/bin/env bash

awk 'BEGIN{x = "likegeeks"; print toupper(x)}'

Детальная информация

Строковые функции

Awk поддерживает множество строковых функций. Все они устроены более или менее одинаково. Вот, например, функция toupper:

$ awk 'BEGIN{x = "likegeeks"; print toupper(x)}'

Эта функция преобразует символы, хранящиеся в переданной ей строковой переменной, к верхнему регистру.

Пользовательские функции

#!/usr/bin/env bash

awk '
function myprint(){
    print $1,$2
}
BEGIN{FS="."}
{myprint()}' myfile_2

Детальная информация

Пользовательские функции

При необходимости вы можете создавать собственные функции awk. Такие функции можно использовать так же, как встроенные:

$ awk '
    function myprint() {
        printf "The user %s has home path at %sn", $1,$6
    }
    BEGIN{FS=":"}
    {
        myprint()
    }' /etc/passwd

В примере используется заданная нами функция myprint, которая выводит данные.

Позиционные переменные, хранящие данные полей

#!/usr/bin/env bash

awk '{print $1}' myfile
awk -F: '{print $1}' myfile

Детальная информация

Позиционные переменные, хранящие данные полей

Одна из основных функций awk заключается в возможности манипулировать данными в текстовых файлах. Делается это путём автоматического назначения переменной каждому элементу в строке. По умолчанию awk назначает следующие переменные каждому полю данных, обнаруженному им в записи:

* $0 — представляет всю строку текста (запись).
* $1 — первое поле.
* $2 — второе поле.
* $n — n-ное поле.

Поля выделяются из текста с использованием символа-разделителя. По умолчанию — это пробельные символы вроде пробела или символа табуляции.

Рассмотрим использование этих переменных на простом примере. А именно, обработаем файл, в котором содержится несколько строк (этот файл показан на рисунке ниже) с помощью такой команды:

$ awk '{print $1}' myfile

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

Иногда в некоторых файлах в качестве разделителей полей используется что-то, отличающееся от пробелов или символов табуляции. Выше мы упоминали ключ awk -F, который позволяет задать необходимый для обработки конкретного файла разделитель:

$ awk -F: '{print $1}' /etc/passwd

Эта команда выводит первые элементы строк, содержащихся в файле /etc/passwd. Так как в этом файле в качестве разделителей используются двоеточия, именно этот символ был передан awk после ключа -F.

Использование нескольких команд

#!/usr/bin/env bash

echo "My name is Tom" | awk '{$4="Adam"; print $0}'

Детальная информация

Использование нескольких команд

Вызов awk с одной командой обработки текста — подход очень ограниченный. Awk позволяет обрабатывать данные с использованием многострочных скриптов. Для того, чтобы передать awk многострочную команду при вызове его из консоли, нужно разделить её части точкой с запятой:

$ echo "My name is Tom" | awk '{$4="Adam"; print $0}'

В данном примере первая команда записывает новое значение в переменную $4, а вторая выводит на экран всю строку.

Чтение скрипта awk из файла

#!/usr/bin/env bash

awk -F: -f mycommand myfile

Детальная информация

Чтение скрипта awk из файла

Awk позволяет хранить скрипты в файлах и ссылаться на них, используя ключ -f. Подготовим файл testfile, в который запишем следующее:

{print $1 " has a  home directory at " $6}

Вызовем awk, указав этот файл в качестве источника команд:

$ awk -F: -f testfile /etc/passwd

Тут мы выводим из файла /etc/passwd имена пользователей, которые попадают в переменную $1, и их домашние директории, которые попадают в $6. Обратите внимание на то, что файл скрипта задают с помощью ключа -f, а разделитель полей, двоеточие в нашем случае, с помощью ключа -F.

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

{
    text = " has a  home directory at "
    print $1 text $6
}

Тут мы храним текст, используемый при выводе данных, полученных из каждой строки обрабатываемого файла, в переменной, и используем эту переменную в команде print. Если воспроизвести предыдущий пример, записав этот код в файл testfile, выведено будет то же самое.

Выполнение команд до начала обработки данных

#!/usr/bin/env bash

awk 'BEGIN {print "Hello Worldn---"}
{print $0}' myfile

Детальная информация

Выполнение команд до начала обработки данных

Иногда нужно выполнить какие-то действия до того, как скрипт начнёт обработку записей из входного потока. Например — создать шапку отчёта или что-то подобное.

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

$ awk 'BEGIN {print "Hello World!"}'

А вот — немного более сложный пример:

$ awk 'BEGIN {print "The File Contents:"}
{print $0}' myfile

Сначала awk исполняет блок BEGIN, после чего выполняется обработка данных. Будьте внимательны с одинарными кавычками, используя подобные конструкции в командной строке. Обратите внимание на то, что и блок BEGIN, и команды обработки потока, являются в представлении awk одной строкой. Первая одинарная кавычка, ограничивающая эту строку, стоит перед BEGIN. Вторая — после закрывающей фигурной скобки команды обработки данных.

Выполнение команд после окончания обработки данных

#!/usr/bin/env bash

awk '
BEGIN {print "HEAD content"}
{print $0}
END {print "END of file"}
' myfile

echo $'n'

awk '
BEGIN {
    print "Начало отчёта"
    print "--- t --- tt ---"
    print "Номер t Наименование t Должность"
    print "--- t --- tt ---"
    FS="."
}
{
    print $1 "t" $2 "tt" $3
}
END {
    print "--- t --- tt ---"
    print "Конец"
}' myfile_2

Детальная информация

Выполнение команд после окончания обработки данных

Ключевое слово END позволяет задавать команды, которые надо выполнить после окончания обработки данных:

$ awk 'BEGIN {print "The File Contents:"}
{print $0}
END {print "End of File"}' myfile

После завершения вывода содержимого файла, awk выполняет команды блока END. Это полезная возможность, с её помощью, например, можно сформировать подвал отчёта. Теперь напишем скрипт следующего содержания и сохраним его в файле myscript:

BEGIN {
    print "The latest list of users and shells"
    print " UserName t HomePath"
    print "-------- t -------"
    FS=":"
}
{
    print $1 " t " $6
}
END {
    print "The end"
}

Тут, в блоке BEGIN, создаётся заголовок табличного отчёта. В этом же разделе мы указываем символ-разделитель. После окончания обработки файла, благодаря блоку END, система сообщит нам о том, что работа окончена.

Запустим скрипт:

$ awk -f myscript  /etc/passwd

Всё, о чём мы говорили выше — лишь малая часть возможностей awk. Продолжим освоение этого полезного инструмента.

Встроенные переменные: настройка процесса обработки данных

#!/usr/bin/env bash

awk 'BEGIN{FS="."; OFS="-"} {print $1,$2,$3}' myfile_2

echo $'n'

awk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' myfile_3

echo $'n'

awk 'BEGIN{FS="n"; R=""} {print $1,$3}' address

Детальная информация

Встроенные переменные: настройка процесса обработки данных

Утилита awk использует встроенные переменные, которые позволяют настраивать процесс обработки данных и дают доступ как к обрабатываемым данным, так и к некоторым сведениям о них.

Мы уже рассматривали позиционные переменные — $1, $2, $3, которые позволяют извлекать значения полей, работали мы и с некоторыми другими переменными. На самом деле, их довольно много. Вот некоторые из наиболее часто используемых:

* FIELDWIDTHS — разделённый пробелами список чисел, определяющий точную ширину каждого поля данных с учётом разделителей полей.
* FS — уже знакомая вам переменная, позволяющая задавать символ-разделитель полей.
* RS — переменная, которая позволяет задавать символ-разделитель записей.
* OFS — разделитель полей на выводе awk-скрипта.
* ORS — разделитель записей на выводе awk-скрипта.

По умолчанию переменная OFS настроена на использование пробела. Её можно установить так, как нужно для целей вывода данных:

$ awk 'BEGIN{FS=":"; OFS="-"} {print $1,$6,$7}' /etc/passwd

Переменная FIELDWIDTHS позволяет читать записи без использования символа-разделителя полей.

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

При установленной переменной FIELDWIDTHS awk будет игнорировать переменную FS и находить поля данных в соответствии со сведениями об их ширине, заданными в FIELDWIDTHS.

Предположим, имеется файл testfile, содержащий такие данные:

1235.9652147.91
927-8.365217.27
36257.8157492.5

Известно, что внутренняя организация этих данных соответствует шаблону 3-5-2-5, то есть, первое поле имеет ширину 3 символа, второе — 5, и так далее. Вот скрипт, который позволит разобрать такие записи:

$ awk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' testfile

Посмотрим на то, что выведет скрипт. Данные разобраны с учётом значения переменной FIELDWIDTHS, в результате числа и другие символы в строках разбиты в соответствии с заданной шириной полей.

Переменные RS и ORS задают порядок обработки записей. По умолчанию RS и ORS установлены на символ перевода строки. Это означает, что awk воспринимает каждую новую строку текста как новую запись и выводит каждую запись с новой строки.

Иногда случается так, что поля в потоке данных распределены по нескольким строкам. Например, пусть имеется такой файл с именем addresses:

Person Name
123 High Street
(222) 466-1234

Another person
487 High Street
(523) 643-8754

Если попытаться прочесть эти данные при условии, что FS и RS установлены в значения по умолчанию, awk сочтёт каждую новую строку отдельной записью и выделит поля, опираясь на пробелы. Это не то, что нам в данном случае нужно.

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

Кроме того, в данном примере понадобится записать в переменную RS пустую строку. Обратите внимание на то, что в файле блоки данных о разных людях разделены пустой строкой. В результате awk будет считать пустые строки разделителями записей. Вот как всё это сделать:

$ awk 'BEGIN{FS="n"; RS=""} {print $1,$3}' addresses

Как видите, awk, благодаря таким настройкам переменных, воспринимает строки из файла как поля, а разделителями записей становятся пустые строки.

Встроенные переменные: сведения о данных и об окружении

#!/usr/bin/env bash

awk 'BEGIN{print ARGC,ARGV[1]}' myfile

echo $'n'

awk '
BEGIN{
    print ENVIRON["HOME"]
    print ENVIRON["PATH"]
}'

echo $'n'

awk 'BEGIN{FS="."; OFS=":"} {print $1,$NF}' myfile_2

echo $'n'

awk '
BEGIN{FS="."}
{print $1, "FNR="FNR, "NR="NR}
END{print "There were",NR,"record processed"}' myfile_2 myfile_2

Детальная информация

Встроенные переменные: сведения о данных и об окружении

Помимо встроенных переменных, о которых мы уже говорили, существуют и другие, которые предоставляют сведения о данных и об окружении, в котором работает awk:

* ARGC — количество аргументов командной строки.
* ARGV — массив с аргументами командной строки.
* ARGIND — индекс текущего обрабатываемого файла в массиве ARGV.
* ENVIRON — ассоциативный массив с переменными окружения и их значениями.
* ERRNO — код системной ошибки, которая может возникнуть при чтении или закрытии входных файлов.
* FILENAME — имя входного файла с данными.
* FNR — номер текущей записи в файле данных.
* IGNORECASE — если эта переменная установлена в ненулевое значение, при обработке игнорируется регистр символов.
* NF — общее число полей данных в текущей записи.
* NR — общее число обработанных записей.

Переменные ARGC и ARGV позволяют работать с аргументами командной строки. При этом скрипт, переданный awk, не попадает в массив аргументов ARGV. Напишем такой скрипт:

$ awk 'BEGIN{print ARGC,ARGV[1]}' myfile

После его запуска можно узнать, что общее число аргументов командной строки — 2, а под индексом 1 в массиве ARGV записано имя обрабатываемого файла. В элементе массива с индексом 0 в данном случае будет «awk».

Переменная ENVIRON представляет собой ассоциативный массив с переменными среды. Опробуем её:

$ awk '
    BEGIN {
        print ENVIRON["HOME"]
        print ENVIRON["PATH"]
    }'

Переменные среды можно использовать и без обращения к ENVIRON. Сделать это, например, можно так:

$  echo | awk -v home=$HOME '{print "My home is " home}'

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

$ awk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd

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

Переменные FNR и NR, хотя и могут показаться похожими, на самом деле различаются. Так, переменная FNR хранит число записей, обработанных в текущем файле. Переменная NR хранит общее число обработанных записей. Рассмотрим пару примеров, передав awk один и тот же файл дважды:

$ awk 'BEGIN{FS=","}{print $1,"FNR="FNR}' myfile myfile

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

Взглянем теперь на то, как ведёт себя в подобной ситуации переменная NR:

$ awk '
BEGIN {FS=","}
{print $1,"FNR="FNR,"NR="NR}
END{print "There were",NR,"records processed"}' myfile myfile

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

Пользовательские переменные

#!/usr/bin/env bash

awk '
BEGIN{
    title="Title text"
    print title
}'

Детальная информация

Пользовательские переменные

Как и любые другие языки программирования, awk позволяет программисту объявлять переменные. Имена переменных могут включать в себя буквы, цифры, символы подчёркивания. Однако, они не могут начинаться с цифры. Объявить переменную, присвоить ей значение и воспользоваться ей в коде можно так:

$ awk '
BEGIN{
    test="This is a test"
    print test
}'

Типы регулярных выражений

Детальная информация

Типы регулярных выражений

Реализации регулярных выражений в различных средах, например, в языках программирования вроде Java, Perl и Python, в инструментах Linux вроде sed, awk и grep, имеют определённые особенности. Эти особенности зависят от так называемых движков обработки регулярных выражений, которые занимаются интерпретацией шаблонов.
В Linux имеется два движка регулярных выражений:

  • Движок, поддерживающий стандарт POSIX Basic Regular Expression (BRE).
  • Движок, поддерживающий стандарт POSIX Extended Regular Expression (ERE).

Большинство утилит Linux соответствуют, как минимум, стандарту POSIX BRE, но некоторые утилиты (в их числе — sed) понимают лишь некое подмножество стандарта BRE. Одна из причин такого ограничения — стремление сделать такие утилиты как можно более быстрыми в деле обработки текстов.

Стандарт POSIX ERE часто реализуют в языках программирования. Он позволяет пользоваться большим количеством средств при разработке регулярных выражений. Например, это могут быть специальные последовательности символов для часто используемых шаблонов, вроде поиска в тексте отдельных слов или наборов цифр. Awk поддерживает стандарт ERE.

Существует много способов разработки регулярных выражений, зависящих и от мнения программиста, и от особенностей движка, под который их создают. Непросто писать универсальные регулярные выражения, которые сможет понять любой движок. Поэтому мы сосредоточимся на наиболее часто используемых регулярных выражениях и рассмотрим особенности их реализации для sed и awk.

Символ «звёздочка»

Детальная информация

Символ «звёздочка»

Если в шаблоне после символа поместить звёздочку, это будет означать, что регулярное выражение сработает, если символ появляется в строке любое количество раз — включая и ситуацию, когда символ в строке отсутствует.

$ echo "test" | awk '/tes*t/{print $0}'
$ echo "tessst" | awk '/tes*t/{print $0}'

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

$ echo "I like green color" | awk '/colou*r/{print $0}'
$ echo "I like green colour " | awk '/colou*r/{print $0}'

В этом примере одно и то же регулярное выражение реагирует и на слово «color», и на слово «colour». Это так благодаря тому, что символ «u», после которого стоит звёздочка, может либо отсутствовать, либо встречаться несколько раз подряд.

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

$ awk '/this.*test/{print $0}' myfile

В данном случае неважно сколько и каких символов находится между словами «this» и «test».

Звёздочку можно использовать и с классами символов:

$ echo "st" | awk '/s[ae]*t/{print $0}'
$ echo "sat" | awk '/s[ae]*t/{print $0}'
$ echo "set" | awk '/s[ae]*t/{print $0}'

Регулярные выражения POSIX ERE

Детальная информация

Регулярные выражения POSIX ERE

Шаблоны стандарта POSIX ERE, которые поддерживают некоторые утилиты Linux, могут содержать дополнительные символы. Как уже было сказано, awk поддерживает этот стандарт, а вот sed — нет.

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

Вопросительный знак

#!/usr/bin/env bash

echo "tet" | awk '/tes?t/{print $0}'
echo "test" | awk '/tes?t/{print $0}'
echo "tesst" | awk '/tes?t/{print $0}'

Детальная информация

Вопросительный знак

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

$ echo "tet" | awk '/tes?t/{print $0}'
$ echo "test" | awk '/tes?t/{print $0}'
$ echo "tesst" | awk '/tes?t/{print $0}'

Как видно, в третьем случае буква «s» встречается дважды, поэтому на слово «tesst» регулярное выражение не реагирует.

Вопросительный знак можно использовать и с классами символов:

$ echo "tst" | awk '/t[ae]?st/{print $0}'
$ echo "test" | awk '/t[ae]?st/{print $0}'
$ echo "tast" | awk '/t[ae]?st/{print $0}'
$ echo "taest" | awk '/t[ae]?st/{print $0}'
$ echo "teest" | awk '/t[ae]?st/{print $0}'

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

Символ «плюс»

#!/usr/bin/env bash

echo "test" | awk '/te+st/{print $0}'
echo "teest" | awk '/te+st/{print $0}'
echo "tst" | awk '/te+st/{print $0}'

Детальная информация

Символ «плюс»

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

$ echo "test" | awk '/te+st/{print $0}'
$ echo "teest" | awk '/te+st/{print $0}'
$ echo "tst" | awk '/te+st/{print $0}'

В данном примере, если символа «e» в слове нет, движок регулярных выражений не найдёт в тексте соответствий шаблону. Символ «плюс» работает и с классами символов — этим он похож на звёздочку и вопросительный знак:

$ echo "tst" | awk '/t[ae]+st/{print $0}'
$ echo "test" | awk '/t[ae]+st/{print $0}'
$ echo "teast" | awk '/t[ae]+st/{print $0}'
$ echo "teeast" | awk '/t[ae]+st/{print $0}'

В данном случае если в строке имеется любой символ из класса, текст будет сочтён соответствующим шаблону.

Фигурные скобки

Детальная информация

Фигурные скобки

Фигурные скобки, которыми можно пользоваться в ERE-шаблонах, похожи на символы, рассмотренные выше, но они позволяют точнее задавать необходимое число вхождений предшествующего им символа. Указывать ограничение можно в двух форматах:

n — число, задающее точное число искомых вхождений
n, m — два числа, которые трактуются так: «как минимум n раз, но не больше чем m».

Вот примеры первого варианта:

$ echo "tst" | awk '/te{1}st/{print $0}'
$ echo "test" | awk '/te{1}st/{print $0}'

В старых версиях awk нужно было использовать ключ командной строки —re-interval для того, чтобы программа распознавала интервалы в регулярных выражениях, но в новых версиях этого делать не нужно.

$ echo "tst" | awk '/te{1,2}st/{print $0}'
$ echo "test" | awk '/te{1,2}st/{print $0}'
$ echo "teest" | awk '/te{1,2}st/{print $0}'
$ echo "teeest" | awk '/te{1,2}st/{print $0}'

В данном примере символ «e» должен встретиться в строке 1 или 2 раза, тогда регулярное выражение отреагирует на текст.

Фигурные скобки можно применять и с классами символов. Тут действуют уже знакомые вам принципы:

$ echo "tst" | awk  '/t[ae]{1,2}st/{print $0}'
$ echo "test" | awk  '/t[ae]{1,2}st/{print $0}'
$ echo "teest" | awk  '/t[ae]{1,2}st/{print $0}'
$ echo "teeast" | awk  '/t[ae]{1,2}st/{print $0}'

Шаблон отреагирует на текст в том случае, если в нём один или два раза встретится символ «a» или символ «e».

Символ логического «или»

#!/usr/bin/env bash

echo "This is a test" | awk '/test|exam/{print $0}'
echo "This is an exam" | awk '/test|exam/{print $0}'
echo "This is something else" | awk '/test|exam/{print $0}'

Детальная информация

Символ логического «или»

Символ | — вертикальная черта, означает в регулярных выражениях логическое «или». Обрабатывая регулярное выражение, содержащее несколько фрагментов, разделённых таким знаком, движок сочтёт анализируемый текст подходящим в том случае, если он будет соответствовать любому из фрагментов. Вот пример:

$ echo "This is a test" | awk '/test|exam/{print $0}'
$ echo "This is an exam" | awk '/test|exam/{print $0}'
$ echo "This is something else" | awk '/test|exam/{print $0}'

В данном примере регулярное выражение настроено на поиск в тексте слов «test» или «exam». Обратите внимание на то, что между фрагментами шаблона и разделяющим их символом | не должно быть пробелов.

Группировка фрагментов регулярных выражений

Детальная информация

Группировка фрагментов регулярных выражений

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

$ echo "Like" | awk '/Like(Geeks)?/{print $0}'
$ echo "LikeGeeks" | awk '/Like(Geeks)?/{print $0}'

В данных примерах слово «Geeks» заключено в круглые скобки, после этой конструкции идёт знак вопроса. Напомним, что вопросительный знак означает «0 или 1 повторение», в результате регулярное выражение отреагирует и на строку «Like», и на строку «LikeGeeks».

Пример

#!/usr/bin/env bash

echo $PATH | awk '
BEGIN{
    RS=":"
}
{print $0}'

Детальная информация

Пример

Регулярные выражения POSIX BRE

#!/usr/bin/env bash

echo "This is a test. With sed" | sed -n '/test/p'

echo "This is a test. With awk" | awk '/test/{print $0}'

Детальная информация

Регулярные выражения POSIX BRE

Пожалуй, самый простой шаблон BRE представляет собой регулярное выражение для поиска точного вхождения последовательности символов в тексте. Вот как выглядит поиск строки в sed и awk:

$ echo "This is a test" | sed -n '/test/p'
$ echo "This is a test" | awk '/test/{print $0}'

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

Работая с регулярными выражениями нужно учитывать то, что они чувствительны к регистру символов:

$ echo "This is a test" | awk '/Test/{print $0}'
$ echo "This is a test" | awk '/test/{print $0}'

Первое регулярное выражение совпадений не нашло, так как слово «test», начинающееся с заглавной буквы, в тексте не встречается. Второе же, настроенное на поиск слова, написанного прописными буквами, обнаружило в потоке подходящую строку.

В регулярных выражениях можно использовать не только буквы, но и пробелы, и цифры:

$ echo "This is a test 2 again" | awk '/test 2/{print $0}'

Пробелы воспринимаются движком регулярных выражений как обычные символы.

Специальные символы

#!/usr/bin/env bash

echo "There is 10$ on my pocket" | awk '/$/{print $0}'

Детальная информация

Специальные символы

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

Если один из них нужен в шаблоне, его нужно будет экранировать с помощью обратной косой черты (обратного слэша) — .

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

There is 10$ on my pocket

Знак доллара можно обнаружить с помощью такого шаблона:

$ awk '/$/{print $0}' myfile

Кроме того, обратная косая черта — это тоже специальный символ, поэтому, если нужно использовать его в шаблоне, его тоже надо будет экранировать. Выглядит это как два слэша, идущих друг за другом:

$ echo " is a special character" | awk '/\/{print $0}'

Хотя прямой слэш и не входит в приведённый выше список специальных символов, попытка воспользоваться им в регулярном выражении, написанном для sed или awk, приведёт к ошибке:

$ echo "3 / 2" | awk '///{print $0}'

Если он нужен, его тоже надо экранировать:

$ echo "3 / 2" | awk '///{print $0}'

Якорные символы

Детальная информация

Якорные символы

Существуют два специальных символа для привязки шаблона к началу или к концу текстовой строки. Символ «крышка» — ^ позволяет описывать последовательности символов, которые находятся в начале текстовых строк. Если искомый шаблон окажется в другом месте строки, регулярное выражение на него не отреагирует. Выглядит использование этого символа так:

$ echo "welcome to likegeeks website" | awk '/^likegeeks/{print $0}'
$ echo "likegeeks website" | awk '/^likegeeks/{print $0}'

Символ ^ предназначен для поиска шаблона в начале строки, при этом регистр символов так же учитывается. Посмотрим, как это отразится на обработке текстового файла:

$ awk '/^this/{print $0}' myfile

При использовании sed, если поместить крышку где-нибудь внутри шаблона, она будет восприниматься как любой другой обычный символ:

$ echo "This ^ is a test" | sed -n '/s ^/p'

В awk, при использовании такого же шаблона, данный символ надо экранировать:

$ echo "This ^ is a test" | awk '/s ^/{print $0}'

С поиском фрагментов текста, находящихся в начале строки мы разобрались. Что, если надо найти нечто, расположенное в конце строки?

В этом нам поможет знак доллара — $, являющийся якорным символом конца строки:

$ echo "This is a test" | awk '/test$/{print $0}'

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

$ awk '/^this is a test$/{print $0}' myfile

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

Вот как, пользуясь якорными символами, отфильтровать пустые строки:

$ awk '!/^$/{print $0}' myfile

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

Символ «точка»

Детальная информация

Символ «точка»

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

$ awk '/.st/{print $0}' myfile

Как видно по выведенным данным, шаблону соответствуют лишь первые две строки из файла, так как они содержат последовательность символов «st», предварённую ещё одним символом, в то время как третья строка подходящей последовательности не содержит, а в четвёртой она есть, но находится в самом начале строки.

Классы символов

Детальная информация

Классы символов

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

Благодаря такому подходу можно организовать поиск любого символа из заданного набора. Для описания класса символов используются квадратные скобки — []:

$ awk '/[oi]th/{print $0}' myfile

Тут мы ищем последовательность символов «th», перед которой есть символ «o» или символ «i».

Классы оказываются очень кстати, если выполняется поиск слов, которые могут начинаться как с прописной, так и со строчной буквы:

$ echo "this is a test" | awk '/[Tt]his is a test/{print $0}'
$ echo "This is a test" | awk '/[Tt]his is a test/{print $0}'

Отрицание классов символов

Детальная информация

Отрицание классов символов

Классы символов можно использовать и для решения задачи, обратной описанной выше. А именно, вместо поиска символов, входящих в класс, можно организовать поиск всего, что в класс не входит. Для того, чтобы добиться такого поведения регулярного выражения, перед списком символов класса нужно поместить знак ^. Выглядит это так:

$ awk '/[^oi]th/{print $0}' myfile

В данном случае будут найдены последовательности символов «th», перед которыми нет ни «o», ни «i».

Диапазоны символов

Детальная информация

Диапазоны символов

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

$ awk '/[e-p]st/{print $0}' myfile

В данном примере регулярное выражение реагирует на последовательность символов «st», перед которой находится любой символ, расположенный, в алфавитном порядке, между символами «e» и «p».

Диапазоны можно создавать и из чисел:

$ echo "123" | awk '/[0-9][0-9][0-9]/'
$ echo "12a" | awk '/[0-9][0-9][0-9]/'

В класс символов могут входить несколько диапазонов:

$ awk '/[a-fm-z]st/{print $0}' myfile

Данное регулярное выражение найдёт все последовательности «st», перед которыми есть символы из диапазонов a-f и m-z.

Специальные классы символов

#!/usr/bin/env bash

echo 123 | awk '/^[[:digit:]]*$/{print $0}'

Детальная информация

Специальные классы символов

В BRE имеются специальные классы символов, которые можно использовать при написании регулярных выражений:

[[:alpha:]] — соответствует любому алфавитному символу, записанному в верхнем или нижнем регистре.
[[:alnum:]] — соответствует любому алфавитно-цифровому символу, а именно — символам в диапазонах 0-9, A-Z, a-z.
[[:blank:]] — соответствует пробелу и знаку табуляции.
[[:digit:]] — любой цифровой символ от 0 до 9.
[[:upper:]] — алфавитные символы в верхнем регистре — A-Z.
[[:lower:]] — алфавитные символы в нижнем регистре — a-z.
[[:print:]] — соответствует любому печатаемому символу.
[[:punct:]] — соответствует знакам препинания.
[[:space:]] — пробельные символы, в частности — пробел, знак табуляции, символы NL, FF, VT, CR.

Использовать специальные классы в шаблонах можно так:

$ echo "abc" | awk '/[[:alpha:]]/{print $0}'
$ echo "abc" | awk '/[[:digit:]]/{print $0}'
$ echo "abc123" | awk '/[[:digit:]]/{print $0}'

Понравилась статья? Поделить с друзьями:
  • Bash trap error
  • Bash throw error
  • Bash syntax error unterminated quoted string
  • Bash syntax error operand expected error token is
  • Bash syntax error near unexpected token перевод