问题

想要写一个shell script实现如下功能:

给定一个ip的list和对应的hostname,对每个ip的机器设置静态hostname并重启。并可以远程调用hostname命令来显示所有机器的hostname是否已设置成功。

脚本最初的版本是这样的:

#!/bin/sh
# Get the absolute directory path of this file
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
while read line
do
	array=()
	for word in $line
	do
		array+=($word)
	done

	echo "Setting the hostname of ${array[0]} to ${array[1]}"
	sshpass -p sp1unk ssh -o StrictHostKeyChecking=no root@${array[0]} "hostnamectl set-hostname ${array[1]} --static && reboot"
	echo "====================================================================="
	done <$DIR/host_mapping.txt

所有的ip和hostname信息存在同目录下的host_mapping.txt文件中,每一行为一个条目,ip和hostname以空格间隔。

结果运行下来,设置完第一个ip的hostname程序就结束了。

分析原因

  • 猜想1:因为reboot命令导致ssh链接终端从而使进程直接结束。那么我们去掉reboot试一下,发现仍然是只执行了一个条目就退出了。

  • 猜想2:sshpass这条命令抛出了异常导致程序结束。但实验下来,shell脚本出现异常好像并不影响后面的语句执行,比如下面的脚本:

    #!/bin/sh
    UnkonwnCommand
    echo "Executed here"
    

    输出为:

    ./test.sh: line 2: UnkonwnCommand: command not found
    Executed here
    
  • 猜想3:跟sshpass或ssh这条命令相关,参考stackoverflow。也就是说ssh命令默认会读取后续的所有内容作为它的输入,解决方法是把它的输入连到别的地方去。

解决问题

可远程配置hostname的最终脚本如下:

#!/bin/sh
# Get the absolute directory path of this file
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
while read line
do
	array=()
	for word in $line
	do
		array+=($word)
	done

	echo "Setting the hostname of ${array[0]} to ${array[1]}"
	sshpass -p sp1unk ssh -o StrictHostKeyChecking=no root@${array[0]} "hostnamectl set-hostname ${array[1]} --static && reboot" < /dev/null
	echo "====================================================================="
	done <$DIR/host_mapping.txt