Механика этого синтаксиса оболочки: ${1:-$(</ dev / stdin)}
Я недавно столкнулся с этим действительно аккуратным синтаксисом, в контексте создания bash
функции, которые могут принимать аргумент или поток из STDIN (т.е. могут быть переданы по каналу). На первый взгляд, я понимаю, что здесь происходит, но я хотел бы немного больше объяснить фактическую механику того, как это работает.
Вот синтаксис (согласно заголовку): ${1:-$(</dev/stdin)}
И в контексте можно использовать его как:
log(){
echo -e >&1 "INFO: ${1:-$(</dev/stdin)}"
}
Разрешить следующее использование:
$ log foo
INFO: foo
В качестве альтернативы, вы также можете сделать это
mv -v foo.ext bar.ext | log
INFO: renamed 'foo.ext' -> 'bar.ext'
Это здорово, потому что это единственный лаконичный метод, который я видел для включения функций аргументов и конвейеров с bash
функции (я забыл, где я столкнулся с этим сейчас, к сожалению).
Теперь я понимаю (или думаю, что понимаю) большую часть того, что происходит здесь, по крайней мере, поверхностно, но я был бы признателен за более глубокое понимание. Вот как я это понимаю, а затем мои оставшиеся вопросы:
${1:-$(</dev/stdin)}
${1}
очевидно, это аргумент по умолчанию, который принимает функция${1:-x}
это расширение переменной / скобки 'отступить' к строке'x'
если$1
в противном случае пусто (или не установлено?). В этом случае возвращаемся к процессу STDIN.$()
очевидно, подстановка командыпроцесса- и наконец,
</dev/stdin
это, очевидно, перенаправление со стандартного ввода, которое позволяет каналу работать вообще.
Это по сути говорит, что если $1
не заполнен аргументом, вернемся к использованию STDIN - что я доволен концептуально.
Итак, вот мои вопросы:
- Я никогда не видел перенаправления (
<
) внутри подстановки командыпроцесса, без фактической команды, предшествующей ей (например,$(cat < somefile.ext)
). Итак, что же на самом деле происходит (мелочность), когда подстановка командыпроцессаполучает перенаправление без вызова другой команды? - Почему вообще необходимо заключать перенаправление STDIN в подстановку команд
процесса? (на самом деле, когда я пишу это, мне приходит в голову, я не проверял это без, но я буду держать это простым). - Это безопасно? Я использовал его с многострочным STDIN, и он до сих пор не сломался. Где это может упасть (если где-нибудь?).
2 ответа
$(
..)
: из руководства bash - подстановка команд, а не подстановка процессов<(
..)
, и из подстановки команд
Подстановка команд $(cat file) может быть заменена эквивалентной, но более быстрой $ (
/dev/stdin
символическая ссылка на/proc/self/fd/0
удобно здесь из-за$(<
..)
синтаксис, который ожидает файл.это может вызвать проблемы, потому что команда может быть заблокирована, пока стандартный ввод не будет закрыт. это безопасно в том смысле, что многострочный ввод будет сохранен, потому что двойные кавычки.
Наконец, создание канала и разветвление процесса (как в mv -v foo.ext bar.ext | log
) для каждой команды журнала может быть неэффективным.
Отвечая на ваши вопросы
- Вы перепутали синтаксис между использованием процесса подстановки, который принимает синтаксис
<(cmd)
и простое перенаправление оболочки< file
, В простой форме,<
в основном это читать со стандартного ввода и>
записать на стандартный вывод. Синтаксис< file
это сокращенный синтаксис для размещения содержимогоfile
сделан доступным на стандартном вводе, который может быть прочитан командами. - Поэтому, когда вы бежите
cat < file
, это в основном ставит содержаниеfile
в стандартном дескрипторе входного файла, который позже, чемread
посредствомcat
процесс. Преимущество использования$(<file)
будет оболочка не имеет форк внешнего процессаcat
и просто использовать свой собственный механизм для чтения содержимого файла. $(..)
это синтаксис для подстановки команд, где команда запускается в среде под-оболочки и$(..)
заменяется стандартным выводом команды. Для особого случая"$(<file)"
т. е. без каких-либо команд и только задействованных перенаправлений оболочка вместо чтения со стандартного ввода начинает чтение с начала файла и помещает результат в стандартный вывод.