Bash: динамически перенаправлять стандартный ввод в скрипт
Я пытался сделать это, чтобы решить, стоит ли перенаправить stdin в файл или нет:
[ ...some condition here... ] && input=$fileName || input="&0"
./myScript < $input
но это не работает, потому что, когда переменная $input "&0", bash интерпретирует ее как имя файла.
однако я мог бы просто сделать:
if [ ...condition... ];then
./myScript <$fileName
else
./myScript
проблема в том, что ./myScript - это длинная командная строка, которую я не хочу дублировать, и я не хочу создавать для нее функцию, потому что она тоже не такая длинная (это того не стоит).
затем мне пришло в голову сделать вот что:--4-->
[ ...condition... ] && input=$fileName || input= #empty
cat $input | ./myScript
но для этого требуется выполнить еще одну команду и канал (т. е. подсеть).
Есть ли другой способ, который проще и эффективнее?
7 ответов
прежде всего stdin - это файловый дескриптор 0 (ноль), а не 1 (который является stdout).
вы можете дублировать дескрипторы файлов или использовать имена файлов условно следующим образом:
[[ some_condition ]] && exec 3<"$filename" || exec 3<&0
some_long_command_line <&3
обратите внимание, что команда будет выполняться второй exec
Если либо условие false или первый exec
не удается. Если вы не хотите, чтобы потенциальный сбой сделал это, вы должны использовать if
/ else
:
if [[ some_condition ]]
then
exec 3<"$filename"
else
exec 3<&0
fi
но затем последующие перенаправление из файлового дескриптора 3 завершится неудачно, если первое перенаправление завершится неудачно (после выполнения условия true).
(
if [ ...some condition here... ]; then
exec <$fileName
fi
exec ./myscript
)
в подрешетке условно перенаправьте stdin и выполните скрипт.
стандартный ввод также может быть представлен специальным файлом устройства /dev/stdin
, Так что использование в качестве имени файла будет работать.
file="/dev/stdin"
./myscript < "$file"
как о
function runfrom {
local input=""
shift
case "$input" in
-) "$@" ;;
*) "$@" < "$input" ;;
esac
}
я использовал знак минус для обозначения стандартного ввода, потому что это традиционно для многих программ Unix.
Теперь вы пишите
[ ... condition ... ] && input="$fileName" || input="-"
runfrom "$input" my-complicated-command with many arguments
Я нахожу эти функции / команды, которые принимают команды в качестве аргументов (например,xargs(1)
) может быть очень полезным, и они хорошо сочиняют.
если вы будете осторожны, вы можете использовать' - Это, наверное, не стоит; это вряд ли будет узким местом.
еще лучше дизайн myScript
так что он работает как обычный фильтр Unix-он читает со стандартного ввода, если ему не дается один или несколько файлов для работы (например, cat
или grep
в качестве примеров). Этот дизайн основан на долгом и здравом опыте - и поэтому стоит подражать, чтобы избежать необходимости иметь дело с такими проблемами, как это.
использовать eval
:
#! /bin/bash
[ $# -gt 0 ] && input="'""'" || input="&1"
eval "./myScript <$input"
это простой дублер для myScript
#! /usr/bin/perl -lp
$_ = reverse
производит следующий вывод:
$ ./myDemux myScript pl- lrep/nib/rsu/ !# esrever = _$ $ ./myDemux foo oof bar rab baz zab
обратите внимание, что он также обрабатывает пробелы во входах:
$ ./myDemux foo\ bar eman eht ni ecaps a htiw elif
для ввода в трубу до myScript
используйте подмена процесса:
$ ./myDemux <(md5sum /etc/issue) eussi/cte/ 01672098e5a1807213d5ba16e00a7ad0
обратите внимание, что если вы пытаетесь передать вывод напрямую, как в
$ md5sum /etc/issue | ./myDemux
он будет висеть, ожидая ввода от терминал, тогда как ephemient это!--12--> не имеет этого недостатка.
небольшое изменение производит желаемое поведение:
#! /bin/bash
[ $# -gt 0 ] && input="'""'" || input=/dev/stdin
eval "./myScript <$input"
люди показывают вам очень длинные сценарии, но.... вы получаете bash trap :) Вы должны привести все в bash. например, вам нужен файл списка с именем &0 .
filename= '&0 ' #справа Общ $имя_файла #неправильно! это заменяет $filename и интерпретирует &0 ls "$filename " #right
другой, файлы с пробелами.
filename= ' некоторый файл с пробелами ' LS $filename #неправильно, bash вырезает первое и последнее пространство и уменьшает несколько пробелов между словами with и пробелами лат "$filename " righ
то же самое в вашем сценарии. пожалуйста, измените:
./myScript < $input
to
./myScript < "$input"
все. у Баша больше ловушек. Я предлагаю сделать цитату для "$file " по той же причине. пробелы и другие символы, которые могут быть интерпретированы, всегда создают проблемы.
но как насчет /dev / stdin ? это можно использовать только тогда, когда вы перенаправили stdin и хотите напечатать что-то в реальном stdin.
Итак, ваш скрипт должен показать, как это:
[ ...some condition here... ] && input="$fileName" || input="&0"
./myScript < "$input"