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

问题描述 投票:29回答:7

我正在尝试确定是否将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

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

linux bash shell io-redirection
7个回答
24
投票

首先stdin是文件描述符0(零)而不是1(stdout)。

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

[[ some_condition ]] && exec 3<"$filename" || exec 3<&0

some_long_command_line <&3

请注意,如果条件为假,则显示的命令将执行第二个exec 第一个exec失败。如果您不想这样做,那么应该使用if / else

if [[ some_condition ]]
then
    exec 3<"$filename"
else
    exec 3<&0
fi

但是如果第一次重定向失败(在条件为真之后),那么从文件描述符3进行的后续重定向将失败。


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

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


7
投票

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

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

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))可能非常有用,并且组合得很好。


2
投票

如果小心,可以使用'eval'和第一个想法。

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

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

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

[更好的是设计myScript,使其像常规的Unix过滤器一样工作-除非从标准输入中读取一个或多个文件即可工作(例如,以catgrep为例), 。该设计基于长期和完善的经验-因此值得进行仿真,以避免不得不处理此类问题。


1
投票

使用eval

eval

此简单的#! /bin/bash [ $# -gt 0 ] && input="'"$1"'" || input="&1" eval "./myScript <$input" 替代品

myScript

产生以下输出:

$ ./myDemux myScriptplrep / nib / rsu /!#esrever = _ $$ ./myDemux富钱币酒吧拉布巴兹扎布

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

$ ./myDemux foo \ bareman eht ni ecaps a htiw elif

要通过管道将输入降低到#! /usr/bin/perl -lp $_ = reverse ,请使用myScript

$ ./myDemux 

请注意,如果您尝试直接通过管道传递输出,如中所示

$ md5sum / etc / issue | ./myDemux

它将挂起,等待来自终端的输入,而process substitution没有此缺点。

稍有变化就会产生所需的行为:

ephemient's answer

0
投票

人们向您展示很长的脚本,但是....您会遇到bash陷阱:)您必须用bash引用所有内容。例如,您想要名为&0的列表文件。

filename ='&0'#rightls $ filename#错误!这个替代$ filename并解释&0ls“ $ filename” #right

另一个,带空格的文件。

filename ='一些带有空格的文件'ls $ filename#错误,bash剪切了第一个和最后一个空格,并在with和空格词之间减少了多个空格ls“ $ filename” righ

脚本中也是如此。请更改:

#! /bin/bash

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

to

./myScript < $input

全部。 bash有更多陷阱。我建议出于同样的原因对“ $ file”进行报价。空格和其他无法解释的字符总是会引起问题。

但是/ dev / stdin呢?仅当您重定向标准输入并想将某些内容打印到实际标准输入时,此功能才可用。

因此,您的脚本应显示如下:

./myScript < "$input"
© www.soinside.com 2019 - 2024. All rights reserved.