Вопрос или проблема
Предположим, что в Linux есть два файла. fileA и fileB, оба с различными списками фруктов. Я применяю следующие команды:
diff fileA fileB > file.diff
Затем я выполняю следующую команду:
patch fileA 0< file.diff
Вышеуказанная команда применяет патчи (исправляет ошибки) в оригинальном файле (fileA) из входных данных, предоставленных file.diff, и отправляет вывод в fileA (это мое понимание, возможно, я ошибаюсь). Другими словами, fileA и fileB совпадают.
“0<” является символом перенаправления для стандартного ввода (насколько я понимаю). Теперь, поскольку стандартный ввод — это клавиатура, разве команда patch не должна считывать с клавиатуры, а не из file.diff? Как работает указанная команда?
Команды ниже одинаковы. 1
является значением по умолчанию и поэтому может быть опущено. По сути, стандартный вывод из diff
перенаправляется с экрана в файл file.diff
.
diff fileA fileB > file.diff
diff fileA fileB 1> file.diff
Команды ниже одинаковы. 0
является значением по умолчанию и поэтому может быть опущено. По сути, стандартный ввод для patch
перенаправляется с клавиатуры, чтобы поступать из файла file.diff
.
patch fileA 0< file.diff
patch fileA < file.diff
Я попытаюсь объяснить следующее. Когда я ввожу команду tty
, я получаю следующий вывод:
/dev/pts/0
Это означает, что терминальному окну присвоено имя файла /dev/pts/0
. Оба стандартных ввода и вывода назначены именами файлов /dev/fd/0
и /dev/fd/1
соответственно.
Команда ниже проверяет, имеют ли стандартный ввод (/dev/fd/0
) и терминальное окно (/dev/pts/0
) одинаковые значения устройства и inode. Другими словами, тест, чтобы увидеть, являются ли они одинаковыми. В этом случае вывод — true
.
if [[ /dev/fd/0 -ef /dev/pts/0 ]]; then echo "true"; else echo "false"; fi
Команда ниже проверяет, имеют ли стандартный ввод (/dev/fd/0
) и файл file.diff
одинаковые значения устройства и inode. В этом случае вывод — false
.
if [[ /dev/fd/0 -ef file.diff ]]; then echo "true"; else echo "false"; fi
Однако, если стандартный ввод перенаправлен из файла file.diff
, как показано ниже, вывод будет true
.
if [[ /dev/fd/0 -ef file.diff ]]; then echo "true"; else echo "false"; fi < file.diff
“0<” является символом перенаправления для стандартного ввода (насколько я понимаю). Теперь, поскольку стандартный ввод — это клавиатура, разве команда patch не должна считывать с клавиатуры, а не из file.diff? Как работает указанная команда?
Нет. Стандартный ввод подключен к клавиатуре (или, точнее, к устройству ‘tty’, через которое ОС предоставляет ввод с клавиатуры). В любое время соединение может быть закрыто, и вместо него может быть открыто что-то другое – термин “стандартный ввод” относится к конкретному “слоту” подключения, а не к месту его направления. (Поэтому он называется “стандартный ввод”, а не “ввод с клавиатуры”.)
Цифры не просто синтаксис оболочки; они представляют то, как сами программы работают с открытыми файлами. В каждом процессе каждый “открытый файл” представлен числом (дескриптор файла, или как его именуют в Windows, handle). По стандартной конвенции, какой бы открытый файл ни был назначен дескриптору файла 0
, он является “стандартным вводом”.
Если вы запускаете программу из терминала, то “tty” терминала предварительно открывается как FD 0
– или, скорее, унаследован от оболочки, где он уже был открыт – и, следовательно, это стандартный ввод программы. (То же самое для 1
, который является стандартным выводом, и 2
, который является stderr.) Тогда программа ‘diff’ сама должна открыть некоторые файлы, так что она выполнит open("fileA", ...)
, и fileA будет открыт как FD 3
и так далее.
Но так же, как программа может закрыть любой файл, который она сама открыла (например, она может закрыть fileA, выполняя close(3)
), она также может выполнить close(0)
, чтобы закрыть свой стандартный ввод и открыть что-то еще на его месте; до тех пор пока вновь открытый файл получает файловый дескриптор 0
, он по определению является “стандартным вводом”.
Оболочка может сделать то же самое непосредственно перед тем, как запустить программу. Использование <file.diff
или 0<file.diff
означает, что оболочка закроет свой оригинальный дескриптор файла ввода close(0)
и откроет файл open("file.diff")
в качестве нового FD 0
, который затем становится новым стандартным вводом, наследуемым программой ‘diff’.
Ответ или решение
Вопрос, касающийся работы стандартного оператора ввода в Linux, особенно символа "0<", часто вызывает недоумение у пользователей, особенно тех, кто привык думать о стандартном вводе как исключительно о вводе с клавиатуры. Давайте рассмотрим, как работает этот механизм, используя теорию, примеры и их конкретное применение.
Теория:
Стандартный ввод (stdin) в современных операционных системах часто ассоциируется с клавиатурой пользователя. Однако на практике stdin — это нечто большее, чем просто ввод с клавиатуры. В UNIX-подобных системах, таких как Linux, стандартный ввод, стандартный вывод (stdout) и стандартная ошибка (stderr) представлены в виде файловых дескрипторов. Они имеют численные идентификаторы: 0 для stdin, 1 для stdout и 2 для stderr. Эти дескрипторы позволяют оболочкам и программам динамически перенаправлять данные из одного источника в другой.
Конструкция "0<" в командной строке указывает на перенаправление стандартного ввода. В то время как стандартный ввод по умолчанию подключен к клавиатуре через терминальный интерфейс (‘tty’), этот ввод можно заменить данными из файла. Это позволяет программам, таким как patch
, использовать содержимое файла вместо данных, вводимых пользователем с клавиатуры.
Пример:
Рассмотрим конкретный пример с использованием файлов fileA
и fileB
, содержащих списки фруктов. Сначала мы используем команду diff
:
diff fileA fileB > file.diff
Эта команда сравнивает содержимое двух файлов, отправляя вывод (различия) в файл file.diff
. Здесь используется оператор ">", который перенаправляет стандартный вывод в файл. Этот же принцип применим и к перенаправлению stdin. Далее используется команда:
patch fileA 0< file.diff
Здесь оператор "0<" перенаправляет стандартный ввод команды patch
, означая, что программа будет считывать данные из файла file.diff
, а не с клавиатуры. Общепринято считать, что "0<" аналогично просто "<", так как числовое обозначение (0 для stdin) опционально.
Применение:
В чем практическая значимость? Во-первых, понимание этого механизм позволяет автоматизировать обновления и изменения в файлах без необходимости пользовательского ввода. Например, при разработке программного обеспечения или администрировании сервера, автоматизация процессов коррекции данных через патчи помогает поддерживать код в актуальном состоянии.
Кроме того, возможность перенаправления стандартного ввода из файла делает Shell-скрипты более гибкими и мощными. Можно, например, заранее сформировать различные патчи для разных наборов данных и применять их по мере необходимости, что значительно экономит время и ресурсы.
Дополнительно, понимание работы с файловыми дескрипторами в Linux расширяет навык работы с файловой системой и взаимодействие с различными устройствами как с файлами. В пределах процесса, любая сущность, открытая как файл, может быть использована как поток данных, будь то внутренняя работа программы или взаимодействие с внешними устройствами.
Подводя итог, перенаправление стандартного ввода — это фундаментальный аспект UNIX-подобных систем, который улучшает управление процессами и оптимизирует автоматизацию задач, связанных с обработкой данных. Для ИТ-специалистов овладение этими навыками является неотъемлемой частью профессиональной компетенции, обеспечивающей эффективное использование командной строки Linux и оболочки Bash в частности.