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
В одной из предыдущих статей я немного рассказал про потоки процесса и их нумерацию:
- 0 - stdin - входной поток процесса, через него могут передаваться данные из другой команды
- 1 - stdout - выходной поток процесса, в этот поток процесс может писать результаты
- 2 - stderr - поток для ошибок и отладки
Все перенаправления потоков, рассмотренные дальше, действуют только в рамках текущей команды или набора команд.
оператор [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
Ссылки #
- https://www.gnu.org/software/bash/manual/html_node/Redirections.html
- https://tldp.org/LDP/abs/html/io-redirection.html
- https://man7.org/linux/man-pages/man1/dash.1.html