2

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

Cyrus
  • 84,225
  • 14
  • 89
  • 153
IDontKnow
  • 23
  • 1
  • 4
  • 1
    See: [how to open a gnome terminal to execute a command with gnome-terminal, constantly?](http://stackoverflow.com/q/38135995/3776858) – Cyrus Apr 05 '17 at 19:05

2 Answers2

2

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.

Adrian Bartyczak
  • 188
  • 3
  • 14
  • `echo "${cmd}"` is not very portable. Keep in mind that the `$(...)` expansion is run with the current shell, *not* with bash, so you don't know whose implementation of `echo` it is. See [the POSIX spec for `echo`]( http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html) to get an idea of just how uncertain/unreliable its behavior can be. – Charles Duffy May 22 '17 at 16:56
  • `echo "${@}"` is particularly unreliable. You can't tell the difference between `echo "hello" "world"` and `echo "hello world"`, for obvious reasons. – Charles Duffy May 22 '17 at 16:57
  • *If* you're guaranteed that your exterior shell is bash or ksh, much better to use `printf '%q ' "$@"` to form a properly quoted command (`printf -v cmd '%q ' "$@"` if it's known to be bash; ksh optimizes `cmd=$(printf '%q ' "$@")` to avoid forking an actual subshell, but bash does not). – Charles Duffy May 22 '17 at 16:59
  • Thanks @CharlesDuffy. This was in fact only tested in bash. I will test your solution with different shells and update the answer accordingly. – Adrian Bartyczak May 22 '17 at 17:11
  • I'd suggest something like `printf '%s\n' "hello world" "*" "goodbye world"` as the command under test so quoting/string-splitting issues are detected. – Charles Duffy May 22 '17 at 17:19
  • Thanks for the reference. I did not know that echo was not very portable. It seems like a common mistake among novice Linux users. Several months ago, I could not get the Here document to work as the command with echo using anything other than "$(echo "${@}"; printf '\n exec bash')" with xterm. Your solution works and is better as it is portable and does not create a subshell so I will use it in the answer instead. Changed `cmd="wc -w <<-EOF ...` to `printf -v cmd '%s\n ' "wc -w <<-EOF ...`. – Adrian Bartyczak May 22 '17 at 21:04
  • Unfortunately, `printf -v cmd ...` is a bashism -- so while it's faster to execute than `cmd=$(printf ...)` on bash, if portability is a goal, you might need to consider the latter. – Charles Duffy May 22 '17 at 21:05
  • BTW, I very sincerely doubt that `bash -c "$@"` or `bash -c "${@}"` does what you intend. Only `"$1"` is passed in the `-c` position; `$2` and onward are passed later, becoming `$0`, `$1`, etc. when running the script in the first position. – Charles Duffy May 22 '17 at 21:08
  • To demonstrate with the test command I gave earlier, `set -- printf '%s\n' "hello world" "*" "goodbye world"` to assign each word of that command to a different argument position, and then run `xterm -e bash -c "${@}; sleep 30"`. – Charles Duffy May 22 '17 at 21:09
  • ...by contrast, *with bash or ksh*, `set -- printf '%s\n' "hello world" "*" "goodbye world"; cmd=$(printf '%q ' "$@"); xterm -e bash -c "$cmd; sleep 30"` actually Does The Right Thing. – Charles Duffy May 22 '17 at 21:11
  • By the way, if you want to make your test case even more pathological, consider `set -- printf '%s\n' "hello world" "*" '$(touch /tmp/insecure)' "goodbye world"`, and look at whether `/tmp/insecure` exists after each round of testing. – Charles Duffy May 22 '17 at 21:13
  • Changed `printf -v cmd '%s\n ' "wc -w <<-EOF...` to `cmd="$(printf '%s\n ' 'wc -w <<-EOF...`. As for `set -- printf '%s\n' "hello world" "*" "goodbye world"; cmd=$(printf '%q ' "$@"); xterm -e bash -c "$cmd; sleep 30"`, it works, escaping the "*" as expected, however, not when arguments are passed rather than set, as `%q` causes everything to get escaped. How can `printf` be used when the arguments are passed? – Adrian Bartyczak May 22 '17 at 22:13
  • eh? `yourfunc() { set -- printf '%s\n' "hello world" "*" "goodbye world"; ...; }` is **exactly** the same as `yourfunc() { ...; }` invoked as `yourfunc printf '%s\n' "hello world" "*" "goodbye world" `. You'll need to provide a test case where this isn't true for me to grok where you're coming from. – Charles Duffy May 22 '17 at 22:26
  • I meant when passing just `"hello world" "*" "goodbye world"`. – Adrian Bartyczak May 22 '17 at 22:33
  • I think we're not entirely on the same page though. Passing `"hello world" "*" "goodbye world"` on its own works, however, only to print it. The problem is to get the arguments to be used as commands. E.g. passing `"echo firstecho" "echo secondecho"` would output "firstecho", followed by a newline, and then "secondecho." – Adrian Bartyczak May 22 '17 at 22:41
  • That's a completely different interface, where you have each command passed as a pre-escaped/pre-quoted string -- and it's a harder one to use safely, because it means someone has to use `printf '%q'` or an equivalent to safely form each individual argument in the first place, or have those arguments be hand-generated and hand-audited by humans with no literal substitution of data into them. Taking each word in a simple shell command as a single argument and executing that command list literally is that it's safe from shell injection attacks; what you're proposing above is not. – Charles Duffy May 22 '17 at 22:52
1

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
Dario
  • 2,673
  • 20
  • 24
  • Awesome bro, that works :D Btw I defined in the profile that it doesnt close, so i dont need read but nice to see that it would work like that too. And only one question: How could I integrate `printf "\n"` in the code so basically I want that it prints an empty line before it executes the other command, i tried writing it in front of `route -n` but it does nothing. – IDontKnow Apr 05 '17 at 19:13
  • Edited with `printf \\\\n` as you requested... Quotes and escapes tend to multiply out of control... – Dario Apr 05 '17 at 19:31
  • And how can I add variables to it so something like `command="route -n"` ... 'bash -c "$command"' (<- That doesnt work) (Sry thats my first bash script xD) – IDontKnow Apr 05 '17 at 19:35
  • Edited again. :) – Dario Apr 05 '17 at 19:49
  • THANK YOU VERY MUCH BRO!!! You cant believe how much you helped me :D And I always use double quotes so thats just perfect for me :D And thanks for writing the file-version here again although I already knew that but my commands are too simple for that. Thanks again <3 – IDontKnow Apr 05 '17 at 19:56
  • Showing how to use `printf -v cmd_q '%q ' "${cmd[@]}"` to generate a string that, when run (with `gnome-terminal -e "bash -c $cmd_q"`) will invoke that command would probably be a helpful extension to this answer. – Charles Duffy May 22 '17 at 16:55