语境:https://stackoverflow.com/a/37253784/15603477
cmd='printf "%s\n" "$(date -d "$variable" +%c)"'
echo ${variable}
回报2010-09-08 12:34:56
echo "$cmd"
返回printf "%s\n" $(date -d "$variable")
echo "${cmd}"
返回printf "%s\n" $(date -d "$variable")
然后运行以下命令bash -c "$cmd"
,如答案所示:
Fri
Dec
24
00:00:00
IST
2021
这是发现的结果:
- https://stackoverflow.com/questions/20858381/what-does-bash-c-do
- https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Invoking-Bash
从第一个非选项参数 command_string 读取并执行命令,然后退出。如果 command_string 之后有参数,则第一个参数将分配给 $0,任何剩余参数将分配给位置参数。对 $0 的赋值设置了 shell 的名称,该名称用于警告和错误消息。
我的问题是关于最后一个命令bash -c "$cmd"
,输出:今天(现在是2021年12月24日)来自哪里?
--更新:我检查过https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#eval,https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Bourne-Shell-Builtins,https://man7.org/linux/man-pages/man1/bash.1.html
以下引用是我发现的eval
eval [arg ...] 读取参数并将其连接在一起形成单个命令。然后 shell 读取并执行该命令,其退出状态作为 eval 的值返回。如果
答案1
shell代码bash -c "$cmd"
执行bash
带有 3 个参数的命令:
bash
-c
$cmd
变量的内容
bash
调用这样的 shell 是让它解释第三个参数中的代码的标准方法(每个 shell 都会这样做,而不仅仅是1)。
在这里,你说它$cmd
被初始化为:
cmd='printf "%s\n" "$(date -d "$variable" +%c)"'
但你的输出echo "$cmd"
显示它被分配为:
cmd='printf "%s\n" $(date -d "$variable" +%c)'
因此,新调用将解释的 shell 代码bash
是:
printf "%s\n" $(date -d "$variable" +%c)
现在,由于正在bash
启动一个新的解释器,因此$variable
不会将其分配给那里。如果您运行bash -uc
(与 相同bash -o nounset -c
)而不是-c
,您将得到:
bash: line 1: variable: unbound variable
如果没有该nounset
选项,未设置的变量将像空一样展开,因此date
使用以下参数调用该命令:
date
-d
- 空论
+%c
-d
不是标准date
选项。对于 GNU date
,它用于指定输入日期。当该输入日期为空时,相当于date -d 0
或date -d 00:00:00
,即今天早上 00:00:00。
现在,因为$(date...)
没有引用并且位于列表上下文中(在printf
命令的参数中),所以它受到 split+glob 的影响。分割部分破坏了$IFS
bash 中默认包含空格、制表符和换行符的日期输出,因此您会看到printf
为输出中的每个空格分隔的单词生成一个单独的参数date
(通配部分不此处执行任何操作,因为 ) 的输出中没有通配符date
。
因此,通过date
输出:Fri Dec 24 00:00:00 IST 2021
,将导致printf
使用这些参数进行调用:
printf
%s\n
Fri
Dec
00:00:00
IST
2021
并将尽可能多次地printf
重用该%s\n
格式来消耗所有参数,最终将所有参数打印在单独的行上。
现在,如果您希望该新bash
实例继承调用它的shell$variable
的 ,您可以export
将该变量添加到环境中,以便将来调用每个命令:
export variable
bash -c "$cmd"
(printenv variable
, perl -E 'say $ENV{variable}'
...也会看到它)。
或者只是为了对bash
³ 进行一次调用:
(export variable; exec bash -c "$cmd")
或者:
variable=$variable bash -c "$cmd"
在
eval "$cmd"
当前 shell 被告知解释 中的代码$cmd
,并且由于这是您在其中分配的 shell 调用$variable
,因此它将能够访问其内容,因此您不需要将其导出到环境中。
¹ 这也是一些其他语言的解释器的情况,这些语言不能真正被视为 shell 语言,例如python
.而其他一些人则使用不同的选项,例如sed -e 'sed code'
, perl -e 'perl code'
, php -r 'php code'
... 或根本不使用选项:awk 'awk code'
,sed 'sed code'
² 并$0
设置为bash
,并且此处位置参数列表为空。
bash
³ 以及该实例将自行执行的命令
答案2
该变量定义的命令$cmd
引用了一个名为 的变量$variable
。鉴于它$cmd
被传递给另一个进程中的新 shell 调用,因此在$cmd
执行时未设置此变量。因此,获取日期的结果调用是date -d "" +%c
,它返回当前日期。
从编码的角度来看,将命令存储在变量中是不好的做法。你将要迟早会陷入一场引用噩梦。使用变量作为数据,使用函数作为代码。
cmd() {
local v=$1
printf "%s\n" "$(date -d "$v" +%c)"'
}
variable="2010-09-08 12:34:56"
cmd "$variable"
最后,除非您有充分的理由,否则在使用变量时始终确保它们位于双引号内。 (echo $variable
很差;echo "$variable"
更好;printf "%s\n" "$variable"
最好。)