Bash:在脚本中dynamic地redirect标准input

我试图做这个决定是否redirectstdin到一个文件或不:

[ ...some condition here... ] && input=$fileName || input="&0" ./myScript < $input 

但是,这是行不通的,因为当variables$ input是“&0”时,bash将其解释为文件名。

不过,我可以这样做:

 if [ ...condition... ];then ./myScript <$fileName else ./myScript 

问题是./myScript实际上是一个很长的命令行,我不想重复,也不想为它创build一个函数,因为它不是那么长(这是不值得的)。

然后,我想到了这一点:

 [ ...condition... ] && input=$fileName || input= #empty cat $input | ./myScript 

但是这需要运行一个命令和一个pipe道(即一个子shell)。
还有另一种更简单,更高效的方法吗?

Solutions Collecting From Web of "Bash:在脚本中dynamic地redirect标准input"

首先stdin是文件描述符0(零),而不是1(这是标准输出)。

你可以像这样有条件地复制文件描述符或使用文件名:

 [[ some_condition ]] && exec 3<$filename || exec 3<&0 some_long_command_line <&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过滤器一样工作 – 它从标准输入中读取,除非给出一个或多个文件(例如catgrep )。 这种设计是基于长期和健全的经验 – 因此值得仿效,以避免必须处理这样的问题。

使用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
钱币
酒吧
 RAB
巴兹
朱达

请注意,它也处理输入中的空格:

 $ ./myDemux foo \ bar
尽管如此,

要将输入传递到myScript ,请使用流程替换 :

 $ ./myDemux <(md5sum / etc / issue)
 eussi / cte / 01672098e5a1807213d5ba16e00a7ad0

请注意,如果您尝试直接输出输出,如

 $ md5sum / etc / issue |  ./myDemux

它会挂在终端等待输入,而ephemient的答案没有这个缺点。

轻微的变化会产生所需的行为:

 #! /bin/bash [ $# -gt 0 ] && input="'"$1"'" || input=/dev/stdin eval "./myScript <$input" 

人们向你展示非常长的脚本,但是……你得到了bash陷阱:)你必须在bash中引用所有的东西。 例如,您想要名为&0的列表文件。

filename ='&0'#right ls $ filename #wrong! 这个替换$ filename并解释&ls“$ filename”#right

另一个,文件与空间。

文件名='有空格的文件'ls $ filename #wrong,bash剪切首先和最后一个空格,并减少with和空格之间的多个空格ls“$ filename”righ

你的脚本也一样。 请更换:

 ./myScript < $input 

 ./myScript < "$input" 

它的一切。 bash有更多的陷阱。 我建议用相同的原因为“$ file”做报价。 空格和其他字符不能被解释,总是会造成问题。

但是/ dev / stdin呢? 这只有在你重定向stdin并想打印一些东西到真正的stdin时才可用。

所以,你的脚本应该如下所示:

 [ ...some condition here... ] && input="$fileName" || input="&0" ./myScript < "$input"