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

拜拜、爱过 提交于 2020-01-23 03:27:25

问题


If i use this command in pipeline, it's working very well;

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

But if I want to set grep into variable like:

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

And if I put variable in pipeline;

pipeline ... | $var

nothing happens, like there isn't any matches.

Any help what am I doing wrong?


回答1:


The robust way to store a simple command in a variable in Bash is to use an array:

# 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[@]}"

If, by contrast, your command is more than a simple command and, for instance, involves pipes, multiple commands, loops, ..., defining a function is the right approach:

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

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

Why what you tried didn't work:

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

causes the single quotes around the regex to become a literal, embedded part of $var's value.

When you then use $var - unquoted - as a command, the following happens:

  • Bash performs word-splitting, which means that it breaks the value of $var into words (separate tokens) by whitespace (the chars. defined in special variable $IFS, which contains a space, a tab, and a newline character by default).

    • Bash also performs globbing (pathname expansion) on the resulting works, which is not a problem here, but can have unintended consequences in general.
    • Also, if any of your original arguments had embedded whitespace, word splitting would split them into multiple words, and your original argument partitioning is lost.
      • (As an aside: "$var" - i.e., double-quoting the variable reference - is not a solution, because then the entire string is treated as the command name.)
  • Specifically, the resulting words are:

    • grep
    • -P
    • '^[^\s]*\s3\s' - including the surrounding single quotes
  • The words are then interpreted as the name of the command and its arguments, and invoked as such.

    • Given that the pattern argument passed to grep starts with a literal single quote, matching won't work as intended.

Short of using eval "$var" - which is NOT recommended for security reasons - you cannot persuade Bash to see the embedded single quotes as syntactical elements that should be removed (a process appropriate called quote removal).

Using an array bypasses all these problems by storing arguments in individual elements and letting Bash robustly assemble them into a command with "${cmd[@]}".




回答2:


What you are doing wrong is trying to store a command in a variable. For simplicity, robustness, etc. commands are stored in aliases (if no arguments) or functions (if arguments), not variables. In this case:

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

I recommend you read the book Shell Scripting Recipes by Chris Johnson ASAP to get the basics of shell programming and then Effective Awk Programming, 4th Edition, by Arnold Robbins when you're ready to start writing scripts to manipulate text.



来源:https://stackoverflow.com/questions/36813565/how-do-i-store-a-command-in-a-variable-and-use-it-in-a-pipeline

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!