Bash中的进程替换字符串

huangapple go评论60阅读模式
英文:

Bash process substitution in string

问题

我正在尝试向脚本添加一个开关,该开关将使内部命令的一个参数变为可选。通常,我会通过使用条件语句来运行带参数或不带参数的命令,或者如果参数很简单,只需创建一个包含该参数的字符串并进行替换来实现这一点。如果不需要参数,则字符串为空。

我有一个额外的复杂性,就是有一个仅打印命令而不运行它的干运行模式,因此已经有一个条件语句。因此,我只想将内部参数创建为字符串,然后将其替换为干运行的echo命令或实际命令中。然而,选项有点复杂,看起来像这样:

<(echo $msg_enc | base64 -d -)

这会接受一个以base64编码的字符串,解码它并将内容呈现为一个临时文件,可以读取。我不能轻松改变这个结构,所以我正在尝试弄清楚如何将它保留在字符串中并进行评估。为此,我创建了一个Shell脚本call_just_print.sh和另一个小的Shell脚本来模仿命令,just_print.sh

call_just_print.sh

msg="hello world"
msg_enc="$(echo $msg | base64)"

dry_run=0
if [[ "${1}" == "dry-run" ]]; then
    dry_run=1
fi

enc_cmd=" <(echo $msg_enc | base64 -d -)"
if [[ "${dry_run}" == "1" ]]; then
    echo "./just_print.sh ${enc_cmd}"
else
    ./just_print.sh "${enc_cmd}"
fi

这只是从命令行中获取一个干运行提示,如果设置了该标志,则仅打印它即将运行的命令,否则将运行命令。

just_print.sh

echo "[$(cat ${1})]"

这个简单地接收一个参数,假设它是一个文件,并将其显示在终端上。理想情况下,当没有干运行模式时,输出应该打印出[hello world]。但当我运行它时,我得到以下结果:

$ ./call_just_print.sh dry-run
./just_print.sh  <(echo $msg_enc | base64 -d -)
$ ./call_just_print.sh
cat: invalid option -- 'd'
Try 'cat --help' for more information.
[]

干运行模式可以正常工作,但在没有标志的情况下使用时,msg_enc 变量被原样传递给 just_print.sh,导致运行以下命令:cat '<(echo' '$msg_enc' '|' base64 -d '-'),这绝对不正确。

我尝试在调用中使用 eval,如下所示:

./just_print.sh "$(eval ${enc_cmd})"

但我发现,虽然这会导致正确评估变量以创建临时文件,但然后会尝试执行它:

./call_just_print.sh: line 15: /dev/fd/63: Permission denied

有谁能告诉我我哪里错了?

英文:

I'm trying to add a switch to a script which will make a parameter on an internal command optional. Normally, I'd do this by either using a conditional to run the command with the parameter or without it, or if the parameter is simple, just create a string with the parameter in and substitute that. If the parameter is not needed then the string is just empty.

I have the added complexity that there's a dry run mode which just prints the command rather than running it so there's already one conditional. For this reason, I just wanted to create the internal parameter as a string and then substitute it either in the echo of the dry run, or the real command. However, the option is a little complex and looks like this:

&lt;(echo $msg_enc | base64 -d -)

This takes a string encoded in base64, decodes it and presents the contents as a temporary file which can be read. I can't really change this structure easily, so I'm trying to work out how I can keep this in a string and evaluate it. To do this I created a shell script, call_just_print.sh and another tiny shell script to mimic the command, just_print.sh

call_just_print.sh

msg=&quot;hello world&quot;
msg_enc=&quot;$(echo $msg | base64)&quot;

dry_run=0
if [[ &quot;&quot; == &quot;dry-run&quot; ]]; then
    dry_run=1
fi

enc_cmd=&quot; &lt;(echo $msg_enc | base64 -d -)&quot;
if [[ &quot;${dry_run}&quot; == &quot;1&quot; ]]; then
    echo &quot;./just_print.sh ${enc_cmd}&quot;
else
    ./just_print.sh &quot;${enc_cmd}&quot;
fi

This really just picks up a dry run hint from the command line and, if set, simply prints the command it's going to run, or if not set, will run the command.

just_print.sh

echo &quot;[$(cat )]&quot;

This trivially receives a parameter which it assumes to be a file and displays it to the terminal. Ideally, the output would print [hello world] when run without dry-run mode. When I run it, I get the following:

$ ./call_just_print.sh dry-run
./just_print.sh  &lt;(echo $msg_enc | base64 -d -)
$ ./call_just_print.sh
cat: invalid option -- &#39;d&#39;
Try &#39;cat --help&#39; for more information.
[]

The dry-run mode works fine but when used without the flag, the msg_enc var is passed verbatim to just_print.sh resulting in the following being run: cat &#39;&lt;(echo&#39; &#39;$msg_enc&#39; &#39;|&#39; base64 -d &#39;-)&#39; which is absolutely not right.

I tried using eval in the call as follows:

./just_print.sh &quot;$(eval ${enc_cmd})&quot;

but I found that while this results in the correct evaluation of the variable resulting in creation of the temporary file, it then tries to execute it:

./call_just_print.sh: line 15: /dev/fd/63: Permission denied

Can anyone tell me where I'm going wrong?

答案1

得分: 3

将以下部分进行翻译:

./just_print.sh &quot;${enc_cmd}&quot;

改为

eval &quot;./just_print.sh ${enc_cmd}&quot;

以便将变量扩展的结果作为shell代码进行评估。否则,它将被视为just_print.sh的文字参数。

在使用eval时要非常小心 - 要执行的代码应该由您的脚本生成,而不是由用户提供的。

英文:

Change

    ./just_print.sh &quot;${enc_cmd}&quot;

to

    eval &quot;./just_print.sh ${enc_cmd}&quot;

so it will evaluate the result of the variable expansion as shell code. Otherwise it's treated as a literal argument to just_print.sh.

Be very careful when using eval -- the code to be executed should be generated by your script, not user-provided.

答案2

得分: 1

msg="hello world"
msg_enc="$(echo $msg | base64)"

dry_run="eval"
if [[ "${1}" == "dry-run" ]]; then
    dry_run="echo"
fi

enc_cmd=" <(echo $msg_enc | base64 -d -)"
$dry_run "./just_print.sh ${enc_cmd}"
英文:
msg=&quot;hello world&quot;
msg_enc=&quot;$(echo $msg | base64)&quot;

dry_run=&quot;eval&quot;
if [[ &quot;${1}&quot; == &quot;dry-run&quot; ]]; then
    dry_run=&quot;echo&quot;
fi

enc_cmd=&quot; &lt;(echo $msg_enc | base64 -d -)&quot;
$dry_run &quot;./just_print.sh ${enc_cmd}&quot;

You were on the right track with eval, but you over-complicated it. You just needed to restructure it so that the whole command was evaled.

I also simplified your logic so that dry run commands and executed commands are unified, avoiding ugly and error-prone if blocks.

huangapple
  • 本文由 发表于 2023年7月7日 02:33:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76631657.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定