Linux Tools: shells, ash #3 - ash syntax, redirections

linux linux-tools shell ash

Продолжаем про ash и простые команды, сегодня разберемся с потоками и перенаправлением потоков процесса.

Предыдущая статья: shells, ash #2 - ash syntax, simple commands

Следующая статья: shells, ash #4 - ash syntax, complex commands, pipelines, conditions and loops

В одной из предыдущих статей я немного рассказал про потоки процесса и их нумерацию:

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

оператор [n]> file

Потоки можно перенаправлять в файлы

# 123 будет записано в файл
# тут мы перенаправили поток stdout в файл tmp.txt
$ echo 123 > tmp.txt

Оператор > позволяет перенаправить поток в файл по номеру потока. Приведенная выше команда это краткая запись команды. Полная запись выглядит так

# перенаправляем stdout в файл tmp.txt
$ echo 123 1> tmp.txt

Аналогичным образом можно перенаправить stderr

$ echo 'error' 2> errors.log

Оператор > полностью перезаписывает содержимое файла новыми данными, если файл уже существует. Если файла нет - создает и записывает данные.

Для защиты от перезаписи файлов, например, при отладке каких-то уже существующих скриптов, существует аргумент (noclobber), который предотвращает перезапись файла — с этим аргументом ash будет выдавать ошибку, если файл уже существует.

оператор [n]>| file

Делает все то же самое что и предыдущий оператор, но при запуске с аргументом -C (noclobber) файл все равно будет перезаписан — такой force для перенаправления потока в файл.

# скрипт записывает текст в файл с использованием оператора >
$ cat test.sh
echo 123 > tmp.txt

# выполняем - в файл tmp.txt записывается 123
$ ash test.sh

# еще раз выполняем тот же скрипт,
# файл tmp.txt будет перезаписан с тем же текстом 123
$ ash test.sh

# с аргументом -C при перенаправлении с оператором > будет ошибка
$ ash -C test.sh
test.sh: line 1: can't create tmp.txt: File exists

# если переделать оператор > на >|
$ cat test.sh
echo 1234 >| tmp.txt

# то даже с аргументом -C файл будет перезаписан
$ ash -C test.sh

оператор [n]>> file

Дописывает данные из потока в конец файла, если файла нет - то создает

$ echo 567 1>> tmp.txt
$ echo 890 >> tmp.txt
$ cat tmp.txt
1234
567
890

оператор [n]< file

чтение потока из файла

В основном используется для чтения stdin

# команда cat выводит stdin
# или содержимое файла (если указан путь к файлу)
# будет выведено содержимое stdin,
# которое будет прочитано из файла tmp.txt
$ cat 0< tmp.txt
1234
567
890

# сокращенная запись без указания номера потока
$ cat < tmp.txt
# вывод такой же

Оператор перенаправления может быть записан и до команды

$ < tmp.txt cat
# командой cat из stdin будет выведено содержимое файла tmp.txt

оператор [n1]>&n2

Перенаправляет поток n1 (или stdout - 1 по умолчанию) в поток с номером n2

Интерактивный шелл тоже процесс и у него есть свои stdin, stdout, stderr

# пишем в stdout - поток с номером 1
$ echo 'out' >&1
out

# пишем в stderr - поток с номером 2
$ echo 'error' >&2
error

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

Для скриптов также можно писать отдельно в stdout и stderr

# скрипт пишет раздельно в stdout и в stderr
$ cat test.sh
echo 'out' >&1
echo 'error' >&2

# если просто выполнить скрипт, то всё увидим в консоли
$ ash test.sh
out
error

# С помощью оператора >
# мы можем перенаправить stdout и stderr в разные файлы
$ ash test.sh 1> stdout.txt 2>stderr.txt
$ cat stdout.txt
out
$ cat stderr.txt
error

Еще с помощью этого оператора можно наоборот сводить потоки, например, stdout и stderr в один файл

$ ash test.sh 1> stdout.txt
error

# в консоли вывелось error,
# потому что sterr не перенаправлен и отображается в консоль

# заворачиваем поток stderr в stdout
$ ash test.sh 1> stdout.txt 2>&1

# и stdout и stderr записались в файл stdout.txt
$ cat stdout.txt
out
error

Многострочные данные для потока - here-document, heredoc

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

$ echo "very very very \
long long long \
text text text"
very very very long long long text text text

Для решения этой проблемы в ash существует синтаксис here-document, который в общем виде выглядит так

[n]<< delimiter
    here-doc-text ...
delimiter

delimiter может быть любой последовательностью символов, первый раз после указания delimiter на следующей строке должны начаться данные, после второго указания delimiter с начала строки данные завершились. Обычно delimiter указывают EOL - как аббревиатура End Of Line, но он может быть и другой последовательностью символов.

$ cat 0<< EOL
> very very very
> long long long
> text text text
> EOL
very very very
long long long
text text text

В интерактивном режиме после EOL и переноса строки shell символом > в начале строки подсказывает что ввод команды продолжается.

Для скриптов heredoc используется аналогично

$ cat test.sh
cat << EOL
1
2
3
EOL
$ ash test.sh
1
2
3

Если требуется в скрипте визуально отделить содержимое heredoc, можно использовать символ табуляции и добавить оператор <<- – в этом случае shell удалит все табы с начала строк

$ cat test.sh
cat <<- EOL
<tab>1
<tab>2
<tab>3
EOL
$ ash test.sh
1
2
3

Кроме этого внутри heredoc можно использовать shell-переменные

$ cat test.sh
cat << EOL
pwd = $PWD
EOL
$ ash test.sh
pwd = /home/ubuntu

Чтобы текст внутри heredoc обрабатывался как обычные символы без подстановки переменных, нужно первый delimiter заключить в кавычки (двойные или одинарные)

$ cat test.sh
cat << 'EOL'
pwd = $PWD
EOL
$ ash test.sh
pwd = $PWD

Работа с дескрипторами

Еще одна фича для работы с дескрипторами файлов — это возможность открыть файл в какой-то номер дескриптора, номера могут быть от 0 до 9.

Для того чтобы использовать номер дескриптора в скрипте или интерактивном shell для нескольких команд нужно использовать builtin-команду exec (подробнее мы рассмотрим ее в следующих статьях).

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

оператор [n]<> file

Позволяет открыть дескриптор файла на чтение и запись

# открываем поток (дескриптор файла) с номером 3 для файла tmp.txt
$ exec 3<> ./tmp.txt
# так как команда была выполнена с помощью exec
# дескриптор доступен для следующих команд
$ echo '1' >&3
$ echo '2' >&3
$ echo '3' >&3
$ cat ./tmp.txt
1
2
3

оператор [n]>-

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

# продолжая предыдущий пример
$ exec 3>-
# дескриптор файла tmp.txt закрыт
# если снова открыть дескриптор,
# то запись или чтение из файла начнется с начала
$ exec 3<> ./tmp.txt
# читаем из дескриптора 2 байта (1\n) - единицу и перенос строки
$ read -n 2 <&3
# сейчас указатель дексриптора указывает на вторую строку (2\n)
# запишем 9
$ echo 9 >&3
$ cat ./tmp.txt
1
9
3

Аналогично можно работать с input с помощью операторов [n1]<&n2, [n]<-

 

=

Сегодня мы разобрались с перенаправлением потоков stdin, stdout, stderr и возможностями использовать дескрипторы файлов в скриптах. В следующих статьях поговорим про сложные команды, пайпы, условия выполнения и циклы.

Следующая статья: shells, ash #4 - ash syntax, complex commands, pipelines, conditions and loops

 

Ссылки


Все статьи серии “Linux Tools”