Как работает перенаправление ввода?

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

$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

Другие вопросы по тегам