是否可以提前强制zsh参数扩展?

时间:2018-01-12 12:21:14

标签: zsh

问题:我目前在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。

2 个答案:

答案 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