転載・引用について

ユーザ用ツール

サイト用ツール


tweet:2017:0623_01

shell scriptとwhileとreadとssh

大変にハマったのでメモ書き。今日はもういいや。

shell scriptにおいて、ファイルから1行ずつ読み込んで処理をするような場合、以下のようにかける。

while read line; do
  echo ${line}
done < file

ところが、この手を使ってsshを実行すると、なぜか最初の行しか実行されない

while read line; do
  ssh xxx.xxx.xxx.xxx ${line}	# XXX 期待通りに動かない
done < file

原因はsshコマンド実行に伴う標準入力の切替と考えられる。 sshコマンドを実行すると、ローカルホストのstdinからの入力を終了し、sshで指定したリモートホストのstdinからの入力受付を開始する。 従って、ローカルホストのファイルの読込みを終了させた上でsshコマンドを実行し、再びreadコマンドを実行しようとしていると考えられる。もちろん、この時点で既にファイルがcloseされている為、whileが終了してしまう。

対策は、sshに-nオプションをつけること。これよって、sshコマンドのstdin切り替えを禁止することが出来る。

この原因がちっともわからず、数時間を無駄にしてしまった。

while read line; do
  ssh -n xxx.xxx.xxx.xxx ${line}	# これで/dev/nullがsshのstdinにつながる
done < file

このほかに、for文で for line in `cat file` で代用する手も考えられるが、これは、行にスペースがある場合、$lineに代入される値が1行まるごとではなく、スペースまでの部分になるので、ここにも明確な罠があると考えられる。これを回避するにはIFSを変えれば良いのだから、

IFS=$'\n'				# 区切り文字を" "から"\n"に変える
for line in `cat file`; do		# while readの代わりにcatで読み込ませる
  ssh -n xxx.xxx.xxx.xxx ${line}
done

あと、whileはちょっと特殊な制御文で、場合によってはwhile内で変数設定しているはずなのにloopをでてくると変数が空のようなことが起こる。 これは、Whileと他のコマンドを組み合わせた場合、組み合わせ方次第で処理がsubshellがわで処理されてしまう事が原因である。 shellでpipelineを用いて実行した処理は、subshellで処理される。while loopの内部でpipelineを利用すると、whileブロック全体がsubshellで処理されるため、whileブロックの内部と外部で変数の共有が出来ない。 このような場合、whileに対して出力をredirectしてやることで解決できる。

while read line; do
  OUT=`echo ${line} | sed 's/test/test2'`
done < $1
echo ${OUT}

なお、/bin/shの引数に -v-x をつけると、デバッグに大変に役立つ

このウェブサイトはクッキーを使用しています。 Webサイトを使用することで、あなたはあなたのコンピュータにクッキーを保存することに同意します。 また、あなたはあなたが私たちのプライバシーポリシーを読んで理解したことを認めます。 同意しない場合はウェブサイトを離れてください。クッキーに関する詳細情報
tweet/2017/0623_01.txt · 最終更新: 2017/06/23 21:45 by 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki