Execução remota de um script de shell que usa um arquivo local como um parâmetro de entrada

1

Usando Linux, bash e OpenSSH, posso executar um script de shell em um host remoto de pelo menos três maneiras:

# Method 1: Script is never stored on the remote host
ssh <username>@<hostname> 'bash -s' < myShellScript

# Method 2: Script is permanently stored on the remote host
ssh <username>@<hostname> 'myShellScript'

# Method 3: Copy script to the remote host, execute it, and delete it
scp myShellScript <username>@<hostname>:~
ssh <username>@<hostname> 'myShellScript; rm -f myShellScript'

Eu prefiro o método 1 ao método 2 porque ele impede a proliferação de cópias do script e as dores de cabeça de manutenção associadas.

Eu prefiro o método 1 ao método 3 porque ele é mais rápido e geralmente "mais limpo" do que copiar e excluir o script.

Mas há um problema que eu preciso resolver se quiser ter sucesso na minha meta de seguindo o método 1. myShellScript é desta forma:

#!/bin/bash

# Do stuff...
myProgram input myProgramInputFile
# Do more stuff...

Aqui, myProgram é um utilitário padrão que está disponível em todos os hosts remotos com os quais posso querer interagir. No entanto, myProgramInputFile é um arquivo regular que existe somente na minha máquina local. Não existe em nenhum dos hosts remotos.

Gostaria de poder "empacotar" myShellScript e myProgramInputFile em um "pacote" legal que é executado no host remoto via SSH sem armazenar explicitamente qualquer coisa no sistema de arquivos remoto, permanentemente ou temporariamente. Isso é possível?

    
por Dave 26.06.2017 / 22:36

1 resposta

1

Eu preferiria copiar pelo menos myProgramInputFile ( Método 3 ); Eu acho que seria mais robusto. Existe, no entanto, uma maneira de fazê-lo funcionar com o único Método 1 .

Minha abordagem

Eu aconselho você a fazer um script (local) que copie o arquivo apropriado para o lado remoto, aciona a execução e limpa depois.

Vamos precisar de três arquivos locais: myLocalScript (executável), myRemoteScript e myProgramInputFile .

O conteúdo de myLocalScript :

#!/bin/bash

# step 1: storing command line arguments to meaningful names
ssh_command="ssh $1"
script="$2"
input="$3"

# step 2: creating remote temporary file
remote_input=$($ssh_command 'mktemp')

# step 3: copying the content of local input file to the remote temporary file
cat "$input" | $ssh_command "cat > \"$remote_input\""

# step 4: sourcing the script to the remote side
$ssh_command "input=\"$remote_input\" bash -s" < "$script"

# step 5: removing the remote temporary file
$ssh_command "rm \"$remote_input\""

myRemoteScript será assim:

# shebang is not needed, this script will effectively be sourced

# Do stuff…
myProgram input "$input"
# Do more stuff…

Os arquivos myProgramInputFile (local) e myProgram (remoto) permanecerão como estão na configuração atual.

Uso local:

./myLocalScript <username>@<hostname> myRemoteScript myProgramInputFile

Notas técnicas e explicações:

  • Na etapa 2 mktemp cria um arquivo em um diretório temporário na máquina remota. Não queremos usar mktemp -u para evitar a situação quando alguém cria um arquivo com o mesmo nome entre a etapa 2 e a etapa 3 .
  • Na etapa 3 , poderíamos usar scp se usássemos mktemp -u . O arquivo temporário remoto já existe, então usamos cat para gravar nele.
  • Na etapa 4 myRemoteScript precisa saber o caminho para o arquivo temporário no contexto da máquina remota. É por isso que passamos nossa variável local $remote_input as input para o controle remoto bash . Dessa forma, quando myRemoteScript é alimentado para o dito bash , o $input indica o caminho do arquivo temporário.
  • Se quebrarmos o controle remoto bash (digamos, com uma única Ctrl + c ), então myLocalScript avançará para a etapa 5 e fazer a limpeza de qualquer maneira. Esta é a razão pela qual a limpeza não está no final de myRemoteScript , embora possa ser.

Único Método 1 abordagem

Graças a aqui documentos você pode incorpore myProgramInputFile em myShellScript .

Isso será fácil se myProgramInputFile for um arquivo de texto e myProgram puder ler stdin (talvez com myProgram input - sintaxe? ou talvez quando input for omitido?)

myShellScript será assim:

# shebang is not needed, this script will effectively be sourced

# Do stuff…

myProgram input - <<"EOF"
The content of myProgramInputFile is pasted here,
it continues here
and here,
and so on.
EOF

# Do more stuff…

Em seguida, vamos executá-lo como no Método 1 :

ssh <username>@<hostname> 'bash -s' < myShellScript

Se myProgram não puder ler stdin , mas o sistema remoto permitir que você use /proc/self/ , então devemos escrever a linha crucial em myShellScript da seguinte forma:

myProgram input /proc/self/fd/0 <<"EOF"

Se myProgramInputFile for mais que texto , devemos codificá-lo localmente (consulte uuencode , base64 ) e decodificar no lado remoto. Também devemos verificar se não há linha EOF no texto codificado, altere o delimitador de documento aqui, se for o caso. Note que base64 não usa _ então E_O_F é perfeitamente seguro neste caso (mas talvez não com uuencode , não sei).

Como uma prova de conceito , gzipou o favicon superusuário, codificado com base64 e incorporado no script. Eu escolhi demonstrar a abordagem /proc/self/fd/0 com cp , mas também pode ser cat > ~/SUfavicon.ico ; ou melhor gzip -cd > ~/SUfavicon.ico no início do pipe.

E, claro, no seu caso, haveria myProgram em vez de cp .

Note que neste caso {} são importantes, eles fazem o documento aqui ser direcionado para base64 -d .

# shebang is not needed, this script will effectively be sourced

{ base64 -d | gzip -cd | cp /proc/self/fd/0 ~/SUfavicon.ico ; } <<"E_O_F"
H4sICE3cXFkCA2Zhdmljb24uaWNvAO2XT2jTUBzHf2lnO1HXIAiKYHuSIYhzoCgIlV1EdtDDTmMg
CIKI7DQ9Taq71Il0VfBSmP9QmKziYRd3GCoqu40JIhtiB7sIOm1AcJ1r8/y+5bXEZ1KbtH07uMCH
3/pLvvm8lzYvC5FGAdJ1Qo3R+RaivUQUi1mfx9BPodeOns77ZPXXthZy25IgX2bbZkrz5uIjmvz+
lPKCBZAEmkOeH8/ABOjXt1AXb86NUg+Ov/AtSwOoy6gl1PYq+UtuA0RuHjCw32d+Dn6znrzwH/SZ
fy78w6gBr3lkOpH9DPg53nrNLzykVpzjDp8DatFLfrCXArkHlBLzfwcOgUANed4zE320/es4fRDz
7/Zw/XiP3TxHm5bGK9f/mJ/rv5StfP++8sjmxLU/7DWP7FHkiqhfUNuq5J+AM7h/jvDm/F06geOv
AJ5bBX0uwyvnTV5x/2bE/fsGmQK8U6hdTsGhsMXItI3XNq5hPQlbVPZjtRiKgKi1zvDV5bh9ndH/
9jDG/iCi6yuAVaOcXczuYy78ALPgOtjhND/Za/OzBvjtfARb19HP6a3Dr5GHDS4NvJL8g3795GOD
77bkv6rYn97w/9f+Ecl/Q7G/X/K/B0GF/t1gRRpDBoRU+MUYLjusgznR39lsvxjDAFh2GMdPFX4x
hrNOzwQF178NPHNwl8BEk39//Bk06eC9BfY06veH/Z1gFNwHKRAW/pMO8+5p9P2H/Wnp+A7hT0ru
qWasP1X88vqbbpL/FJgBs+AF0FX6PTx/Nvzr6x9W7B+T/BdV+fm7BshL/g6F/ozknq7z/Uur0bsL
PJbcv8ABRe9/Jekz///ntNt4m/z+N+M27xr8qz79n8A90M2fv//6vso+I0QJEF8jYcQNVnAjCiIG
KwYNZgIWtP4uhA2zNQriFRzz/NwwJF5yHzNCGgiy3wHSerE2FQAA
E_O_F

Execute como antes:

ssh <username>@<hostname> 'bash -s' < myShellScript

Em seguida, você encontrará ~/SUfavicon.ico na máquina remota.

    
por 05.07.2017 / 15:35