Reputation: 2536
I've read this : Parentheses for subshell don't work when stored in a variable and this : http://mywiki.wooledge.org/BashFAQ/048 and they've been insightfull and instructive and I've enjoyed reading them.
I did not find therin a solution to a reccurent problem of wanting refactored code in shell that eval
, up until I wanted to throw subshells into the mix, could (dirtily) answer.
Thus, I am forced to agree that eval is evil and has technical shortcomings on top of the security shortcomings.
How do you solve the following :
According to a switch-case splitting you accross different linux distributions you have different second threads to your script opened in another terminal, with matching close syntax.
The body is the same but does use variables filled within the first thread. And is NOT a single command
The body itself runs subshells
The replacing solution need to not be sequential but "synchronous" as eval is. (it does not make trailing commands wait for it's return to execute)
Here's what we get :
Our if and elif switcher which starts our scripts second terminal or "second thread" :
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
The second thread's main body variable :
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 "";
'
And the opening an closing variables that allow for the second thread of the script to be run by different terminals depending on the distribution.
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";'"'"' &'
Usually when people suggest replacements to eval the contexts are beyond simplified. eval is running a single echo "hello world"
.
here it is not my case and I've been able to apply none of the solutions.
Upvotes: 0
Views: 556
Reputation: 532238
Let's start with a high-level overview of what you want to do: run some number of arbitrary scripts (contained in strings) in a separate tab of an OS-specific terminal emulator. The contents of the scripts doesn't really matter, so let's just say we have them in two variables:
faf_script='...'
download_script='...'
Now, we'd like a function that looks something like this:
run_scripts () {
for script in "$@"; do
run_in_tab "$script"
done
}
where run_in_tab
is an OS-specific function that runs its argument in a new tab of the desired terminal emulator. Maybe it doesn't actually open a new tab; maybe it opens an entirely new window, but run_scripts
doesn't care about that. It just needs a function that will run a shell script in a way that the user can interact with it.
Next, we actually define run_in_tab
in an OS-specific way.
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
Once that's done, we simply call run_scripts
:
run_scripts "$faf_script" "download_script"
Upvotes: 2
Reputation: 15418
An alternative which basically does the same thing as eval
, but is easier (at least for me) in complex cases like this:
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
}
"sourcing" a file means reading it as if it's actual content had been typed instead of the line that sources it - almost literally. The context of the calling script are maintained. traps, variables, functions, aliases - everything available in the calling script is available to the code being called. This is virtually the only way a called script can set a variable in the caller, because this doesn't run the code in a subshell.
If anything breaks you have the whole file available to debug.
I recommend setting set -x
debugging on for the first few test runs just to make sure you see what you expect.
Upvotes: 0