32

我试图这样做来决定是否将标准输入重定向到文件:

[ ...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)。
还有其他更简单、更高效的方法吗?

4

7 回答 7

27

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

于 2010-01-01T00:19:51.173 回答
8

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

file="/dev/stdin"
./myscript < "$file"
于 2010-01-01T00:43:54.137 回答
7
(
    if [ ...some condition here... ]; then
        exec <$fileName
    fi
    exec ./myscript
)

在子外壳中,有条件地重定向标准输入并执行脚本。

于 2009-12-31T23:23:49.537 回答
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))的函数/命令非常有用,而且它们组合得很好。

于 2009-12-31T21:37:40.793 回答
2

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

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

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

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

更好的设计myScript是让它像一个普通的 Unix 过滤器一样工作——它从标准输入中读取,除非给它一个或多个文件来工作(例如,cat或者grep作为示例)。该设计基于长期而可靠的经验 - 因此值得效仿以避免不得不处理此类问题。

于 2009-12-31T22:08:20.130 回答
1

使用eval

#! /bin/bash

[ $# -gt 0 ] && input="'"$1"'" || input="&1"

eval "./myScript <$input"

这个简单的替身myScript

#! /usr/bin/perl -lp
$_ = reverse

产生以下输出:

$ ./myDemux myScript
pllrep/nib/rsu/ !#
esrever = _$

$ ./myDemux
富
钱币
酒吧
拉布
巴兹
扎布

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

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

要将输入向下传递到myScript,请使用进程替换

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

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

$ md5sum /etc/issue | ./myDemux

它会挂起等待来自终端的输入,而ehemient 的答案没有这个缺点。

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

#! /bin/bash

[ $# -gt 0 ] && input="'"$1"'" || input=/dev/stdin
eval "./myScript <$input"
于 2009-12-31T21:54:48.703 回答
0

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

文件名='&0' #正确 ls $文件名 #错误!这个替换 $filename 并解释 &0 ls "$filename" #right

另一个,带空格的文件。

filename=' 一些带空格的文件 ' ls $filename #错误,bash 删掉第一个和最后一个空格,并减少 with 和空格之间的多个空格 word ls "$filename" righ

你的脚本也是如此。请更换:

./myScript < $input

./myScript < "$input"

它的全部。bash 有更多的陷阱。我建议以同样的理由为 "$file" 报价。空格和其他无法解释的字符总是会产生问题。

但是 /dev/stdin 呢?这仅在您重定向标准输入并希望将某些内容打印到真正的标准输入时才可用。

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

[ ...some condition here... ] && input="$fileName" || input="&0"
./myScript < "$input"
于 2013-08-22T13:59:06.187 回答