从命令行传递字符串时Bash怪异

时间:2015-01-30 13:15:18

标签: bash shell scripting sh

这是我为展示古怪行为而创建的一个例子。 我希望bash按原样传递引用的命令行参数。

john@doe:~/tmp$ cat script.sh 
#! /bin/bash
set -o xtrace
$1 sleep 3
john@doe:~/tmp$ ./script.sh "echo"
+ echo sleep 3
sleep 3
john@doe:~/tmp$ ./script.sh "echo -n"
+ echo -n sleep 3
sleep 3john@doe:~/tmp$

但有时bash编辑我的字符串参数,并在符号周围添加刻度 喜欢 ;或&&

john@doe:~/tmp$ ./script.sh "echo hello ;"
+ echo hello ';' sleep 3
hello ; sleep 3
john@doe:~/tmp$ ./script.sh "echo hello && "
+ echo hello '&&' sleep 3
hello && sleep 3

这个bash规则是什么以及如何规避它?非常感谢:))

2 个答案:

答案 0 :(得分:1)

规则是引用元字符,否则这些元字符将有不同的解释。分号是语句分隔符,&&也是如此。引用可以轻松剪切和粘贴特定行并使其重现原始效果

想想

的区别
echo hello '&&'

echo hello &&

第一个回声两个字,第二个等待更多输入。你称之为quirky的引用按照你的设计工作,就是你真正想要的,不,除了黑客攻击源代码之外没有办法解决它。

答案 1 :(得分:1)

手册中概述了该规则:Section Shell Operation

你会读到:

  

以下是shell操作时的简要说明   读取并执行命令。基本上,shell执行以下操作:

     
      
  1. 从文件中读取其输入(请参阅Shell Scripts),从作为参数提供的字符串读取-c调用选项(请参阅Invoking Bash),或从用户终端读取。 / LI>   
  2. 将输入分解为单词和运算符,遵守Quoting中描述的引用规则。这些令牌被分开   元字符。别名扩展由此步骤执行(请参阅   Aliases)。
  3.   
  4. 将令牌解析为简单和复合命令(请参阅Shell Commands)。
  5.   
  6. 执行各种shell扩展(请参阅Shell Expansions),将扩展的标记分解为文件名列表(请参阅Filename Expansion)以及命令和参数。
  7.   
  8. 执行任何必要的重定向(请参阅Redirections)并从参数列表中删除重定向运算符及其操作数。
  9.   
  10. 执行命令(参见Executing Commands)。
  11.   
  12. (可选)等待命令完成并收集其退出状态(请参阅Exit Status)。
  13.   

所以,你用脚本:

#! /bin/bash

$1 sleep 3

执行./script "echo hello ;"时会发生什么?

  1. Bash会读取您的脚本。
  2. 它将输入分解为单词和运算符。请注意,此时,Bash没有看到参数;中的控件操作符echo hello ;。 此时,它只会看到三个令牌$1sleep3
  3. 它将令牌解析为简单和复合命令。目前还没有简单的命令或复合命令。
  4. 执行shell和文件名扩展:此时,您的令牌$1已扩展为echohello;(而不是此处的分号一个参数,它不被理解为控制操作符,因为控制操作符的扫描已经在步骤2中完成,不再执行了。)
  5. 这里没有重定向。
  6. Bash现在执行命令:它会看到带有参数echohello;sleep的命令3。所以它很高兴地回应hello ; sleep 3
  7. 执行echo后收集退出状态(非常可能成功)。
  8. 有两种方法可以避免这种情况:第一种方法是使用eval,但这很危险并且有许多警告:用

    替换你的脚本
    #!/bin/bash
    
    eval "$1 sleep 3"
    

    使用此脚本:

    $ ./script "echo hello;"
    hello
    $
    

    (延迟3秒后显示最终提示)。 ./script "echo hello &&"也是如此。 但这不是一个好主意。请继续阅读。

    作为一般规则,你应该避免(至少在Bash中)混合数据和代码:变量在这里保存数据(字符串),而不是代码。所以这种技术不是很好。我说这不是一个好的,不仅因为有人曾经说过你不应该将数据与代码混合在一起,而是因为如果你开始在你的“代码”中使用特殊字符,它就会变得无法使用。例如,如果您想回显*符号,则必须使用笨重的调用./script "echo \"*\";"。还有许多其他警告。

    代码保存在函数或脚本中。使用函数:

    say_hello_and_execute() {
        echo hello; "$@"
    }
    

    然后导出此函数:export -f say_hello_and_execute并将其用作脚本的参数:./script say_hello_and_execute

    使用脚本可以实现同样的目的:调用以下脚本say_hello_and_execute

    #!/bin/bash
    
    echo hello; "$@"
    

    然后chmod +x say_hello_and_execute并以./script ./say_hello_and_execute运行您的脚本。


    这个答案试图回答你的问题。但是,如果你能准确地告诉我们你想要实现的目标会更好,因为可能有更好的设计来实现这一目标。