Bash - Running multiple commands using string evaluation

泄露秘密 提交于 2019-12-11 06:42:43

问题


I'm trying to create a shell script that concatenates multiple commands to a string and then execute the commands.

The following succeeds (Creates the 'y' and 'z' files and prints ls output):

$ /bin/touch /z && ls && /bin/touch /y

But the following fails (Creates the 'y' and 'z' files, but also a '&&' and 'ls' files)

$ A="/bin/touch /z && ls && /bin/touch /y"
$ $A

It seems that by executing the line using $A the binary touch gets the rest of the string as parameters and the && operator does not execute as intended. (I also tried switching && with ;, || and such but got the same result)

I found out the eval $A does the trick, but I'm still curious as to why this happens. (And possibly want to skip the need to eval)

Thanks!


回答1:


Broadly speaking, there are two stages to evaluating a command line: parsing and expansion.


When you enter

/bin/touch /z && ls && /bin/touch /y

the string is first parsed into a preliminary series of words separated by whitespace:

  • /bin/touch
  • /z
  • &&
  • ls
  • &&
  • /bin/touch
  • /y

At this points, it recognizes the &&s as separating a list of simple commands consisting of the following words:

  1. /bin/touch, /z
  2. ls
  3. /bin/touch, /y

Now, it applies a set of expansions to each word in an attempt to produce more words. In this case, there is nothing to expand, and each simple command is examined again to identify the command itself and its arguments:

  1. Command /bin/touch, argument /z
  2. Command ls, no arguments
  3. Command /bin/touch, argument /y

In the case of $A, parsing is simple: there is a single word $A in the preliminary series. That word next undergoes expansion; the only relevant one is parameter expansion, which produces the same list of words as identified in the parsing step of the first example. The difference here is that list does not undergo further expansion; it's not time to identify the command name and its arguments.

  1. Command /bin/touch, with arguments...

    1. /z
    2. &&
    3. ls
    4. &&
    5. /bin/touch
    6. /y

A string is practically never the correct way to bundle up a set of commands. What you want to do is define a function

a () {
  /bin/touch /z && ls && /bin/touch /y
}

which you then use as an ordinary command:

$ a
/z
$ ls
/z
/y


来源:https://stackoverflow.com/questions/47288959/bash-running-multiple-commands-using-string-evaluation

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