У меня есть символическая ссылка на скрипт в моем $PATH
файле, который я хотел отредактировать. Я забыл путь к файлу, поэтому я попытался сделать следующее:
$ which my_script_link | readlink
Я ожидал, что это выведет путь к файлу, но вместо этого он вывел
> readlink: missing operand
> Try 'readlink --help' for more information
Я уже видел подобное поведение в других ситуациях (например, при попытке передать список файлов в vim для редактирования). Я знаю, что есть обходные пути, например, subshell readlink $(which my_script_link)
, но я хочу понятьпочемуВ этой ситуации трубопровод не работает так, как я думаю.
Спасибо!
решение1
Проще говоря, потому что программа не написана для этого. Программисты должны решить, может ли их программное обеспечение читать из STDIN или ему требуется входной файл. В случае readlink
, на его man
странице указано (выделено мной):
readlink - печать разрешенасимволические ссылкиили каноническийимена файлов
СИНТАКСИС
readlink [ВАРИАНТ]...ФАЙЛ...
Как правило, программы, которые принимают входные данные из STDIN, предназначены для того, чтобы каким-то образом анализировать эти входные данные. Когда вы передаете данные в конвейер, readlink
он получает текстовый поток и не знает, что с ним делать, поскольку он имеет дело только с файлами, а не с их содержимым. То же самое относится к таким программам, как ls
или cd
или cp
и т. д. Только программы, которые имеют дело с потоками текста/данных, могут принимать входные данные из конвейера.
решение2
Получает ли программа свои входные данные из stdin
или в качестве аргументов командной строки, зависит от разработчика. Оба подхода имеют свои достоинства. Однако для программ, работающих строго с файлами, обычно удобнее передавать имена файлов в качестве аргументов командной строки, а не через stdin
.
Самая очевидная причина заключается в том, что в общем случае, когда требуется просто запустить программу для файла, проще ввести: readlink file
вместо echo file | readlink
.
Более тонкая проблема — корректность. Проблема в том, что когда имена файлов передаются через stdin
, программа должна уметь отличать одно имя файла от другого. Часто это делается, предполагая, что имена файлов разделены пробелами или символами новой строки, но это неверно, поскольку имена файлов могут включать пробелы. Лучший способ — разделить имена файлов нулевыми байтами, но может быть неудобно генерировать список файлов, разделенных нулями.
Передача имен файлов в командной строке позволяет избежать этой проблемы, поскольку оболочка обрабатывает весь разбор и цитирование для программы. Вы можете ввести touch $'foo\nbar'
и touch
правильно увидеть это как одно имя файла, содержащее новую строку, без необходимости обрабатывать какой-либо специальный разбор или цитирование.
При всем при этом, если вы хотите передать файлы stdin
для определенной программы, вы можете. Это то, что xargs
нужно для. xargs
позволяет вам взять программу, которая принимает только аргументы в командной строке, и вместо этого заставить ее принимать свои аргументы через stdin
.
Другими словами, он позволяет вам сделать следующее: which my_script | xargs readlink
.
Если вы хотите, чтобы readlink
работа всегда выполнялась таким образом, вы можете создать псевдоним: alias readlink="xargs readlink"
. Это позволит вам набирать which my_script | readlink
, как вы изначально и хотели.
решение3
Для команд, которые не принимают аргументы через STDIN, вы можете использовать следующую прагму code:
$ readlink $(which my_script_link)
Пример
$ ln -s /bin/ls ~/bin/somecmd
Теперь проверьте, есть ли он на $PATH
.
$ which somecmd
~/bin/somecmd
Или предпочтительный способ, используя type
вместо which
:
$ type somecmd
somecmd is /home/saml/bin/somecmd
Или просто значение:
$ type -P somecmd
/home/saml/bin/somecmd
Теперь бежим readlink
:
$ readlink $(type -P somecmd)
/bin/ls