Bash - SSH経由でリモートホストにアレイを渡す

Bash - SSH経由でリモートホストにアレイを渡す

smartctl複数のリモートホストにログインし、(パッケージ内の)配列で定義されたディスクで実行されるBashスクリプトを作成しました。配列はリモートホストに渡すことができますが、スクリプトは最初の要素を繰り返し繰り返し表示します。配列を文字列として扱い、最初の要素以降のすべての要素を無視するようです。smartmontoolsDISKS

DISKS=("/dev/sda" "/dev/sdb" "/dev/sdc")
HOSTS=("Ariadne" "Nyx")

for i in "${HOSTS[@]}"; do
    ssh "$i" "bash" << EOF
        for j in "${DISKS[@]}"; do
            echo "$j"
        done
EOF
done

リモートホストで(2番目のforループの前に)配列を定義しようとしましたが、DISKS=("${DISKS[@]}")役に立ちませんでした。

SSHを介してアレイをリモートホストに正しく渡し、アレイを繰り返すにはどうすればよいですか?

答え1

次のように配列の定義をリモートで渡すことができますbash(これはリモートユーザーのログインシェルがあると仮定しますbash)。

DISKS_definition=$(typeset -p DISKS)
for i in "${HOSTS[@]}"; do
  ssh "$i" "$DISKS_definition"'
    for j in "${DISKS[@]}"; do
      echo "$j"
    done'
done

またはここで説明されている方法を使用する場合:

DISKS_definition=$(typeset -p DISKS)
for i in "${HOSTS[@]}"; do
  ssh "$i" bash <<EOF
    $DISKS_definition
    for j in "\${DISKS[@]}"; do
      echo "\$j"
    done
EOF
done

sshクライアントとサーバーが変数の受け渡しを許可している場合は、LC_*この操作も実行できます。

DISKS_definition=$(typeset -p DISKS)
for i in "${HOSTS[@]}"; do
  LC_DISKS=$DISKS_definition ssh "$i" bash <<'EOF'
    eval "$LC_DISKS"
    for j in "${DISKS[@]}"; do
      echo "$j"
    done
EOF
done

参照のすべての内容は、拡張を実行するローカルまたはリモートシェルを決定します。変数は二重引用符内で拡張され、ここでは区切り文字が引用符で囲まれていない文書<<'EOF'で拡張される<<EOFことに注意してください。ローカルシェルによって拡張されます)。

非ASCIIデータを含む配列の場合、ローカルおよびリモートシステムのロケールの文字セットが同じであることを確認するか、少なくともUTF-8以外のマルチバイト文字セットを使用しないでください。

bash-4.3$ locale charmap
ISO-8859-1
bash-4.3$ a=($'foo\xa3$(uname>&2)bar' baz)
bash-4.3$ a_definition=$(typeset -p a)
bash-4.3$ LC_ALL=zh_HK.big5hkscs bash -c "$a_definition"
Linux

別の文字セットに切り替えると、内容の$a_definition意味が異なります(ここでuname>&2コマンドが実行されます)。

答え2

コマンドラインがecho "$j"評価されると評価されるため、forループ内では実際には変数ではなく定数(および未定義)です。sshfor j...

この試み

for i in "${HOSTS[@]}"; do
    ssh "$i" "bash" << EOF
        for j in "${DISKS[@]}"; do
            echo "\$j"
        done
EOF
done

または、$DISKS配列に空白なしで安全な単語が含まれている場合。

for i in "${HOSTS[@]}"; do
    ssh "$i" "for j in ${DISKS[@]}; do echo \"\$j\"; done"
done

このような問題を解決するために私が好む方法は、必要なタスクを実行するスクリプトを作成して各リモートサーバーに常駐させることです。その後、各サーバーでスクリプトを呼び出すプロセスは簡単で簡単です。

for h in "${HOSTS[@]}"; do ssh "$h" /usr/local/bin/myscript; done

スクリプトが古くなるかどうか心配している場合は、実行する前にローカルシステムからファイルをコピーすることは不可能な作業ではありません。次のように動作します。

for h in "${HOSTS[@]}"
do
    scp -p /local/path/to/myscript "$h":myscript.$$
    ssh "$h" "myscript.$$; rm -f myscript.$$"
done

関連情報