Se eu ls
arquivar sem um loop for em um host remoto, tudo estará bem, no entanto, se eu capturar o arquivo ls
em uma variável e tentar echo
cada arquivo, ele falhará porque as variáveis não se expandem/têm um valor.
O que eu quero dizer:
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
Mas na verdade preciso percorrer a saída ls
para poder fazer coisas com os arquivos, mas a variável não se expande:
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
Como posso $i
expandir ao executar um loop no host remoto?
O problema é que a substituição do comando que você deseja executar nos servidores remotos é executada localmente. Isso se deve à substituição que ocorre entre aspas duplas, o que significa que o shell local calculará sua expansão antes de chamar
ssh
.Além disso, ler a saída de
ls
é desaconselhável (veja Por que *não* analisar `ls` (e o que fazer em vez disso)? ).Em vez de:
Isso usa um "documento aqui" sem aspas como script que é enviado aos servidores remotos. O script primeiro define a
nullglob
opção shell (supondo que o shell remoto sejabash
) para evitar o loop no loop interno se o padrão não corresponder a nenhum nome de arquivo. Em seguida, ele itera sobre o padrão/dir/xyz_$server*/details.txt
onde$server
será substituído pelo nome do servidor atual.O loop então imprime uma mensagem curta sobre quais nomes de arquivos correspondem ao padrão, e o código garante que a
$pathname
variável não seja expandida pelo shell local escapando do$
.Observe que
$
in$server
não tem escape e, portanto, será expandido pelo shell local, que é o que queremos, mas que$
in$pathname
precisa ter escape para suprimir a expansão local.Não precisamos definir
IFS
nada que não seja padrão. Presumo que você fez isso para poder escrever a atribuição do array como fez, ou por algum outro motivo, mas nenhum código acima precisa disso.Estou usando
-T
withssh
para desativar explicitamente a alocação de pseudo-TTY.Você também pode fornecer o script como uma única string se achar que isso é necessário:
Nesse caso, você também deve garantir que cada aspas duplas incorporadas tenham escape (ou elas encerrariam a string entre aspas duplas que constitui o script de shell remoto).
Nunca faça um loop for sobre a saída de ls; é literalmente para isso que serve * globbing, e todo o seu uso de ls é totalmente supérfluo.
Isso resolve todo o problema, honestamente, porque atualmente você está executando localmente
$(…)
em vez de no host de destino.