如何将命令存储在变量中并在管道中使用它?

How do I store a command in a variable and use it in a pipeline?

如果我在管道中使用这个命令,它工作得很好;

pipeline ... | grep -P '^[^\s]*\s3\s'

但是如果我想将 grep 设置为如下变量:

var="grep -P '^[^\s]*\s3\s'"

如果我将变量放入管道中;

pipeline ... | $var

没有任何反应,就像没有匹配项一样。

任何帮助我做错了什么?

您做错的是试图将命令存储在变量中。为了简单、健壮等,命令存储在别名(如果没有参数)或函数(如果有参数)中,而不是变量。在这种情况下:

$ alias foo='grep X'
$ echo "aXb" | foo
aXb

我建议您尽快阅读 Chris Johnson 撰写的 Shell Scripting Recipes 一书,了解 shell 编程的基础知识,然后在准备好后阅读 Arnold Robbins 撰写的 Effective Awk Programming,第 4 版开始编写脚本来操作文本。

简单命令存储在Bash变量中的可靠方法是使用一个数组:

# Store the command names and arguments individually
# in the elements of an *array*.
cmd=( grep -P '^[^\s]*\s3\s' )

# Use the entire array as the command to execute - be sure to
# double-quote ${cmd[@]}.
echo 'before 3 after' | "${cmd[@]}"

相比之下,如果您的命令 不仅仅是 简单 命令,例如,涉及管道、多个命令、循环, ..., 定义一个函数是正确的方法:

# Define a function encapsulating the command...
myGrep() { grep -P '^[^\s]*\s3\s'; }

# ... and use it:
echo 'before 3 after' | myGrep

为什么您尝试的方法不起作用:

var="grep -P '^[^\s]*\s3\s'"

使正则表达式周围的单引号成为 文字,嵌入 $var 值的一部分。

当您随后使用 $var - 未加引号 - 作为命令 时,会发生以下情况:

  • Bash 执行分词,这意味着它将 $var 的值通过 whitespace (chars.在特殊变量 $IFS 中定义,默认包含一个 space、一个制表符和一个换行符)。

    • Bash 还会对生成的作品执行 globbing(路径名扩展),这在这里不是问题,但通常会产生意想不到的后果。
    • 此外,如果您的任何原始论点有嵌入白色space,分词会将它们分成多个个词,并且您的原始参数分区丢失了。
      • (顺便说一句:"$var" - 即 双引号 变量引用 - 不是 解决方案,因为 entire 字符串被视为命令 name.)
  • 具体来说,得到的词是:

    • grep
    • -P
    • '^[^\s]*\s3\s' - 包括周围的单引号
  • 然后这些词被解释为命令的名称及其参数,并照此调用。

    • 鉴于传递给 grep 的模式参数以 文字 单引号开头,匹配将无法按预期进行。

不使用 eval "$var" - 出于安全原因不推荐 - 您无法说服 Bash 将 嵌入的 单引号视为 应该删除的语法 元素(一个适当的过程称为删除引号)。

使用 array 通过将参数存储在单个元素中并让 Bash 稳健地 assemble 将它们放入带有 [=23= 的命令中来绕过所有这些问题].