Friday, September 16, 2011

slaves.sh的小细节


Hadoop集群中,批量地对slaves节点进行操作,对于略懂得shell脚本技巧的人其实并不是一个大的问题。简单地说,就是对slaves文件中的每个host进行循环,然后ssh执行命令即可。近来看同事操作,总算发现bin下有一个slaves.sh,已经做了同样的事情,实在是省下不少typing work!

后来因为一个不相干的问题稍微被看一下slaves.sh的代码,下面最关键的一句,却没完全看明白:

ssh $HADOOP_SSH_OPTS $slave $"${@// /\\ }" \
   2>&1 | sed "s/^/$slave: /" &

其中 ${@// /\\ } 难理解。看到这里,你就知道下面文章实际与hadoop没有多大关系,而是跟 shell 脚本有关系了。

先简单说明变量字符串字串替换的语法,譬如有变量v:

v="hello borqs, hello me"
[bing@myvm ~]$ echo ${v/hello/great}
great borqs, hello me
[bing@myvm ~]$ echo ${v//hello/great}
great borqs, great me

当v后面是一个斜杠时,是指替换第一处"hello"为"great";当后面是两个斜杠时,是指替换所有的"hello"为"great"。

其次,是 ${@// /\\ } 的语义。用小脚本 print_args.sh 作为演示:

$ cat print_args.sh
#!/bin/bash

echo "${@// /\\ }"
echo "$@"
echo "$#"

分别执行下面的命令:

$ ./print_args.sh i say "hello hanborqs"     
i say hello\ hanborqs
i say hello hanborqs
3

也就是说,${@// /\\ } 是对$@这个数组中的每一个元素进行了空格转义,而不是对"$@"这一个整个字符串进行空格转义(这是最初困扰我的)。起初,我以为 ${@// /\\ } 会将参数替换为"i\ say\ hello\ hanborqs"。这也是我最开始无法理解 slaves.sh 中这处代码的地方:有与没有是一样的。
其实,$@ 也无非就是一个数组罢了。也就是说,这个字符串替换语法,在应用于一个Array上面时,应当是对先每个元素替换。当然,这样设计才是合理的 :) 。Anyway,让我们再尝试一下:

$ arr=("i" "say" "hello borqs")
$ echo ${arr[@]}
i say hello borqs
$ echo ${#arr[@]}
3
$ echo ${arr[@]// /\\ /}
i say hello\ /borqs

这样,就能确定这个字串替换语法中应用于Array时,的确考虑了计算的优先次序的问题。
回过来看 slaves.sh。slaves.sh这样实现,无非是为了解决传递带空格的参数或命令的问题。譬如,想在slaves.sh 上都创建一个名为“hello world.txt”,则可以这样运用:
slaves.sh touch "hello world.txt"
如果想创建"hello"文件和"world.txt",则应当:
slaves.sh touch hello world.txt
同样地,如果你要执行的命令(第三方的命令吧?)或脚本的名字中有空格,用引号括起来就行了,slaves.sh是可以应付的。everything works just like in a bash shell !