Como o SSH pode trabalhar com uma condição if?

8

Eu tenho uma ifdeclaração para calcular arquivos e excluir todos, exceto os três arquivos mais recentes. Mas eu quero executar este comando remotamente. Como posso combinar sshcom uma ifcondição?

Eu tentei isso, mas sem sucesso.

#!/bin/bash

ssh -t  test@192.168.94.139 "cd /var/www/test.com/backup ;

if [ $(ls  | wc -l) -lt 3  ]
then
    echo "Less"
else [ $(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]
    echo "deleted"
fi"

O erro que recebi:

ls: não pode acessar * .tgz: esse arquivo ou diretório não existe

Janith
fonte
Então, como posso personalizar isso?
Janith
@ user68186 Por que isso? O comando está entre aspas.
Lightness Races em Órbita
2
A $( )parte do comando é executada pelo shell local antes mesmo de iniciar o sshcomando. Isso é verdade tanto quando $( )autônomo quanto quando é encerrado por "s. No entanto, se $( )estivesse dentro de 's, não seria executado pelo shell local.
precisa saber é

Respostas:

3

NOTA: de fato, existem duas camadas para a pergunta aqui. Uma é 'Eu quero executar uma tarefa não trivial em um servidor remoto acessível via SSH'. O outro é: 'Estou tentando passar uma cadeia complexa para um comando, e o argumento acaba sendo diferente do que eu pretendia'. Estou respondendo à pergunta de baixo nível sem discutir se a abordagem usada é "correta" (conveniente, sem tendência a erros, segura etc.) para resolver a de alto nível. Conforme indicado pelas outras respostas e comentários, possivelmente não é.

Sua linha de comando está quase certa; você só precisa alterar um pouco as citações.

O principal problema é que as seqüências de aspas duplas são expandidas pelo shell local e, portanto, as $(...)partes serão avaliadas no sistema local. Para passá-los para o sistema remoto, você deve colocar o script entre aspas simples.

Você também tem algumas aspas incorporadas. No seu script original, existem os argumentos para os dois echos; se você alterar a cotação externa para aspas simples, será o script awk. Isso efetivamente resulta na omissão das aspas, o que não incomoda os echos, mas atrapalha o script do awk, pois o sinal maior que se torna o redirecionamento de saída. Portanto, depois de alterar as aspas externas para aspas simples, altere-as para aspas duplas.

Este é o seu script com a citação corrigida. O script pode ter outros problemas, eu apenas corrigi a sintaxe.

#!/bin/bash

ssh -t  test@192.168.94.139 'cd /var/www/test.com/backup ;

if [ $(ls  | wc -l) -lt 3  ]
then
  echo "Less"
else [ $(ls -t *.tgz|awk "NR >3"|xargs rm -f) ]
  echo "deleted"
fi'
pt314
fonte
Quero atribuir /var/www/test.com/backupa uma variável chamada "BACKUPDEST" e cd $BACKUPDESTvocê pode me dizer como fazer isso?
janith
1
Para onde você atribui o valor BACKUPDEST? Se isso acontecer no lado remoto, inclua-o normalmente no script. Se você deseja defini-lo localmente (por exemplo, calculá-lo no script local ou transmiti-lo como argumento da linha de comando), você pode alterar a primeira linha para, por exemplo "cd $BACKUPDEST"' ;- o shell expande a parte entre aspas duplas, mantém as aspas simples parte intacta, concatena os dois e passa o resultado como o último argumento para ssh.
Pt314
Uh, faça isso "cd '$BACKUPDEST'"' ;caso o caminho nessa variável contenha alguma estranheza (por exemplo, um espaço). Novamente: apenas dizendo que isso pode ser feito dessa maneira, não que isso deva ser feito dessa maneira.
Pt314
10

Sim, você pode executar scripts complexos via ssh

#!/bin/bash -e

ssh_cmd="$(cat <<-EOF
    cd /var/www/test.com/backup;

    if [ \$(ls  | wc -l) -lt 3  ]; then
        echo "less"
    elif [ \$(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]; then
        echo "deleted"
    else
        echo "something else"
    fi
EOF
)"

ssh -t test@192.168.94.139 "$ssh_cmd"

Este exemplo usa um documento do bash here para gerar a cadeia de comandos. De qualquer forma, a passagem de scripts via ssh é suscetível a erros, porque é difícil citar e escapar variáveis ​​(lembre-se da barra invertida antes dos comandos). Se o script ficar muito complexo, é melhor copiá-lo scpe executá-lo no host de destino.

Não tentei corrigir seu script , mas aqui está um exemplo de como a contagem e a exclusão de um host remoto podem funcionar:

#!/bin/bash -e

tmp_dir="$(mktemp -d)"

ssh_cmd="$(cat <<-EOF
    cd "$tmp_dir"

    cnt_tgz=(\$(find . -type f -name "*.tgz"))
    if [[ \${#cnt_tgz[@]} -lt 3 ]]; then
        echo "less"
    else
        rm "\${cnt_tgz[@]}"
        echo "deleted"
    fi
EOF
)"

touch "$tmp_dir/1.tgz"
ssh -t localhost "$ssh_cmd"
touch "$tmp_dir/2.tgz" "$tmp_dir/3.tgz"
ssh -t localhost "$ssh_cmd"

O ls -t *.tgznão vai funcionar, uma vez que o englobamento está acontecendo apenas no sistema local. Também usando lspara arquivos de contagem não é uma boa ideia, uma vez que também retorna entradas como ., ..e diretórios.

Simon Sudler
fonte
Eu tenho alguns erros. syntax error near unexpected token elif',elif [ $(ls -t |awk 'NR >3'|xargs rm -f) ]; then'
janith
Havia uma ;falta na primeira declaração if. Mas eu não consertei seu script! Existem muitas outras questões, por exemplo, ols *.tgz
Simon Sudler
1
"O ls -t * .tgz não funcionará, pois o globbing está acontecendo apenas no sistema local." - isso acontecerá no sistema remoto se você escapar corretamente. A versão original estava nua $(...)dentro de uma sequência de aspas duplas, então o shell local a avaliou. Escapá-lo como \$(...)mostrado no primeiro exemplo, ou usar aspas simples em todo o script, evita que o shell local o expanda, sendo enviado para o sistema remoto, expandido pelo shell remoto e o script funciona como pretendido.
Pt314
Este é o tipo de situação onde eu altamente recomendo o uso de aspas simples em vez de um documento aqui. Mesmo que você tenha algumas variáveis ​​que deseja inserir, é melhor usar uma string de aspas simples e printfexpandir variáveis ​​- isso ainda será mais fácil de gerenciar do que tentar escapar de todas as variáveis ​​do shell. Além disso, evitaria a necessidade de usar catapenas para capturar o documento aqui em uma variável!
Daniel Pryden
4

Eu acho que todo esse material de citação complicado é prova suficiente para não usá-lo, mas usar um script. Se você deseja evitar várias sshconexões, canalize o script para o outro host e deixe executar em um comando:

Arquivo local, diga myscript.sh:

cd /var/www/test.com/backup;

if [ $(ls  | wc -l) -lt 3  ]; then
    echo "less"
elif [ $(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]; then
    echo "deleted"
else
    echo "something else"
fi

Então:

cat myscript.sh | \
    ssh -t test@192.168.94.139 \
    "cat - > /tmp/ms.sh && sh /tmp/ms.sh && rm /tmp/ms.sh"

Ou (evitando um uso inútil de gato ):

ssh -t test@192.168.94.139 \
    "cat - > /tmp/ms.sh && sh /tmp/ms.sh && rm /tmp/ms.sh" \
    < myscript.sh

Isso canaliza o script local myscript.shpara o lado remoto, onde é redirecionado para um arquivo (temporário) /tmp/ms.sh, executado e finalmente removido.

Nota: Não verifiquei o script original quanto a erros, mas só queria mostrar a ideia. Nenhuma citação propensa a erros é necessária e todos os comandos no script são executados no lado remoto.

PerlDuck
fonte
Não há necessidade de salvar o script em um arquivo temporário, apenas coloque-o em um shell adequado, ou seja ssh user@host bash <myscript.sh.
#
2
Lembre-se, porém, que o redirecionamento funciona apenas quando o próprio script não tenta ler da entrada padrão.
chepner
@ pt314 Sim, mas veja Enviar um script para o bash?
PerlDuck
Sim, o redirecionamento tem suas limitações. Ele também evita vários problemas de segurança associados à gravação (e execução) de um arquivo temporário com nome previsível. Se você usar um script temporário (por opção ou necessidade), crie-o usando mktempou algo similarmente seguro.
Pt314
3

Eu preferiria colocar o script na instância remota e apenas executá-lo através do ssh, mas aqui está minha sugestão de como isso pode ser feito da maneira que você deseja:

#!/bin/bash

HOST='[email protected]'
REMOTE_PATH='/var/www/test.com/backup'

COMMAND1="ssh \"$HOST\" 'ls \"$REMOTE_PATH\" | wc -l'"
COMMAND2="ssh \"$HOST\" 'ls -t \"$REMOTE_PATH\"/*.tgz'"
COMMAND3="xargs ssh \"$HOST\" rm -rf"

if [[ $(eval "$COMMAND1") -le 3 ]]
then
    echo "Less"
else
    eval "$COMMAND2" | awk 'NR > 3' | eval "$COMMAND3" && echo "Deleted"
fi

Notas:

  • A expressão condicional -lté substituída por -le.
  • eval - constrói o comando concatenando argumentos.
  • Não tenho certeza se realmente precisamos dessas citações extras \"dentro da $COMMAND{1..3}expressão, mas decido adicioná-las.
pa4080
fonte