Как работает перенаправление ввода?
Насколько я понимаю, любая команда, которая читает со стандартного ввода (то есть с клавиатуры), что она получает свой ввод из файла.
$echo < text_content.txt
$
Но команда echo не читает и не отображает text_content.txt на терминале. что здесь не так?
1 ответ
стандартный ввод и команды
Насколько я понимаю, любая команда, которая читает со стандартного ввода (то есть с клавиатуры), что она получает свой ввод из файла.
По сути, да. Как я уже обсуждал в своем ответе Что характеризует файл в Linux/Unix? файл - это любой объект, над которым вы можете выполнять стандартные операции, такие как read()
, open()
, write()
, close()
, stdin
в этом отношении представление через файловый дескриптор 0 фактически является файлом, и любая команда / процесс в Linux получает 3 стандартных файловых дескриптора - stdin, stdout, stderr - при запуске этого процесса. Каковы фактические файлы за этими файловыми дескрипторами? Команда не заботится и не должна, пока она может выполнять операции над ней.
Но команда echo не читает и не отображает text_content.txt на терминале. что здесь не так?
Теперь команда может делать то, что будет с этими файловыми дескрипторами 1. В случае echo
это касается только stdout
и не выполняет никаких действий на stdin
совсем. Так что нет ничего плохого в самой команде.
<
перенаправление будет open()
файл text_content.txt
для чтения, и он по-прежнему будет назначать дескриптор файла (например, 3), возвращаемый из open()
вызовите файловый дескриптор 0, и если команда связана с stdin - она прочитает из файлового дескриптора 0, как будто ничего не произошло. На самом деле, вы увидите это в действии, если вы запустите strace -f -e dup2,write,openat bash -c 'echo < text_content.txt
openat(AT_FDCWD, "/etc/passwd", O_RDONLY) = 3
dup2(3, 0) = 0
write(1, "\n", 1
) = 1
dup2(10, 0) = 0
+++ exited with 0 +++
Обратите внимание на dup2()
системный вызов. Вот как файловый дескриптор 3 (файл) назначается / перенаправляется. В некотором роде cp original copy
синтаксис, dup2(3,0)
делает копию файлового дескриптора 3 в файловый дескриптор 0, и они указывают на один и тот же файл.
Обратите внимание также, что write()
вывести новую строку в дескриптор файла 1
, Это поведение по умолчанию. Если мы делаем strace -f -e dup2,write,openat bash -c 'echo FOO < /etc/passwd'
вот что мы увидим
dup2(3, 0) = 0
write(1, "FOO\n", 4FOO
) = 4
dup2(10, 0) = 0
+++ exited with 0 +++
Итак, опять же, ничего плохого здесь - перенаправление выполняется правильно и echo
делает свою работу написания материала для stdout
который является файловым дескриптором 1.
Как на самом деле читать файл
Теперь давайте обратимся к чему-то еще. Как мы можем прочитать файл в оболочке? Ну, для этого существует cat
Команда, которая принимает аргументы, так что вы можете сделать просто cat file.txt
, Ты можешь сделать cat < file.txt
? Конечно. Но это означает, что оболочка должна будет сделать это dup2()
звоните, тогда как cat file.txt
нет - так что меньше ненужных системных вызовов - вот что я говорю.
В сложных случаях, например, когда вам нужно выполнить действие с каждой строкой файла, вы должны сделать
while IFS= read -r line || [ -n "$line" ]; do
# command to process line variable here
done < /etc/passwd
Теперь для дескриптора файла всего цикла 0
будет копией любого файлового дескриптора, возвращаемого при открытии /etc/passwd
, Конечно, если вы можете использовать cat
или другая конкретная команда для чтения файла - сделайте это. Оболочка медленный метод и имеет много подводных камней. См. Также, Почему использование цикла оболочки для обработки текста считается плохой практикой?
1. Некоторые приложения могут по-прежнему заботиться о том, что они могут делать с stdin, или определять, является ли stdin файлом или конвейером. когда stdin
дескриптор файла назначается в качестве конца чтения конвейера (который также является дескриптором файла). Выходные данные недоступны для поиска (это означает, что приложение, написанное на C или другом языке, не может использовать seek()
системный вызов для быстрого перехода к определенному байтовому смещению в файле). Хороший пример этого дан в вопросе " Какая разница между" cat file | ./binary "и"./binary <файл "?
Примечание: в Linux это не совсем клавиатура, откуда вводит stdin. Если вы делаете
$ ls -l /proc/self/fd/0
lrwx------ 1 serg serg 64 Feb 23 16:45 /proc/self/fd/0 -> /dev/pts/0
вы увидите в выводе, что это /dev/pts/0
оконечное устройство, к которому stdin
очки изначально. Терминальное устройство затем взаимодействует с клавиатурой, или это может быть также последовательный кабель.
Кроме того, если файл не очень большой, вы можете воспользоваться bash
"s mapfile
встроенный для чтения строк в массиве:
mapfile -t < /etc/passwd
for i in "${MAPFILE[@]}"; do echo "$i"; done