Bash: prompt remoto interativo

16

Eu tenho um script que se conecta a um servidor remoto e verifica se algum pacote está instalado:

ssh root@server 'bash -s' < myscript.sh

myscript.sh:

OUT=`rpm -qa | grep ntpdate`
if [ "$OUT" != "" ] ; then
    echo "ntpdate already installed"
else
    yum install $1
fi

Este exemplo pode ser simplificado. Aqui está o myscript2.shque tem o mesmo problema:

read -p "Package is not installed. Do you want to install it (y/n)?" choise

Meu problema é que o bash não consegue ler minhas respostas interativamente.

Existe uma maneira de executar o script local remotamente sem perder a capacidade de solicitar ao usuário?

Anthony Ananich
fonte
Você pode elaborar? Não está realmente claro o que você está pedindo? Um pouco mais do seu código seria útil.
slm
11
Apenas um FYI, a razão pela qual isso não funciona é porque você está passando seu script pelo STDIN. Portanto, quando o bash lê o STDIN, ele obtém seu script (ou nada, já que ele já leu o script inteiro e não resta mais nada).
10133 Patrick
11
Recurso útil relacionado a isso: backreference.org/2011/08/10/…
slm

Respostas:

22

Tente algo como isto:

$ ssh -t yourserver "$(<your_script)"

As -tforças uma alocação tty, $(<your_script)lê o arquivo inteiro e nestes casos passa o conteúdo como um argumento para ssh, que será executado pelo shell do usuário remoto.

Se o script precisar de parâmetros, passe-os após o script:

$ ssh -t yourserver "$(<your_script)" arg1 arg2 ...

Funciona para mim, mas não tenho certeza se é universal.

Esteira
fonte
3
É exatamente o que eu estava procurando, obrigado!
Anthony Ananich
Solução agradável, mas existe uma maneira de passar um argumento your script, mantendo as vantagens da sintaxe usada na sua resposta? Usando ssh -t yourserver "$(<your_script --some_arg)"apenas resultados em bash: --some_arg: command not found.
Sampablokuper
11
@sampablokuper: tryssh ... $(<script) script_arg1 script_arg2 ...
Mat
@ Mat, obrigado, WFM :) Talvez adicioná-lo ao corpo da sua resposta?
sampablokuper
3

Seu problema é que sshinicia um shell não interativo de login na máquina remota. A solução fácil óbvia seria copiar o script para o servidor remoto e executá-lo a partir daí:

scp myscript.sh root@server:/tmp && ssh root@server /tmp/myscript.sh

Se a cópia não é uma opção por qualquer motivo, gostaria de modificar o script para a primeira conexão e verificar se $1está instalado, em seguida, reconecte e instalar conforme necessário:

OUT=$(ssh root@server rpm -qa | grep "$1");
if [ "$OUT" != "" ] ; then
    echo "$1 already installed"
else
   read -p "Package $1 is not installed. Do you want to install it (y/n)?" choice
   if [ "$choice" -eq "y" ]; then
       ssh root@server yum install "$1"
   fi
fi
terdon
fonte
Era disso que eu tinha medo. A conexão com o ssh é a operação mais longa e eu estava tentando evitá-la.
Anthony Ananich
@AnthonyAnanich Você pode economizar o tempo de estabelecimento da conexão configurando uma conexão mestre .
Gilles 'SO- stop be evil'
@ Gilles eu estava pensando em sugerir isso, mas eu pensei que não iria funcionar com a minha sugestão. Se a primeira conexão ssh atuar como mestre, ela será fechada após as $OUT=$(ssh root@server rpm -qa | grep "$1");saídas e a segunda conexão levará tanto tempo quanto a primeira. Estou errado?
terdon
11
@terdon Execute algo como ssh -M foo.example.com sleep 99999999ou ssh -M foo.example.com read <somefifocomo mestre e mate-o explicitamente (com killou echo done >somefifo) quando terminar.
Gilles 'SO- stop be evil'
0

Aqui está uma boa explicação .

Então eu adaptei o script para

hostname
echo -n "Make your choice :"
read choice
echo "You typed " ${choice}
echo done

e isso não funcionou.

então mudei o script para o controle remoto para evitar o redirecionamento local no ssh. (Meus comandos estão em um arquivo chamado f )

cat f | ssh user@remotehost.com 'cat >remf'
ssh user@remotehost bash remf

Isso funcionou. Aqui está a saída:

christian@clafujiu:~/tmp$ ssh localhost bash tmp/f
christian@localhost's password: 
Linux clafujiu 2.6.32-52-generic #114-Ubuntu SMP Wed Sep 11 19:00:15 UTC 2013 i686 GNU/Linux
Sun Nov 10 14:58:56 GMT 2013
Make your choice :abc
You typed  abc
done

Como o @terdon mencionou que a intenção original era executar scripts locais remotamente, a cópia remota pode ser automatizada; este é apenas um exemplo, tudo em uma linha.

REMID=`cat f |ssh user@remotehost 'cat > remf_$$; echo $$'` ;ssh root@redtoadservices.com "bash remf_${REMID} ; rm -v remf_${REMID}"
X Tian
fonte
2
Como isso "funcionou"? Você executou esse script com bash -s < script.sho OP? Você poderia incluir a explicação na sua resposta em vez de vincular a ela?
terdon
Opa, desculpe, esqueci de adicionar algo crítico. Também copiei o script para o controle remoto para evitar o redirecionamento local. Acrescentarei isso à minha resposta para deixar claro.
X Tian
11
Se você copiar o script para o controle remoto, o problema desaparecerá. A questão do OP é que ele está tentando fornecer informações interativas para um script local em execução remota. Claro que vai correr de você copiá-lo. O OP deseja executar esse script ao conectar-se a um servidor remoto, presumivelmente a muitos servidores remotos. Duvido que copiar seja prático, a menos que você o automatize.
terdon
0

Eu procurei soluções para esse problema várias vezes no passado, mas nunca encontrei uma totalmente satisfatória. A inserção no ssh perde sua interatividade. Duas conexões (scp / ssh) são mais lentas e seu arquivo temporário pode ficar por aí. E todo o script na linha de comando geralmente acaba escapando do inferno.

Recentemente, descobri que o tamanho do buffer da linha de comando é geralmente bastante grande ('getconf ARG_MAX> 2MB, onde eu olhei). E isso me fez pensar em como eu poderia usar isso e mitigar a questão que escapava.

O resultado é:

ssh -t <host> /bin/bash "<(echo "$(cat my_script | base64 | tr -d '\n')" | base64 --decode)" <arg1> ...

ou usando um documento e um gato aqui:

ssh -t <host> /bin/bash $'<(cat<<_ | base64 --decode\n'$(cat my_script | base64)$'\n_\n)' <arg1> ...

Eu expandi essa ideia para produzir um script de exemplo do BASH totalmente funcional sshxque possa executar scripts arbitrários (não apenas o BASH), onde os argumentos também podem ser arquivos de entrada local, através do ssh. Veja aqui .

SourceSimian
fonte
Interessante, mas como e / ou quando isso é melhor do que a resposta de Mat ?
1576 Scott
11
A resposta de Mat é um bom primeiro passe (que eu costumo usar), no entanto, ele não pode lidar com um script de conteúdo arbitrário, como "caracteres que precisam ser escapados. E também é restrito a scripts BASH. Minha solução é um caso geral para qualquer conteúdo de script de qualquer linguagem de script instalada. Além disso, no sshx , demonstro ainda a serialização de arquivos de argumentos, o que é bastante bacana.
SourceSimian
(1) Você pode publicar um MCVE de um script para o qual a resposta de Mat falha (e a sua funciona)? (2) Consulte O que há de errado com "echo $ (stuff)" ou "echo` stuff` "?  Você echo "$(cat my_script | base64)" | base64 --decodeparece equivalente (-ish) a cat my_script | base64 | base64 --decode, que parece muito com um não-op.
20919 Scott Scott
Olá @Scott. (1): Considere o script: echo "ARG1=$1, USER=$USER, END". a) $ ssh host "$(<my_bash)" foo-> ARG1=, USER=user, END foo. b) $ ssh host bash "<(echo $(cat my_bash|base64) | base64 --decode)" foo-> ARG1=foo, USER=user, END. Aqui (a) está efetivamente em execução: bash -c $'#!/bin/bash\necho "ARG1=$1, USER=$USER, END" foo'ou algo semelhante. Observe que o argumento não funciona. Onde (b) está em execução: bash <(echo IyEvY...5EIgo= | base64 --decode) foo. (2) Eu usei base64 como a codificação de transporte.
SourceSimian 21/05/19