Bash - Passando um array para hosts remotos via ssh

2

Eu escrevi um script Bash que faz login em vários hosts remotos e executa smartctl (do pacote smartmontools ) em discos definidos na matriz DISKS . Eu sou capaz de passar a matriz para os hosts remotos, mas o script apenas ecoa o primeiro elemento repetidamente. Parece que está tratando a matriz como uma string e ignorando todos os elementos após a primeira.

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

Eu tentei definir a matriz no host remoto (antes do segundo loop): DISKS=("${DISKS[@]}") , mas isso não ajudou.

Como posso passar uma matriz adequadamente para um host remoto via SSH e, em seguida, iterar pela matriz?

    
por Andy Forceno 04.02.2017 / 23:42

2 respostas

3

Você pode passar a definição da matriz para o controle remoto bash da seguinte forma (supondo que o shell de login do usuário remoto seja bash ):

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

Ou se estiver usando a abordagem here-document:

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

Se o seu cliente e servidor ssh permitir a passagem de LC_* variables, você também poderá fazer

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

Tudo está na citação que determina qual dos shell local ou remoto faz as expansões. Lembre-se variáveis são expandidas dentro de aspas duplas e aqui-documentos onde o delimitador não é citado (observe o <<'EOF' no último exemplo que impede expansões dentro do documento aqui enquanto nos exemplos anteriores (com <<EOF ) nós tivemos que usar barras invertidas para coisas que não queremos que sejam expandidas pelo shell local).

Para arrays que contenham dados não-ASCII, você deverá certificar-se de que o conjunto de caracteres do locale seja o mesmo na máquina local e remota, ou pelo menos evitar conjuntos de caracteres de múltiplos bytes diferentes de UTF-8. / p>

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

Ao mudar para um conjunto de caracteres diferente, o conteúdo de $a_definition teve um significado diferente (fazendo com que o comando uname>&2 seja executado).

    
por 05.02.2017 / 01:52
0

Seu echo "$j" é avaliado quando a linha de comando ssh é avaliada, portanto, dentro do loop for j... for, é na verdade uma constante (e indefinida) em vez de uma variável.

Tente isso

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

Ou ainda, se a sua matriz $DISKS contiver palavras seguras sem espaço em branco.

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

Minha maneira preferida de resolver esse tipo de problema é criar um script que execute o trabalho necessário e que resida em cada servidor remoto. Chamar o script em cada servidor torna-se um processo direto e simples:

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

Se você está preocupado com o script estar desatualizado, não é impossível copiar o arquivo do sistema local antes de executá-lo. Algo como isso provavelmente funcionaria:

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

Tags