Bash:在脚本中动态重定向标准输入

时间:2009-12-31 21:01:09

标签: linux bash shell io-redirection

我试图这样做以决定是否将stdin重定向到文件:

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

但这不起作用,因为当变量$ input为“&amp; 0”时,bash会将其解释为文件名。

但是,我可以这样做:

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

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

然后我突然想到这样做:

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

但是这需要再运行一个命令和一个管道(即子壳) 还有另一种方法更简单,更有效吗?

7 个答案:

答案 0 :(得分:24)

首先,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的后续重定向将失败。

答案 1 :(得分:7)

(
    if [ ...some condition here... ]; then
        exec <$fileName
    fi
    exec ./myscript
)

在子shell中,有条件地重定向stdin并执行脚本。

答案 2 :(得分:5)

标准输入也可以由特殊设备文件/dev/stdin表示,因此将其用作文件名将起作用。

file="/dev/stdin"
./myscript < "$file"

答案 3 :(得分:2)

怎么样

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))的函数/命令非常有用,并且它们组合得很好。

答案 4 :(得分:2)

如果您小心,可以使用“eval”和您的第一个想法。

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

但是,你说'myScript'实际上是一个复杂的命令调用;如果涉及可能包含空格的参数,那么在决定使用“eval”之前必须非常小心。

坦率地说,担心“cat”命令的成本可能不值得这么麻烦;它不太可能成为瓶颈。

更好的是设计myScript以便它像普通的Unix过滤器一样工作 - 它从标准输入读取,除非给它一个或多个文件起作用(例如,cat或{ {1}}例如)。这种设计基于长期和完善的体验 - 因此值得模仿,以避免不得不处理这样的问题。

答案 5 :(得分:1)

使用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"

答案 6 :(得分:0)

人们向你展示很长的剧本,但是......你得到了bash陷阱:) 你必须用bash引用一切。 例如,您需要名为&amp; 0的列表文件。

filename ='&amp; 0'#right ls $ filename #wrong!这个替换$ filename并解释&amp; 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"