O comando SSH find não reconhece o diretório armazenado na variável local

O comando SSH find não reconhece o diretório armazenado na variável local

Consigo fazer SSH e ir para o diretório armazenado em uma variável local $out_dir. No entanto, não consigo entender por que o find | headcomando não funciona. EmExperimente #2, adicionei uma segunda barra invertida no final do findcomando:

Tente o erro nº 1: find: `/dir/on/remote/server': No such file or directory

ssh $user@$ip << EOF
    discard=$( find $out_dir -exec basename {} \; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
    logout
EOF

Tente o erro nº 2: syntax error near unexpected token `|'

ssh $user@$ip << EOF
    discard=$( find $out_dir -exec basename {} \\; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
    logout
EOF

Responder1

O motivo pelo qual você está obtendo o 'Arquivo ou diretório inexistente' na tentativa nº 1 é que toda a interpolação de variáveis ​​​​e comandos para o heredoc (o <<EOF) é feita localmente, antes de ser enviado ao host remoto.

Como você observou corretamente, $out_direstá sendo interpolado; você vê o diretório que deseja ver. Isso está acontecendo na sua máquina local antes de você fazer a chamada ssh. A substituição do comando ( $()) também está acontecendo localmente (mas você pretende fazer isso remotamente). Dito de outra forma, todo o achado está sendo processado localmente antes de ser enviado para a máquina remota - tudo incluído $()é processado pelo heredoc. Portanto, parece que ${out_dir}não está presente em sua máquina local - 'Esse arquivo ou diretório não existe'.

Se você quiser ver isso melhor por si mesmo, vamos reduzir o exemplo. Experimente isto:

$ ssh foo@localhost <<EOF
  echo "using account: $(whoami)"
EOF                            
Pseudo-terminal will not be allocated because stdin is not a terminal.
<truncated>
using account: vagrant

Claramente o conteúdo de $() está sendo executado localmente, já que minha conta local é 'vagrant'; se estivesse funcionando corretamente, a conta teria sido 'foo' porque eu especifiquei ssh foo@localhost.

A tentativa # 2 está quebrada porque você escapou do '\' (em vez do ';'). Find precisa ser -execencerrado e agora não é. Em vez disso, o comando termina abruptamente e você tem um ';' pendente. que encerra o comando bash. '|' espera encaminhar alguma entrada, mas não há nada especificado. Efetivamente, você criou isto:

$ | cat
-bash: syntax error near unexpected token `|'

Então, qual é uma solução funcional?

Bem, findposso fazer a exclusão para você:

ssh $user@$ip <<EOF
  find $out_dir -delete
EOF

É simples e direto. Se você estiver preocupado em escolher alguns arquivos e excluir outros, revise o arquivo find.

Dou o findconselho de 'revisar o' porque em sua versão atual, parece que você está tentando lidar com casos extremos:

  • o uso do nome base
  • o uso dehead -n -1

Mas não espero que esse tratamento funcione como você pretende.

Primeiro, a chamada basenameirá eliminar a maior parte do caminho e, no seu comando ssh, você não está fazendo nada para alterar o diretório. Sua execução ssh tentará deletar tudo relativo à home de ${user} - e ao especificar ${out_dir} você parece querer usar um diretório diferente de home! Novamente, deixe findfazer as exclusões para você.

Em segundo lugar, e posso estar errado com este, mas suspeito que você use head -n -1para se livrar de ${out_dir} da sua lista de arquivos de destino.

$ find junk/
junk/
junk/one
junk/two
junk/three

Com razão, você não deseja excluir arquivos junk/.

No entanto, tente isto:

$ seq 1 4
1
2
3
4
$ seq 1 4 | head -n -1
1
2
3

Isso não preserva a primeira linha, preserva a última. Experimente a cauda:

$ seq 1 4 | tail -n +2
2
3
4

Mas, novamente, deixe-nos findfazer as exclusões para você.

Se você precisar remover apenas arquivos, consulte -type f. Mas também existem padrões de exclusão e inclusão para uma filtragem mais avançada.

Responder2

Experimente isto:

ssh "$user@$ip" "out_dir=$out_dir;" '
    discard=$( find $out_dir -exec basename {} \; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
'

Observação:o comando remoto pretendido poderia ser mais simples e seguro, mas reclamar disso apenas confundirá o ponto.

Basicamente, você deve passar as partes quea)devetambémser ampliado nolocalmáquina e aqueles queb)deveria estarapenasser ampliado nocontrolo remotomáquina comoseparadoargumentos para ssh, o primeiro entre aspas duplas e o último entre aspas simples.

O Ssh juntará todos os argumentos de "comando" com espaços antes de passá-los como um único argumento para a -copção do shell de login do usuário na máquina remota.

Se o próprio comando remoto contiver aspas simples, você pode usar uma combinação de substituição de comando + aqui doc. Além disso, se o valor da variável "local" puder conter espaços e outros caracteres especiais, você deverá escapar deles:

out_dir='/path/to/ * happiness * '
out_dir=$(printf %s "$out_dir" | sed 's/[^a-zA-Z0-9_/.]/\\&/g')
ssh localhost out_dir="$out_dir;" "$(cat <<'EOT'
  echo '<$out_dir>' = "<$out_dir>"
EOT
)"

Observe que aspas simples em <<'EOF'; se omitido, o $out_dirdocumento aqui será expandido (na máquina local).

Versões recentes do bash têm o ${var@Q}formato, então você poderia simplesmente usar "out_dir=${outdir@Q};"em vez daquele feio echo | sed.

Passar o script remoto via stdin raramente (ou nunca!) é necessário.

informação relacionada