Shell:删除“eval”以支持可以托管外部变量的东西,它本身就是一个变量并包含一个子shell

问题描述 投票:0回答:2

我读过这个:Parentheses for subshell don't work when stored in a variable和这个:http://mywiki.wooledge.org/BashFAQ/048,他们一直很有洞察力和指导性,我很喜欢阅读它们。

我没有找到一个解决方案来解决一个经常出现问题的问题,即想要在shell中重构代码eval,直到我想把子shell投入混合,可能(肮脏)回答。

因此,我被迫同意eval是邪恶的,并且在安全缺陷的基础上存在技术缺陷。

你如何解决以下问题:

  1. 根据一个switch-case将你分成不同的linux发行版,你在另一个终端中打开的脚本有不同的第二个线程,并且具有匹配的close语法。
  2. 正文是相同的,但确实使用填充在第一个线程中的变量。并不是一个单一的命令
  3. 身体本身运行着子壳
  4. 替换解决方案不需要是顺序的,而是“同步”,因为eval是。 (它不会使尾随命令等待它返回执行)

这是我们得到的:

我们的if和elif切换器启动我们的脚本第二个终端或“第二个线程”:

if [[ "$operating_system" = "Ubuntu" || "$operating_system" = "Debian GNU/Linux" ]]
then
    eval "$gnome_opening_faf_script $faf_script $gnome_closing_faf_script"
elif [ "$operating_system" = "Kubuntu" ]
then
    eval "$konsole_opening_faf_script $faf_script $konsole_closing_faf_script"
elif [ "$operating_system" = "elementary OS" ]
then
    eval "$io_opening_faf_script $middlescript $io_closing_faf_script"
else
    eval "$xterm_opening_faf_script $faf_script $xterm_closing_faf_script"
fi

第二个线程的主体变量:

faf_script='echo "expecting you to type in Forged Alliances Launch options";
echo "reminder : look in your home folder, theres a file there with the contents to be pasted";
echo "once thats done edit steam settings in order to enable Proton for all games";
steam -login '$steam_user_name' '$steam_password' -applaunch 9420 &
echo "waiting for Forged Alliance to be installed, Game.prefs to exits and Forged Alliance to be shut down";
echo "you may also type \"continue\" to exit this while loop"
echo -n "if you feel the conditions for continuing sucessfully have been met... ";
( i=1;
sp="/-\|";
no_config=true;
while $no_config;
do printf "\b${sp:i++%${#sp}:1}";
[[ ! $(pidof SupremeCommande) && -f $origin/steamapps/compatdata/9420/pfx/drive_c/users/steamuser/Local\ Settings/Application\ Data/Gas\ Powered\ Games/Supreme\ Commander\ Forged\ Alliance/Game.prefs ]] && no_config=false;
sleep 1;
done;
kill $$;
) &;
child_pid=$!;
while $no_config;
do read -r typed_continue;
[[ "$typed_continue" = "continue" ]] && no_config=false;
sleep 1;
done;
kill $child_pid;
echo "";
'

并打开一个结束变量,允许脚本的第二个线程由不同的终端运行,具体取决于分发。

gnome_opening_faf_script='gnome-terminal --tab --active --title="install & run steam, steamcmd, FA" -- bash -c '"'"''
konsole_opening_faf_script='konsole -e /bin/bash --rcfile <(echo '"'"''
io_opening_faf_script='io.elementary.terminal -e "bash -c '"'"'curl  wttr.in/bydgoszcz'"'"';'"'"'sleep 3'"'"''
xterm_opening_faf_script='xterm -T "install & run steam, steamcmd, FA" -e '"'"''

gnome_closing_faf_script='gnome-terminal -- bash -c "cd faf; ./downlords-faf-client";'"'"''
konsole_closing_faf_script='konsole -e /bin/bash --rcfile <(echo "cd faf; ./downlords-faf-client; exit 0") &'"'"') &'
io_closing_faf_script='io.elementary.terminal -e "cd faf; ./downlords-faf-client";'"'"''
xterm_closing_faf_script='xterm -T "FAF" -e "cd faf; ./downlords-faf-client";'"'"' &'

通常,当人们建议替换eval时,情境就会被简化。 eval正在运行一个echo "hello world"

这不是我的情况,我已经能够不应用任何解决方案。

bash shell sh eval subshell
2个回答
2
投票

让我们从您想要做的事情的高级概述开始:在特定于操作系统的终端仿真器的单独选项卡中运行一些任意脚本(包含在字符串中)。脚本的内容并不重要,所以让我们说它们有两个变量:

faf_script='...'
download_script='...'

现在,我们想要一个看起来像这样的函数:

run_scripts () {
    for script in "$@"; do
        run_in_tab "$script"
    done
}

其中run_in_tab是一个特定于操作系统的函数,它在所需终端仿真器的新选项卡中运行其参数。也许它实际上没有打开新的标签;也许它打开了一个全新的窗口,但run_scripts并不关心这一点。它只需要一个以用户可以与之交互的方式运行shell脚本的函数。

接下来,我们实际上以特定于操作系统的方式定义run_in_tab

case $operating_system in
  Ubuntu|"Debian GNU/Linux")
    run_in_tab () {
      gnome-terminal .....
    } ;;

  Kubuntu)
    run_in_tab () {
        konsole ......
    } ;;

  "elementary OS")
    download_script='...'   # For whatever reason, this is different; override it
    run_in_tab () {
        konsole ......
    } ;;

  *) run_in_tab () {
       xterm ....
     } ;;
esac

一旦完成,我们只需调用run_scripts

run_scripts "$faf_script" "download_script"

0
投票

一个替代方案基本上与eval做同样的事情,但在这样的复杂情况下更容易(至少对我而言):

goDo() {
  local tmp=$(mktemp) # create a guaranteed unique temporary filename
  echo "$1" >| $tmp   # send the passed-in string to that file
  . $tmp              # *source* the file back into the local context
}

“采购”一个文件就意味着读取它就好像它的实际内容已被输入而不是输入它的行 - 几乎是字面上的。维护调用脚本的上下文。陷阱,变量,函数,别名 - 调用脚本中可用的所有内容都可用于被调用的代码。这实际上是被调用脚本可以在调用者中设置变量的唯一方法,因为这不会在子shell中运行代码。

如果有任何问题,您可以调试整个文件。我建议在前几次测试运行中设置set -x调试,以确保您看到了您的期望。

© www.soinside.com 2019 - 2024. All rights reserved.