This question already has answers here:
how do i start commands in new terminals in BASH script
(2 answers)
Closed 20 days ago.
So i want to open a new terminal in bash and execute a command with arguments.
As long as I only take something like ls as command it works fine, but when I take something like route -n , so a command with arguments, it doesnt work.
The code:
gnome-terminal --window-with-profile=Bash -e whoami #WORKS
gnome-terminal --window-with-profile=Bash -e route -n #DOESNT WORK
I already tried putting "" around the command and all that but it still doesnt work
You can start a new terminal with a command using the following:
gnome-terminal --window-with-profile=Bash -- \
bash -c "<command>"
To continue the terminal with the normal bash profile, add exec bash:
gnome-terminal --window-with-profile=Bash -- \
bash -c "<command>; exec bash"
Here's how to create a Here document and pass it as the command:
cmd="$(printf '%s\n' 'wc -w <<-EOF
First line of Here document.
Second line.
The output of this command will be '15'.
EOF' 'exec bash')"
xterm -e bash -c "${cmd}"
To open a new terminal and run an initial command with a script, add the following in a script:
nohup xterm -e bash -c "$(printf '%s\nexec bash' "$*")" &>/dev/null &
When $* is quoted, it expands the arguments to a single word, with each separated by the first character of IFS. nohup and &>/dev/null & are used only to allow the terminal to run in the background.
Try this:
gnome-terminal --window-with-profile=Bash -e 'bash -c "route -n; read"'
The final read prevents the window from closing after execution of the previous commands. It will close when you press a key.
If you want to experience headaches, you can try with more quote nesting:
gnome-terminal --window-with-profile=Bash \
-e 'bash -c "route -n; read -p '"'Press a key...'"'"'
(In the following examples there is no final read. Let’s suppose we fixed that in the profile.)
If you want to print an empty line and enjoy multi-level escaping too:
gnome-terminal --window-with-profile=Bash \
-e 'bash -c "printf \\\\n; route -n"'
The same, with another quoting style:
gnome-terminal --window-with-profile=Bash \
-e 'bash -c '\''printf "\n"; route -n'\'
Variables are expanded in double quotes, not single quotes, so if you want them expanded you need to ensure that the outermost quotes are double:
command='printf "\n"; route -n'
gnome-terminal --window-with-profile=Bash \
-e "bash -c '$command'"
Quoting can become really complex. When you need something more advanced that a simple couple of commands, it is advisable to write an independent shell script with all the readable, parametrized code you need, save it somewhere, say /home/user/bin/mycommand, and then invoke it simply as
gnome-terminal --window-with-profile=Bash -e /home/user/bin/mycommand
Related
I am trying to write a Bash script for a custom rofi menu. This menu would have different choices for different custom commands. Currently, I am trying to see if I can write a Bash script to execute a single command. The command is:
exec /home/user/.config/i3/myscript.sh 'todolist' 'xfce4-terminal --command "emacs -nw -Q -l ~/.emacs.d/script.el ~/Dropbox/mytodo.org" '
This is a very long command so I break it down like this:
COMMAND=("/home/user/.config/i3/myscript.sh")
EMACS=(" 'todolist' 'xfce4-terminal --command "emacs -nw -Q -l ~/.emacs.d/script.el ~/Dropbox/mytodo.org"' ")
I tried to echo out: echo $COMMAND $EMACS. The result I got is:
/home/user/.config/i3/script.sh 'todolist' 'xfce4-terminal --command emacs
So, everything from the -nw -Q -l forward are ignored. As a result, it is an incomplete command. How can I fix the second variable EMACS?
Parentheses in a variable assignment in Bash are used to create arrays. The array items are separated by space. Here's an example:
arr=(0 1 2)
echo ${arr[0]}
echo ${arr[1]}
echo ${arr[2]}
So in your case:
EMACS=(" 'todolist' 'xfce4-terminal --command "emacs -nw -Q -l ~/.emacs.d/script.el ~/Dropbox/mytodo.org"' ")
The expression ${EMACS[0]} will contain 'todolist' 'xfce4-terminal --command emacs, the expression ${EMACS[1]} will have the value -nw, and so on.
Also, notice that " 'todolist' etc. "emacs is just the juxtaposition of the strings " 'todolist' etc. " and emacs, so the result will be the concatenation of all characters without the quotes.
My suggestion is that you use variables for the files and keep the rest in plain bash. Use a backslash to break the command into multiple lines if you feel one line is too long:
MY_SCRIPT="${HOME}/.config/i3/myscript.sh"
EMACS_SCRIPT="${HOME}/.emacs.d/script.el"
DROPBOX_FILE="${HOME}/mytodo.org"
exec "$MY_SCRIPT" todolist xfce4-terminal \
--command "emacs -nw -Q -l ${EMACS_SCRIPT} ${DROPBOX_FILE}"
I can execute the command below in my terminal succesfully.
command:
gdalwarp -s_srs "+datum=WGS84 +no_defs +geoidgrids=egm96-15.gtx" -t_srs "+datum=WGS84 +no_def" input.tif output.tif
Now, I want to store this command into a variable and expand this command inside a docker container.
My script run.sh looks like the following. I first store my target command into mycommand and run the container with the command as input.
mycommand=$#;
docker run -ti --rm osgeo/gdal:ubuntu-small-latest /bin/bash -c "cd $(pwd); ${mycommand}"
And then I execute the run.sh as following.
bash run.sh gdalwarp -s_srs "+datum=WGS84 +no_defs +geoidgrids=egm96-15.gtx" -t_srs "+datum=WGS84 +no_def" input.tif output.tif
Issue:
I was hoping everything after bash run.sh can be store literally into the mycommand variable.
And inside the docker container, the mycommand can be expand and execute literally. But it looks like that the double quote in my original command will be lost during this process.
Thank you.
You could pass the command as argument and then invoke "$#" inside the shell. I prefer mostly single quotes.
docker run -ti --rm osgeo/gdal:ubuntu-small-latest \
/bin/bash -c 'cd '"$(pwd)"' && "$#"' -- "$#"
If only you want cd just let docker change the directory with -w. In Bash $PWD will be faster then pwd command.
docker run ... -w "$PWD" image "$#"
Note that "$(pwd)" is not properly quoted inside child shell - the result will undergo word splitting and filename expansion. Anyway, I recommend declare -p and Bash arrays (and declare -f for functions) to transfer data between Bash-es. declare will always properly quote all stuff, so that child shell can properly import it.
cmd=("$#")
pwd=$PWD
work() {
cd "$pwd"
"${cmd[#]}"
}
docker ... bash -c "$(declare -p pwd cmd); $(declare -f work); work"
Research: when to use quoting in shell, difference between single and double quotes, word splitting expansion and how to prevent it, https://mywiki.wooledge.org/Quotes , bash arrays, https://mywiki.wooledge.org/BashFAQ/050 .
I've simplified my example to the following:
file1.sh:
#!/bin/bash
bash -c "./file2.sh $#"
file2.sh:
#!/bin/bash
echo "first $1"
echo "second $2"
I expect that if I call ./file1.sh a b to get:
first a
second b
but instead I get:
first a
second
In other words, my later arguments after the first one are not getting passed through to the command that I'm executing inside a new bash shell. I've tried many variations of removing and moving around the quotation marks in the file1.sh file, but haven't got this to work.
Why is this happening, and how do I get the behavior I want?
(UPDATE - I realize it seems pointless that I'm calling bash -c in this example, my actual file1.sh is a proxy script for a command that gets called locally to run in a docker container so it's actually docker exec -i mycontainer bash -c '')
Change file1.sh to this with different quoting:
#!/bin/bash
bash -c './file2.sh "$#"' - "$#"
- "$#" is passing hyphen to populate $0 and $# is being passed in to populate all other positional parameters in bash -c command line.
You can also make it:
bash -c './file2.sh "$#"' "$0" "$#"
However there is no real need to use bash -c here and you can just use:
./file2.sh "$#"
I want to echo a string into the /etc/hosts file. The string is stored in a variable called $myString.
When I run the following code the echo is empty:
finalString="Hello\nWorld"
sudo bash -c 'echo -e "$finalString"'
What am I doing wrong?
You're not exporting the variable into the environment so that it can be picked up by subprocesses.
You haven't told sudo to preserve the environment.
\
finalString="Hello\nWorld"
export finalString
sudo -E bash -c 'echo -e "$finalString"'
Alternatively, you can have the current shell substitute instead:
finalString="Hello\nWorld"
sudo bash -c 'echo -e "'"$finalString"'"'
You can do this:
bash -c "echo -e '$finalString'"
i.e using double quote to pass argument to the subshell, thus the variable ($finalString) is expanded (by the current shell) as expected.
Though I would recommend not using the -e flag with echo. Instead you can just do:
finalString="Hello
World"
I need to run a command with bash -c within a tmux session, from within a shell script. In contrast to screen, tmux seems to require to quote the entire command, which leads to problems as bash -c also requires quoting for correct functioning with more complex command strings.
In the following I'm trying to demonstrate the behavior with a minimal example. What I'm trying to achieve involves more complex commands than ls of course. Also for my purpose it is necessary to expand the CMD variable, as it is built in the script before.
A minimal script for screen:
#!/bin/bash
set -x
CMD="ls -l; sleep 5"
screen -d -m bash -c "$CMD"
When executing this script you get (stdout due to -x)
+ CMD='ls -l; sleep 5'
+ screen -d -m bash -c 'ls -l; sleep 5'
The sleep command is for having time to attach to the screen session and see what happens. When attaching to the screen session after executing the above script one sees that the output of the ls command is in long list format, i.e. the command is executed properly.
In tmux it seems one has to quote the command to get it executed in the new session. I use the following script:
#!/bin/bash
set -x
CMD="ls -l; sleep 5"
tmux new -d "bash -c $CMD"
The stdout is
+ CMD='ls -l; sleep 5'
+ tmux new -d 'bash -c ls -l; sleep 5'
As one can see, the cmd sequence for bash -c is not quoted properly anymore. When attaching to the created tmux session one can see, that this results in ls being executed without the long list option recognized.
What can I do to get the proper quoting (i.e. single quotes around the expanded string) for the $CMD string passed to bash -c?
Update
Escaping, as Eric Renouf suggested, with \"$CMD\" produces
tmux new -d 'bash -c "ls -l; sleep 5"'
and escaping with '$CMD' produces
tmux new -d 'bash -c '\''ls -l; sleep 5'\'''
Both works for the provided minimal example, but is still not exactly what screen produces and does not work in my case.
Here are the exact call I'm making (see here for all the gory details):
$SCREEN -S "scalaris_$NODE_NAME" -d -m bash -x -f +B -c "$START_CMD; sleep 365d"
which produces (output of -x)
/usr/bin/screen -S scalaris_first#pvs-pc07.zib.de -d -m bash -x -f +B -c '"/usr/bin/erl" -setcookie "chocolate chip cookie" -pa /home/jvf/code/scalaris/contrib/yaws/ebin -pa /home/jvf/code/scalaris/contrib/log4erl/ebin -pa /home/jvf/code/scalaris/ebin -sasl sasl_error_logger false -yaws embedded true -scalaris log_path "\"/home/jvf/code/scalaris/log/first#pvs-pc07.zib.de\"" -scalaris docroot "\"/home/jvf/code/scalaris/docroot\"" -scalaris config "\"/home/jvf/code/scalaris/bin/scalaris.cfg\"" -scalaris local_config "\"/home/jvf/code/scalaris/bin/scalaris.local.cfg\"" -connect_all false -hidden -name first#pvs-pc07.zib.de -scalaris start_type first -scalaris port 14195 -scalaris yaws_port 8000 -scalaris join_at_list '\''[0]'\'' -scalaris start_mgmt_server true -scalaris nodes_per_vm "1" -s scalaris +sbt db +swt low +sbwt short'
I think the solutions suggested so far do not work because of the use of double quotes within the command, but I'm not 100% positive about that. How can I reproduce the exact quoting screen produces (single quotes around the complete command passed to bash -c) with tmux?
I'm trusting that you need to do this for tmux, but to get the extra quotes you want, you could just escape them in the outer quote like:
tmux new -d "bash -c \"$CMD\""
or you could put those in single quotes in most cases like
tmux new -d "bash -c '$CMD'"
since the $CMD will be expanded by the outer quotes