ssh执行远程命令和bash -c string的用法
发现自己看文档没看仔细,长期在用 ssh localhost bash -c "echo hello
world" true
试图让这个命令打出一个 hello world
出来,发现什么也没打
出来之后(其实是有一个换行打出来),我想出来一个招,在echo之前加一个
true;
,发现就好使了。其实最后那个 true
就已经是一种hack了,知其然
却不知其所以然,今天终于把所以然也整明白了。
用得最多的 bash -c
,是类似 find . -name '*.java' | xargs bash -c
'for x in "$@"; do XXX "$x"; done' true
这样的用法。我一直没弄明白为什
么最后要加一个 true
,只知道不加的话会少处理一个java文件(find输出的
第一个文件)。根据find输出行带不带空格,可以给xargs加一个 -d \\n
的开
关,以行为单位进行处理。
而 ssh...bash -c
的问题,就更没有弄明白了,一直用
ssh localhost bash -c 'true; echo hello world'
的方法来执行,不加 true;
的话会发现 hello world
不会被打印出来。
今天碰到一个奇怪的问题,终于让我打开手册页看了一下…
问题是这样的:
ssh localhost bash -c ' true; echo hello world'
发现输出如下:
hello world bash: -c: option requires an argument
hello world
被打了出来,同时 bash -c
报错,它没有找到参数!
究其原因,ssh和xargs简直可以说南北两极:xargs把标准输入流(stream)里读 来的东西放到参数列表里,ssh却反过来把参数列表里的东西放到一个流里。所以 上面的命令中,ssh看到的是三个参数 bash,-c,和"\ntrue; echo hello world",它所做的事却是把这三个参数用空格拼成一个长串,然后把这个长串写 给一个文件、管道流,捅给bash/sh等shell去执行!最后shell看到喂过来的脚本是这样的:
bash -c
true; echo hello world
至于我的那个xargs的例子,则是因为 bash -c
之后的第一个参数会被当成一
个脚本,然后后面还有其他参数的话,会被当成从0开始的位参($0, $1,
$2…),而$0在shell编程里一般是脚本的名字,"$@"这样的表达式是不包括$0
的。
这样以下这些现象就好理解了:
echo 'hello world' => hello world (注意空格数,还是4个) echo hello world => hello world (空格数是1个,echo命令给加上的, 把它的参数依次打印出来,每两个参数中间加一个空格, 这里它有两个参数,hello和world,上面的例子中它只有一个参数, 'hello world'去掉两边的单引号) ssh localhost echo 'hello world' => hello world (空格数变成了1个,因为ssh登录成功后喂给shell的是 echo hello world)
跟ssh类似的还有一个命令,是watch。
如果ssh后执行的是一条简单的命令,比如echo hello world,则即使你写成
ssh localhost echo 'hello ' '' '' "" 'world'
最后的输出也是'hello world'去掉引号。