在 bash 脚本中动态重定向标准输入
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1987105/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Redirect standard input dynamically in a bash script
提问by GetFree
I was trying to do this to decide whether to redirect stdin to a file or not:
我试图这样做来决定是否将 stdin 重定向到文件:
[ ...some condition here... ] && input=$fileName || input="&0"
./myScript < $input
But that doesn't work because when the variable $input is "&0", bash interprets it as a filename.
但这不起作用,因为当变量 $input 为 "&0" 时,bash 将其解释为文件名。
However, I could just do:
但是,我只能这样做:
if [ ...condition... ];then
./myScript <$fileName
else
./myScript
The problem is that ./myScript is actually a long command line that I don't want to duplicate, nor do I want to create a function for it because it's not that long either (it's not worth it).
问题是 ./myScript 实际上是一个很长的命令行,我不想复制它,也不想为它创建一个函数,因为它也没有那么长(不值得)。
Then it occurred to me to do this:
然后我想到要这样做:
[ ...condition... ] && input=$fileName || input= #empty
cat $input | ./myScript
But that requires to run one more command and a pipe (i.e. a subshell).
Is there another way that's simpler and more efficient?
但这需要再运行一个命令和一个管道(即子shell)。
有没有另一种更简单、更有效的方法?
采纳答案by Paused until further notice.
First of all stdin is file descriptor 0 (zero) rather than 1 (which is stdout).
首先,标准输入是文件描述符 0(零)而不是 1(标准输出)。
You can duplicate file descriptors or use filenames conditionally like this:
您可以像这样有条件地复制文件描述符或使用文件名:
[[ some_condition ]] && exec 3<"$filename" || exec 3<&0
some_long_command_line <&3
Note that the command shown will execute the second exec
if either the condition is false orthe first exec
fails. If you don't want a potential failure to do that then you should use an if
/ else
:
请注意,exec
如果条件为假或第一个exec
失败,则显示的命令将执行第二个。如果您不希望这样做可能会失败,那么您应该使用if
/ else
:
if [[ some_condition ]]
then
exec 3<"$filename"
else
exec 3<&0
fi
but then subsequent redirections from file descriptor 3 will fail if the first redirection failed (after the condition was true).
但是如果第一次重定向失败(在条件为真之后),则来自文件描述符 3 的后续重定向将失败。
回答by Ignacio Vazquez-Abrams
Standard input can also be represented by the special device file /dev/stdin
, so using that as a filename will work.
标准输入也可以由特殊设备文件表示/dev/stdin
,因此将其用作文件名将起作用。
file="/dev/stdin"
./myscript < "$file"
回答by ephemient
(
if [ ...some condition here... ]; then
exec <$fileName
fi
exec ./myscript
)
In a subshell, conditionally redirect stdin and exec the script.
在子shell中,有条件地重定向stdin并执行脚本。
回答by Jonathan Leffler
If you're careful, you can use 'eval
' and your first idea.
如果你很小心,你可以使用“ eval
”和你的第一个想法。
[ ...some condition here... ] && input=$fileName || input="&1"
eval ./myScript < $input
However, you say that 'myScript' is actually a complex command invocation; if it involves arguments which might contain spaces, then you must be very careful before deciding to use 'eval
'.
但是,您说“myScript”实际上是一个复杂的命令调用;如果它涉及可能包含空格的参数,那么在决定使用“ eval
”之前必须非常小心。
Frankly, worrying about the cost of a 'cat
' command is probably not worth the trouble; it is unlikely to be the bottleneck.
坦率地说,担心 ' cat
' 命令的成本可能不值得麻烦;它不太可能成为瓶颈。
Even better is to design myScript
so that it works like a regular Unix filter - it reads from standard input unless it is given one or more files to work (like, say, cat
or grep
as examples). That design is based on long and sound experience - and is therefore worth emulating to avoid having to deal with problems such as this.
更好的设计myScript
是让它像一个普通的 Unix 过滤器一样工作——它从标准输入中读取,除非给它一个或多个文件来工作(比如,比如,cat
或者grep
作为例子)。该设计基于长期而完善的经验——因此值得效仿,以避免不得不处理此类问题。
回答by Norman Ramsey
How about
怎么样
function runfrom {
local input=""
shift
case "$input" in
-) "$@" ;;
*) "$@" < "$input" ;;
esac
}
I've used the minus sign to denote standard input because that's traditional for many Unix programs.
我使用减号来表示标准输入,因为这是许多 Unix 程序的传统做法。
Now you write
现在你写
[ ... condition ... ] && input="$fileName" || input="-"
runfrom "$input" my-complicated-command with many arguments
I find these functions/commands which take commands as arguments (like xargs(1)
) can be very useful, and they compose well.
我发现这些将命令作为参数的函数/命令(如xargs(1)
)非常有用,并且它们组合得很好。
回答by Greg Bacon
Use eval
:
使用eval
:
#! /bin/bash
[ $# -gt 0 ] && input="'""'" || input="&1"
eval "./myScript <$input"
This simple stand-in for myScript
这个简单的替身 myScript
#! /usr/bin/perl -lp
$_ = reverse
produces the following output:
产生以下输出:
$ ./myDemux myScript pl- lrep/nib/rsu/ !# esrever = _$ $ ./myDemux foo oof bar rab baz zab
Note that it handles spaces in inputs too:
请注意,它也处理输入中的空格:
$ ./myDemux foo\ bar eman eht ni ecaps a htiw elif
To pipe input down to myScript
, use process substitution:
要将输入向下传递到myScript
,请使用进程替换:
$ ./myDemux <(md5sum /etc/issue) eussi/cte/ 01672098e5a1807213d5ba16e00a7ad0
Note that if you try to pipe the output directly, as in
请注意,如果您尝试直接通过管道传输输出,如
$ md5sum /etc/issue | ./myDemux
it will hang waiting on input from the terminal, whereas ephemient's answerdoes not have this shortcoming.
它将挂起等待来自终端的输入,而ephemient 的答案没有这个缺点。
A slight change produces the desired behavior:
轻微的变化会产生所需的行为:
#! /bin/bash
[ $# -gt 0 ] && input="'""'" || input=/dev/stdin
eval "./myScript <$input"
回答by Znik
people show to you very long scripts, but.... you get bash trap :) You must quote everything in bash. for example, you want list file named &0 .
人们向您展示很长的脚本,但是.... 您会遇到 bash 陷阱 :) 您必须在 bash 中引用所有内容。例如,您需要名为 &0 的列表文件。
filename='&0' #right ls $filename #wrong! this substitute $filename and interpret &0 ls "$filename" #right
filename='&0' #right ls $filename #wrong! 这个替代 $filename 并解释 &0 ls "$filename" #right
another, files with spaces.
另一个,带空格的文件。
filename=' some file with spaces ' ls $filename #wrong, bash cut first and last space, and reduce multiple spaces between with and spaces words ls "$filename" righ
filename=' 一些带有空格的文件 ' ls $filename #wrong,bash 切掉第一个和最后一个空格,并减少 with 和空格之间的多个空格 ls "$filename" 对
the same is in your script. please change:
在您的脚本中也是如此。请更换:
./myScript < $input
to
到
./myScript < "$input"
its all. bash has more traps. I suggest make quotation for "$file" with the same reason. spaces and other characters than can be interpreted are allways make problems.
它的全部。bash 有更多的陷阱。出于同样的原因,我建议为 "$file" 报价。空格和其他无法解释的字符总是会产生问题。
but what about /dev/stdin ? this is useable only when you redirected stdin and want to print something to real stdin.
但是 /dev/stdin 呢?仅当您重定向 stdin 并希望将某些内容打印到真正的 stdin 时,这才可用。
so, your script should show like this:
因此,您的脚本应显示如下:
[ ...some condition here... ] && input="$fileName" || input="&0"
./myScript < "$input"