【问题标题】:How do I echo a command line before executing it?如何在执行之前回显命令行?
【发布时间】:2017-04-13 13:19:07
【问题描述】:

我需要执行命令,我需要显示我执行了哪个命令。所以我一直在尝试将命令存储在变量中并执行该变量。在显示输出时,我正在打印变量值,如下所示。

var="ls -ltr";
echo "$var \n `$var`";

上面一个是我想要的输出,但是当我使用下面提到的命令时,它给我一个错误。有人可以建议我如何存储下面提到的命令。

var="ls -ltr|wc -l";
echo "$var \n `$var`";

输出错误:

ls -ltr | wc -l
ls: cannot access |: No such file or directory
ls: cannot access wc: No such file or directory

将命令存储在变量中的正确方法是什么,如下面的命令。或给我任何替代建议来满足我的要求。

cat passwd.txt|tr ' ' '\n'|egrep -i "$pwd_in"|sort

【问题讨论】:

  • 真的仔细阅读上面的链接。在变量中存储命令,几乎在所有情况下都是错误的想法......
  • @tripleee:这个问题是关于想要在执行任意命令行之前回显它。链接的问题是关于为什么由 shell 扩展产生的引号没有语法意义。虽然存在某种关系,但它们不是重复的。
  • 还不错;感谢您的反馈。对于历史背景,我建议的重复问题是stackoverflow.com/questions/12136948/…

标签: bash ksh


【解决方案1】:

将您的命令放入函数中并启用xtrace

cmd ()
{
    set -x
    ls -ltr | wc -l
    set +x
}

【讨论】:

    【解决方案2】:

    您的方法不起作用的原因是 shell 元字符(例如 |)在 嵌入 字符串中时失去了它们的语法意义强>。 this related question 的答案解释了原因;这个问题是关于嵌入的引号,但答案仍然类似。
    另外,它是generally ill-advised to store commands in variables;简而言之:

    • 如果没有 - 不明智 - 使用 eval,它仅适用于 简单 命令(在控制运算符的帮助下不由多个命令组成的命令,例如 |&&),
    • 只有当您将命令的参数存储在一个 数组 中时,才能稳健地运行。

    得到你想要的,没有办法绕过使用eval,即generally discouraged for security reasons

    • 简而言之:只有在您完全控制或信任您传递给eval 的字符串时才使用eval;也就是说,eval与变量$var (eval "$var") 结合使用是可接受的,当且仅当
      • 完全控制分配给$var的值,知道它是一个命令,并且知道它按预期工作(如果该值的部分是由外部来源提供,下一点适用)
      • 信任任何外部贡献对$var 价值的来源(无论是全部还是部分形成$var 的价值) - 具体而言,您需要确保不会尝试恶意提供/注入不需要的命令。

    最可预测的基于eval 的解决方案如下,它改进了AxT_8041's answer(适用于bashksh):

    注意:由于这个命令是构造的,作为一个文字,你完全控制了命令字符串,所以eval是可以接受的在这种情况下

    cmdLine='ls -ltr|wc -l'
    printf "%s\n" "$cmdLine"; eval "$cmdLine"
    

    使用更复杂的命令行来引用命令会变得很棘手,在这种情况下,您可以使用 here-document

    # Read the command into variable $cmdLine.
    read -d '' -r cmdLine <<'EOF'
    cat passwd.txt|tr ' ' '\n'|egrep -i "$pwd_in"|sort
    EOF
    printf "%s\n" "$cmdLine"; eval "$cmdLine"
    

    注意:虽然这个命令也是你自己构造的,但它是由变量$pwd_in的值合并而成的,所以为了在这里安全地使用eval,你必须知道或信任 $pwd_in 具有预期的值,特别是它不包含注入命令的尝试


    安全但有限的解决方案使用外壳提供的诊断功能

    bash 的情况下,set -v 在执行之前自动回显每个原始命令行(无需先在变量中定义感兴趣的命令):

    set -v            # Turn echoing of raw command lines on.
    ls -ltr | wc -l   # Execute the command of interest.
    set +v            # Turn back off.
    

    这会产生,例如:

    ls -ltr | wc -l
    15
    set +v
    

    但是,有一些限制:

    • Bash 回显的原始命令行发送到 stderr

      • 请注意,ceving's answer 中的 set -x 是一个相关功能,但它的工作方式不同:它将单独回显简单命令(ls -ltr 和@987654354 @) 以已经扩展的形式组成管道。
    • 据我所知,set +v 输出行无法被抑制。

    您可以尝试解决这些问题,但这并不容易或漂亮。

    【讨论】:

      【解决方案3】:

      以下正在开发 Bash。试试这个:

      var="ls -ltr|wc -l";
      echo -e "$var \n `eval $var`";
      
      Output:
      ls -ltr|wc -l 
      10
      

      【讨论】:

      • 你在 $var 中存储了什么,如何存储?
      • 和你存储的一样。
      • @SriramP 使它起作用的是eval 的使用,它将字符串评估为命令行。
      • 拒绝推荐 eval 而没有通常的警告,尤其是在 echo -e 内的反引号中隐藏它,谁知道你在邀请什么意外行为或后果。
      【解决方案4】:

      如果速度不是问题,值得考虑的奖励选项:

      (set -x; ls -l "$PWD")
      

      这将生成一个继承父 shell 的所有属性的子 shell,但好处是您不必担心在要打印的每个语句的末尾使用 set +x 进行清理。

      【讨论】:

        猜你喜欢
        • 2021-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-09
        • 1970-01-01
        • 2017-05-21
        相关资源
        最近更新 更多