我试图这样做来决定是否将stdin重定向到文件:
[ ...some condition here... ] && input=$fileName || input="&0"
./myScript < $input
但这不起作用,因为当变量$ input为“&0”时,bash会将其解释为文件名。
但是,我可以这样做:
if [ ...condition... ];then
./myScript <$fileName
else
./myScript
问题是./myScript实际上是一个我不想复制的长命令行,也不想为它创建一个函数,因为它也不长(它不值得)。
然后我突然想到这样做:
[ ...condition... ] && input=$fileName || input= #empty
cat $input | ./myScript
但是这需要再运行一个命令和一个管道(即子shell)。 还有另一种更简单,更有效的方法吗?
首先,stdin是文件描述符0(零)而不是1(stdout)。
您可以像这样有条件地复制文件描述符或使用文件名:
[[ some_condition ]] && exec 3<"$filename" || exec 3<&0
some_long_command_line <&3
请注意,如果条件为false或第一个exec
失败,则显示的命令将执行第二个exec
。如果您不希望潜在的失败,那么您应该使用if
/ else
:
if [[ some_condition ]]
then
exec 3<"$filename"
else
exec 3<&0
fi
但是如果第一次重定向失败(在条件为真之后),则文件描述符3的后续重定向将失败。
(
if [ ...some condition here... ]; then
exec <$fileName
fi
exec ./myscript
)
在子shell中,有条件地重定向stdin并执行脚本。
标准输入也可以由特殊设备文件/dev/stdin
表示,因此使用它作为文件名将起作用。
file="/dev/stdin"
./myscript < "$file"
怎么样
function runfrom {
local input="$1"
shift
case "$input" in
-) "$@" ;;
*) "$@" < "$input" ;;
esac
}
我使用减号来表示标准输入,因为这是许多Unix程序的传统。
现在你写
[ ... condition ... ] && input="$fileName" || input="-"
runfrom "$input" my-complicated-command with many arguments
我发现这些函数/命令将命令作为参数(如xargs(1)
)非常有用,并且它们组合得很好。
如果你小心,你可以使用'eval
'和你的第一个想法。
[ ...some condition here... ] && input=$fileName || input="&1"
eval ./myScript < $input
但是,你说'myScript'实际上是一个复杂的命令调用;如果它涉及可能包含空格的参数,那么在决定使用'eval
'之前必须非常小心。
坦率地说,担心'cat
'命令的成本可能不值得这么麻烦;它不太可能成为瓶颈。
更好的是设计myScript
,使其像普通的Unix过滤器一样工作 - 它从标准输入读取,除非给出一个或多个文件(例如,cat
或grep
作为示例)。该设计基于长期和良好的体验 - 因此值得模仿,以避免不得不处理这样的问题。
使用eval
:
#! /bin/bash
[ $# -gt 0 ] && input="'"$1"'" || 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
,请使用process substitution:
$ ./myDemux <(md5sum /etc/issue) eussi/cte/ 01672098e5a1807213d5ba16e00a7ad0
请注意,如果您尝试直接管道输出,请参阅
$ md5sum /etc/issue | ./myDemux
它将等待来自终端的输入,而ephemient's answer没有这个缺点。
稍微改变会产生所需的行为:
#! /bin/bash
[ $# -gt 0 ] && input="'"$1"'" || input=/dev/stdin
eval "./myScript <$input"
人们向你展示很长的剧本,但是......你得到了bash陷阱:)你必须用bash引用一切。例如,您需要名为&0的列表文件。
filename ='&0'#right ls $ filename #wrong!这个替换$ filename并解释&0 ls“$ filename”#right
另一个,带空格的文件。
filename ='带空格的文件'ls $ filename #wrong,bash剪切第一个和最后一个空格,并减少with和空格之间的多个空格ls“$ filename”righ
你的脚本也一样。请更换:
./myScript < $input
至
./myScript < "$input"
一切。 bash有更多的陷阱。我建议出于同样的原因为“$ file”报价。空间和其他可以解释的字符总是会产生问题。
但是/ dev / stdin怎么样?这只有在重定向stdin并想要将内容打印到真正的标准输入时才可用。
所以,你的脚本应该显示如下:
[ ...some condition here... ] && input="$fileName" || input="&0"
./myScript < "$input"