【问题标题】:Word not expanding into shell command in bashWord没有在bash中扩展到shell命令
【发布时间】:2019-05-22 04:26:27
【问题描述】:

有人可以帮忙吗:

我想在 shell 脚本中执行一些命令,方法是从另一个文本文件中读取它们,如下所示:

command_file.txt 包含:

EXECUTED_OK_$(date)
EXECUTED_OK_$(hostname)

shell script.sh

#!/bin/sh

while read command
do
echo $command 
done < command_file.txt

我的问题是,echo 打印为命令文件中的文本。 如何使其扩展并打印实际日期和主机名。

例如 预期输出应该是:

EXECUTED_OK_22-02-2010 11:10:10
EXECUTED_OK_localhost

但我得到了:

EXECUTED_OK_$(date)
EXECUTED_OK_$(hostname)

【问题讨论】:

  • 考虑envsubst 是否可用。
  • 您看到的行为是期望的并且符合规范。如果数据被扩展为“背后”的代码,那么在 bash 中编写处理不受信任数据的代码基本上是不可能的。
  • (再一次,您基本上是在请求您的数据作为代码执行,这正是eval 的用途。这也是一个普遍的坏主意,最好避免,但是...嗯,它你要求的)。
  • @Charles Duffy 如果我们考虑必须在运行时创建动态命令的情况,这可能比运行单个命令并连接最终输出更简单。
  • ...但是如果你想要eval的效果,你为什么不实际使用 eval?这是不好的做法,但那是因为它完成的事情(突破数据和代码之间的界限)是不好的做法。你正在积极地要求那个东西(将你的数据评估为代码,相信它的内容可以安全执行),所以,如果你真的,真的确定,不妨使用它这就是你想要的。

标签: linux bash shell scripting


【解决方案1】:

这种白名单方法不允许像eval 那样执行任意代码。如果安全不是问题,您应该坚持使用eval,因为此示例中使用的外部有许多极端情况,替换无法按预期工作。

在打印命令之前,程序会将命令文件的每个命令中引用的某些外部参数替换为它们的输出:

#!/bin/bash

while read command
do
    while read -a external ; do
        output=$( "${external[@]}" )
        command=$( sed "s/\$(${external})/${output}/g" <<< "$command" )
    done <<END
    date -R
    hostname
END
    echo "$command"
done < command_file.txt

要使用更多外部组件,您需要将它们添加到&lt;&lt;ENDEND 之间的heredoc。我已将-R 添加到date 以展示带有参数的外部。

正如已经指出的那样,除了分词之外,不会对外部命令进行任何处理(例如引号、替换)(空格可以转义 (one\ two) 而不是引用)。

【讨论】:

  • $($anything) 不完全等同于$(eval "$anything");见BashFAQ #50
  • ...类似地,read -a external 不处理引号 &c。与 shell 解析的方式相同,因此仅切换到 "${external[@]}" 并不能真正解决未引用 $anything 错误的情况。 (以touch 'hello world' 为例;不知道引用的字符串拆分将让touch 创建两个单独的文件,'helloworld')。
  • @CharlesDuffy 谢谢,那将不适用于参数。我修改了我的示例以使用数组。
【解决方案2】:

使用eval展开变量并重新执行。

$ foo='EXECUTED_OK_$(date)'
$ echo $foo
EXECUTED_OK_$(date)
$ eval "echo $foo"
EXECUTED_OK_Tue May 21 12:39:45 PDT 2019

编辑:eval 功能强大但可能很危险;它基本上是代码注入。引用论点是个好主意;有关详细说明,请参阅下面的 cmets。

【讨论】:

  • eval "echo $foo" 少了一些错误(或者至少,少了一点关于它的工作原理的误导)。例如,如果在 foo='*' 时运行 eval echo $foo 时不带引号,则* 会被替换为文件名列表之前 eval 运行,使其行为几乎无法预测不知道这些名字。
  • (如果你有foo='*'; eval echo $foo,你最好希望没有人用touch '$(rm -rf ~)'创建文件;而foo='*'; eval "echo $foo"*之后被扩展> eval,所以它扩展的文件名不能作为代码执行)。
  • 顺便说一句,请参阅 BashPitfalls #14,了解为什么即使在 echo "$foo" 中引用也是一个好主意。
【解决方案3】:

一种选择是使用eval,假设您信任输入文件

#!/bin/sh

while read command; do 
  eval "echo $command" 
done < command_file.txt

【讨论】:

    猜你喜欢
    • 2017-12-11
    • 1970-01-01
    • 2014-01-28
    • 1970-01-01
    • 2018-08-06
    • 2013-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多