![O comando SSH find não reconhece o diretório armazenado na variável local](https://rvso.com/image/170291/O%20comando%20SSH%20find%20n%C3%A3o%20reconhece%20o%20diret%C3%B3rio%20armazenado%20na%20vari%C3%A1vel%20local.png)
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 | head
comando não funciona. EmExperimente #2, adicionei uma segunda barra invertida no final do find
comando:
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_dir
está 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 -exec
encerrado 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, find
posso 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 find
conselho 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 de
head -n -1
Mas não espero que esse tratamento funcione como você pretende.
Primeiro, a chamada basename
irá 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 find
fazer as exclusões para você.
Em segundo lugar, e posso estar errado com este, mas suspeito que você use head -n -1
para 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 find
fazer 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 -c
opçã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_dir
documento 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.