ls
リモートホストのforループにファイルがない場合はすべて正常ですが、変数をキャプチャして各ファイルを試してみると、変数がls
拡張echo
されていないか値がないため失敗します。
私が言いたいことは:
IFS=$'\n'
servers=(
blue
green
red
)
for i in ${servers[@]}; do
ssh $i "
ls /dir/xyz_${i}*/details.txt
"
done
/dir/xyx_blue0/details.txt
/dir/xyz_blue1/details.txt
/dir/xyx_green2/details.txt
/dir/xyz_green4/details.txt
/dir/xyx_red1/details.txt
/dir/xyz_red8/details.txt
ls
ただし、実際にはファイルを操作できるように出力を繰り返す必要がありますが、変数は拡張されません。
for i in ${servers[@]}; do
ssh $i "
for i in $(ls /dir/xyz_${i}*/details.txt); do
echo $i
done
"
done
not found: /dir/xyx_blue*/details.txt
not found: /dir/xyx_green*/details.txt
not found: /dir/xyx_red*/details.txt
$i
リモートホストでループを実行するときにどのように拡張しますか?
ベストアンサー1
問題は、リモートサーバーで実行したいコマンドの置き換えがローカルで実行されることです。これは、置換が二重引用符内にあるためです。これは、ローカルシェルが呼び出す前に拡張を計算することを意味しますssh
。
また、出力を読むls
ことは賢明ではありません(参照:なぜ`ls`を解析しないのですか*(そしてどうすればいいですか?))。
代わりに:
servers=(
blue
green
red
)
for server in "${servers[@]}"; do
ssh -T "$server" <<END_SCRIPT
shopt -s nullglob
for pathname in /dir/xyz_$server*/details.txt; do
printf 'file path is "%s"\n' "\$pathname"
done
END_SCRIPT
done
これはリモートサーバーに送信されるスクリプトで、引用符なしで「here-document」を使用します。スクリプトは、最初にパターンがファイル名と一致しない場合、内部ループ内のループを防ぐためにnullglob
シェルオプション(リモートシェルがあると仮定)を設定します。bash
その後、現在のサーバー名に置き換えられるパターンを繰り返します/dir/xyz_$server*/details.txt
。$server
その後、ループはどのファイル名がパターンと一致するかについての短いメッセージを出力し、コードは変数が$pathname
一致しないことを確認します。地元のエスケープしてシェル$
。
$
inはエスケープさ$server
れないので、私たちが望むローカルシェルによって拡張されますが、$
inは$pathname
次のようにエスケープする必要があります。止めるローカル拡張。
IFS
デフォルト以外の値を設定する必要はありません。私はあなたが配列割り当てを書くことができるように、または他の理由でこれを行うと仮定しますが、上記のコードのどれもこれを要求しません。
私は疑似TTY割り当てを明示的にオフにするために-T
withを使用します。ssh
あなたできる必要と思われる場合は、スクリプトを単一の文字列として提供することもできます。
servers=(
blue
green
red
)
for server in "${servers[@]}"; do
ssh "$server" "
shopt -s nullglob
for pathname in /dir/xyz_$server*/details.txt; do
printf 'file path is \"%s\"\n' \"\$pathname\"
done"
done
この場合、含まれる各二重引用符がエスケープされていることを確認する必要があります(そうしないと、リモートシェルスクリプトを構成する二重引用符で囲まれた文字列は終了します)。