【问题标题】:How do I store a command in a variable and use it in a pipeline? [duplicate]如何将命令存储在变量中并在管道中使用? [复制]
【发布时间】:2016-08-17 05:59:49
【问题描述】:

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

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

但如果我想将 grep 设置为变量,例如:

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

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

pipeline ... | $var

什么都没有发生,就像没有任何匹配一样。

任何帮助我做错了什么?

【问题讨论】:

  • 顺便说一句:您使用管道这一事实与您的问题无关。

标签: bash variables grep pipeline


【解决方案1】:

在 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 的值通过空格(在特殊变量$IFS 中定义的字符)分解为单词(单独的标记),其中包含一个空格、一个制表符和一个默认换行符)。

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

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

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

没有使用eval "$var" - 出于安全原因不建议这样做 - 你不能说服 Bash 将 embedded 单引号视为应该删除的 syntactical 元素 (一个适当的过程称为引号删除)。

使用 数组 绕过所有这些问题,方法是将参数存储在单个元素中,并让 Bash 使用"${cmd[@]}" 将它们稳健地组装成一个命令。

【讨论】:

  • 感谢您的解释和我的回答。
  • @sjsam:感谢您的反馈。后来我发现$(echo "$var") 实际上与 OP 自己的方法没有什么不同(只是$var)——shell 的处理方式最终是相同的。
  • 我最喜欢有功能的idea,tnx!
【解决方案2】:

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

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

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

【讨论】:

  • 虽然使用 alias 是一种选择,但默认情况下,别名扩展在脚本中 disabled 的事实使这个解决方案很尴尬:你会有首先在你的脚本中运行shopt -o expand_aliases
  • @mklement0 是什么让您认为 OP 想要将其放入脚本中?
  • 虽然您当然可以在交互式 shell 中定义变量甚至函数,但它们通常在脚本中使用。无论 OP 的具体用例是什么,都没有指定该方面,因此值得指出的是,在脚本中别名默认不会被扩展
  • 我的别名是在 .env 或 .profile 文件中定义的,我只是在我的交互式 shell 中使用它们。我不在任何脚本中使用它们,也不需要在脚本中定义它们。我想是 YMMV。
  • 总结一下:++ 用于您的别名方法,以涵盖在 interactive shell 中的使用;在您的答案和我的答案之间,我们现在涵盖了两个用例(交互式使用与在脚本中使用)。但是,我建议使用 OP 的确切命令显示别名定义,以突出潜在的引用问题。
猜你喜欢
  • 1970-01-01
  • 2013-11-25
  • 1970-01-01
  • 2014-10-21
  • 1970-01-01
  • 2011-02-13
  • 1970-01-01
  • 2013-07-20
  • 2015-03-10
相关资源
最近更新 更多