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'去掉引号。