Strange behavior using a space in front of $SHELL environment variable in bash - linux

When I use the following command, everything works as expected (a new gnome-terminal is open, the current working directory is changed and the terminal remains open):
gnome-terminal -e "bash -c 'cd /';$SHELL"
# ↑
# no space character here
But when I use:
gnome-terminal -e "bash -c 'cd /'; $SHELL"
# ↑
# note the space
I can see a terminal that opens, but I can't see if the current working directory is changed or not because the terminal it closes immediately.
My question is: why this is happening; how can be wrong if I put a space in the second case?

In the first case, gnome-terminal executes
bash -c "cd /;/bin/bash"
in the second case, it runs
bash -c "cd /;" /bin/bash
The first case means "evaluate cd /;/bin/bash", which will cd and run an interactive shell.
The second case means "evaluate cd /; with $0 set to /bin/bash", which will run cd and then exit.

gnome-terminal does its own parsing of the command given to it, using g_shell_parse_argv, which apparently doesn't consider ; to be a word separator, so if a ; is adjacent to a non-whitespace character, it is considered to be part of that non-whitespace character's word.
This can result in surprising behavior if the command you pass to gnome-terminal has shell metacharacters in it.
I used strace on the gnome-terminal process to see what it does. The first command
gnome-terminal -e "bash -c 'cd /';$SHELL"
results in gnome-terminal running the following command
execve("/bin/bash", ["bash", "-c", "cd /;/bin/bash"])
The second command
gnome-terminal -e "bash -c 'cd /'; $SHELL"
results in gnome-terminal running the following command
execve("/bin/bash", ["bash", "-c", "cd /;", "/bin/bash"])
That being said, you should also note that a command such as bash -c 'cd /' will not have a lasting effect on the working directory of any command run after it. So I think that the first command you typed got the desired result only because of the gnome-terminal parsing misfeature, and a more robust way of writing it would be
gnome-terminal -e "bash -c 'cd /;$SHELL'"

Related

Executing `sh -c` in a bash script

I have a test.sh file which takes as a parameter a bash command, it does some logic, i.e. setting and checking some env vars, and then executes that input command.
#!/bin/bash
#Some other logic here
echo "Run command: $#"
eval "$#"
When I run it, here's the output
% ./test.sh echo "ok"
Run command: echo ok
ok
But the issue is, when I pass something like sh -c 'echo "ok"', I don't get the output.
% ./test.sh sh -c 'echo "ok"'
Run command: sh -c echo "ok"
%
So I tried changing eval with exec, tried to execute $# directly (without eval or exec), even tried to execute it and save the output to a variable, still no use.
Is there any way to run the passed command in this format and get the ourput?
Use case:
The script is used as an entrypoint for the docker container, it receives the parameters from docker CMD and executes those to run the container.
As a quickfix I can remove the sh -c and pass the command without it, but I want to make the script reusable and not to change the commands.
TL;DR:
This is a typical use case (perform some business logic in a Docker entrypoint script before running a compound command, given at command line) and the recommended last line of the script is:
exec "$#"
Details
To further explain this line, some remarks and hyperlinks:
As per the Bash user manual, exec is a POSIX shell builtin that replaces the shell [with the command supplied] without creating a new process.
As a result, using exec like this in a Docker entrypoint context is important because it ensures that the CMD program that is executed will still have PID 1 and can directly handle signals, including that of docker stop (see also that other SO answer: Speed up docker-compose shutdown).
The double quotes ("$#") are also important to avoid word splitting (namely, ensure that each positional argument is passed as is, even if it contains spaces). See e.g.:
#!/usr/bin/env bash
printargs () { for arg; do echo "$arg"; done; }
test0 () {
echo "test0:"
printargs $#
}
test1 () {
echo "test1:"
printargs "$#"
}
test0 /bin/sh -c 'echo "ok"'
echo
test1 /bin/sh -c 'echo "ok"'
test0:
/bin/sh
-c
echo
"ok"
test1:
/bin/sh
-c
echo "ok"
Finally eval is a powerful bash builtin that is (1) unneeded for your use case, (2) and actually not advised to use in general, in particular for security reasons. E.g., if the string argument of eval relies on some user-provided input… For details on this issue, see e.g. https://mywiki.wooledge.org/BashFAQ/048 (which recaps the few situations where one would like to use this builtin, typically, the command eval "$(ssh-agent -s)").

Linux Gonme: Start multiple terminals and execute a command in each one

How can I implement the following scenario in Ubuntu Linux?
I want to go to my console, then execute ./startdev.sh and then
1) Terminal 1 pops up, starts /home/foobar/bla1.sh
2) Terminal 2 pops up, starts /home/foobar/bla2.sh
3) Terminal 3 pops up, starts /home/foobar/bla3.sh
I already figured that the command "gnome-terminal & disown" starts a new terminal.
However, until now, I don't know how to run a command in that terminal.
I accept any answer that gives me either the entire implementation for startdev.sh or a list of commands that I can use.
Thank you!
Try this content for startdev.sh:
#!/bin/bash
gnome-terminal --command=/home/foobar/bla1.sh & disown
gnome-terminal --command=/home/foobar/bla2.sh & disown
gnome-terminal --command=/home/foobar/bla3.sh & disown
But it is not clear for me why you need to disown the launched processes.
Try this script
If you need to simultaneously pop all terminals
#!/bin/bash
gnome-terminal -e "bash -c '/home/foobar/bla1.sh; sleep 10'" | gnome-terminal -e "bash -c '/home/foobar/bla2.sh; sleep 10'" | gnome-terminal -e "bash -c '/home/foobar/bla3.sh; sleep 10'"
else if you need to run commands one by one terminal then .,
#!/bin/bash
gnome-terminal -e "bash -c '/home/foobar/bla1.sh; sleep 10'"
gnome-terminal -e "bash -c '/home/foobar/bla2.sh; sleep 10'"
gnome-terminal -e "bash -c '/home/foobar/bla3.sh; sleep 10'"
this script will open multiple terminals and execute the command given within the quotes and i have used sleep to hold the terminal from exiting if not added gnome-terminal will execute command and exit immediately.

sudo command behaviour with quotes

I need your help in understanding this behaviour of sudo.
sudo -s -- 'ls -l' this command works but sudo 'ls -l' throws error saying
sudo: ls -l: command not found I realize it treats the entire string within quote as single command (including the spaces) but what I don't get is how does it work fine with -s flag but fails when -s is not there.
Without -s, the first argument is the name of the command to execute. With -s, the first argument is a string passed to the -c option of whatever shell ($SHELL or your system shell) is used to execute the argument.
That is, assuming $SHELL is sh, the following are equivalent:
sudo -s -- 'ls -l'
sudo -- sh -c 'ls -l'
From the sudo man page:
-s [command]
The -s (shell) option runs the shell specified by the SHELL environment variable if it is set or the shell as specified in
the password database. If a command is specified, it is passed to the
shell for execution via the shell's -c option. If no command is
specified, an interactive shell is executed.
It behaves like it does because a new shell is spawned which breaks up the words in your "quoted command" like shells do.

How to execute a file in separate gnome terminal in linux

I want to execute a C program in separate terminal, I've tried this command,
gnome-terminal -x ./test
and
gnome-terminal -e test
But it opens a new terminal and before giving me output, it just vanish.
How could I solve this issue using gnome-terminal?
Thanks in advance
This might be what you search:
gnome-terminal -e "bash -c \"!!; exec bash\""
or (shortly):
gnome-terminal -x sh -c "!!; bash"
It opens gnome-terminal with your last command (!!) executed and it stays open with the command output in the shell, even with an interactive command like top or less...
In your case its:
gnome-terminal -e "bash -c \"./test; exec bash\""
or
gnome-terminal -x sh -c "./test; bash"

Error q : Command not found on Vim

I created a bash script :
#!/bin/bash
su root -c vim $1 -c ':%s/^M//g' -c 'wq'
My script has to remove all the ^M (carriage return on Windows) on my file, then save it.
When I execute my script it returns :
/sequenceFiles/Sequence1.seq: wq: command not found
Does someone know why ?
Thanks for your help.
The -c is seen by su, not vim, and the shell complains about the unknown command.
You need to pass the command as one argument to su:
su root -c "vim $1 -c ':%s/^M//g' -c 'wq'"
man su says:
`-c COMMAND'
`--command=COMMAND'
Pass COMMAND, a single command line to run, to the shell with a
`-c' option instead of starting an interactive shell.
Try
su root -c "vim $1 -c ':%s/^M//g' -c 'wq'"
While you can do it with vim, consider simpler:
perl -pi -e 's/\r\n/\n/' file

Resources