问题:我目前在zsh-script中有一个这种结构的管道
a|b|c|d|e >result
我想修改它,因此如果某个变量$ v不为空,则文件result
应仅包含与$ v中的regexp匹配的那些行。以下是三种明显的方法:
# I don't like this because it causes an extra (possibly large)
# temporary file
a|b|c|d|e >result
if [[ -n $v ]]
then
mv result result1
grep -E $v result1 >result
rm result1
fi
或
# I don't like this, because I have to repeat the pipe,
# which makes it error-prone to maintain.
if [[ -z $v ]]
then
a|b|c|d|e >result
else
a|b|c|d|e|grep -F $v >result
fi
或者:
# I don't like this, because in practice, $v will be empty most
# of the time, and I then have an unnecessary additional process
# in the pipeline
if [[ -z $v ]]
then
filter=cat
else
filter='grep -E $v'
fi
a|b|c|d|e|${(z)filter} >result
我真正想要的是:
# This does not work
if [[ -z $v ]]
then
filter=
else
filter='|grep -E $v'
fi
a|b|c|d|e ${(z)filter} >result
这不起作用,因为Zsh首先识别各个命令,然后THEN评估参数,因此将我的过滤器命令作为参数传递给程序 e 。
我知道我也可以通过基于$ v的值构建我的命令行来解决这个问题,然后使用eval
,但我也不喜欢这样,因为在实际情况中,所有涉及的程序都会通过大量的引用来获得几个参数,并且将这个参数放在一个在eval上运行时仍然有效的形式也很容易导致错误。
在构建命令之前是否有不同的方法强制评估$ filter?我使用的是Zsh 5.3。
答案 0 :(得分:1)
您只需将管道输入 if
语句即可。您可以复制e
:
a | b | c | d | e |
if [[ -z $v ]]; then
e
else
e | grep -F $v
fi > result
或者以额外的cat
命令为代价,通过管道传递输出,将e
拉出if
:
a | b | c | d | e |
if [[ -z $v ]]; then
cat
else
grep -F $v
fi > result
或者您可以使用参数扩展来定义"接受任何内容"与grep
一起使用的模式。
a | b | c | d | e | grep -F ${v:-.} > result
答案 1 :(得分:0)
解决方案可能是使用fifo和文件描述符,比如
# open fd 3
if [[ -z $v ]]; then
exec 3> result
else
fifo=/tmp/fifo$$
mkfifo "$fifo"
grep -E "$v" "$fifo" >result & grep_pid=$!
exec 3>"$fifo"
end
a|b|c|d|e >&3
# close fd3 and clean
exec 3>&-
if [[ -n $v ]]; then
wait $grep_pid
rm "$fifo"
fi