神秘的bash -c選項

某天在執行命令盯著螢幕看的時候,發現一行有趣的指令,大致如下…

num@instance-1:~$ ssh remote-1 "ps -ef | grep bash"
ubuntu   25570 25569  0 02:50 ?        00:00:00 bash -c ps -ef | grep bash
ubuntu   25572 25570  0 02:50 ?        00:00:00 grep bash

透過ssh發給遠端主機的命令中,夾帶了一個-c,在man中的解釋為

-c        If the -c option is present, then commands are read from the first non-option argument command_string.  
          If there are arguments after the command_string, they are assigned to the positional
          parameters, starting with $0.

一般使用以下指令都是正常的

bash -c 'w'
bash -c 'ls'
bash -c 'echo hello world'  

那如果後面繼續帶參數呢…就沒有反應了

$ bash -c 'echo hello world' xxx yyy zzz
hello world

上網四處逛逛,看到了一篇類似文章

這開始令人不解了…用那麼久沒看過指令這樣下的

num@instance-1:~$ ssh localhost bash -c "echo hello world" true

num@instance-1:~$ ssh localhost bash -c "echo hello world"

num@instance-1:~$ ssh localhost bash -c "true; echo hello world"
hello world

所以這個神奇的參數到底在作什麼,哪來的-ctrue的,後來又找到一篇

先撇開ssh這件事,看看-c搞什麼名堂

試著跑了一次

num@instance-1:~$ bash -c 'echo "$0 is $0, $1 is $1, $2 is $2"' foo bar biz
foo is foo, bar is bar, biz is biz

test.sh檔案內容

num@instance-1:~$ cat test.sh
#!/bin/bash

echo "$0 is $0, $1 is $1, $2 is $2"
echo $*

存成檔案再跑一次

num@instance-1:~$ bash -c './test.sh' foo bar biz
./test.sh is ./test.sh,  is ,  is

直接執行的話,結果是這樣

num@instance-1:~$ ./test.sh foo bar biz
./test.sh is ./test.sh, foo is foo, bar is bar

太令人混亂了。於是我換了一個方法,直接在執行指令處印出args:

num@instance-1:~$ bash -c 'echo "$@"' aaa bbb ccc
bbb ccc
num@instance-1:~$ ./test.sh aaa bbb ccc
./test.sh is ./test.sh, aaa is aaa, bbb is bbb
aaa bbb ccc
num@instance-1:~$ bash -c './test.sh' aaa bbb ccc
./test.sh is ./test.sh,  is ,  is

結果顯示,使用bash -c時,是把後面的args都傳給__bash自身__,而不是傳給-c裡面的命令

那麼回到剛開始的ssh localhost bash -c這個問題..

我們已經知道執行ssh remote1 echo hello world的時候,實際上ssh會到remote1這台機器執行

bash -c "echo hello world"

所以前面的指令帶換後也會產生一樣的結果

num@instance-1:~$ bash -c "bash -c "echo hello world" true"

num@instance-1:~$ bash -c "bash -c "echo hello world""

num@instance-1:~$ bash -c "bash -c "true; echo hello world""
hello world