Como posso executar um script em uma máquina remota através de ssh, mas originar arquivos locais

Como posso executar um script em uma máquina remota através de ssh, mas originar arquivos locais

Eu tenho um script local que desejo executar em uma máquina remota sem copiá-lo para a máquina, então estou usando: ssh user@remote <local-script.sh

Isso funciona, mas se eu adicionar uma instrução de origem em local-script.sh para originar outro arquivo, por exemplo source ./another-local-script.sh, quando local-script.sh é executado no controle remoto, ele procura o arquivo de origem no controle remoto. Existe alguma maneira de resolver isso para que os arquivos de origem sejam resolvidos localmente primeiro?

Responder1

Você não pode fazer isso de forma transparente.

Você pode alterar seu script bash para obter arquivos de sua máquina local usando túnel reverso, antes de obtê-los, mas não é muito limpo.

Muito melhor é transferir todos os arquivos necessários e executá-los assim:

scp local-script.sh another-script.sh user@remote
ssh user@remote local-script.sh

Responder2

Com um conjunto restrito de arquivos de entrada cujo conteúdo você controla, você pode usar awkou similar para substituir o sourcecomando no fluxo stdin pelo arquivo de origem. Por exemplo,

desource <local-script.sh | ssh user@remote 

onde desource é o script

#!/bin/sh
awk '$1=="source" && NF>=2 {
      file = $2; while((getline <file)>0)print $0
      close(file); next
}
{ print }' "$@"

Isso apenas corresponde às linhas cuja primeira palavra é "fonte" e considera a segunda palavra como o arquivo a ser inserido. getlinelê uma linha desse arquivo (em $0) e retorna 0 no final do arquivo. A printlinha apenas copia as linhas não correspondentes.

Obviamente, isso é altamente limitado em sua aplicação e, por exemplo, precisará de algum trabalho para ser recursivo se o arquivo incluído também tiver sourcecomandos.


getline alternativo com uma variável em vez de $0:

while((getline inp <file)>0)print inp

script alternativo usando sed. Ele lê o arquivo duas vezes, então o uso precisa do nome do arquivo (descarte o "<" ou seja desource local-script.sh | ssh user@remote:)

#!/bin/bash
file=${1?}
cmd=$(  sed -n '/^[ \t]*source[ \t]/{=;s///;p}' <$file |
    sed  '/^[0-9]/{N;s/\n\(.*\)/{r \1;d;}/}' |
    tr ';' '\012' )
sed "$cmd" <$file

Isso usa sed uma primeira vez para combinar as sourcelinhas, imprime o número da linha (=) e deixa apenas o nome do arquivo (s/// reutiliza o mesmo padrão). O segundo sed pega o número da linha, anexa a próxima linha (N) e substitui a nova linha e o nome do arquivo seguinte (.* é o resto da linha) pelo que se tornará um comando sed para ler o arquivo desejado e excluir a linha original. O trconverte o ponto e vírgula do comando em novas linhas. O comando resultante é dado a um terceiro sed no arquivo original.

Responder3

Obrigado pela ajuda @meuh, mas como não consegui fazer seus exemplos funcionarem no Mac OS X, criei uma função que faz o trabalho, embora tenha certeza que muitos de vocês poderiam melhorar isso:

# desource - substitute source statements in file with contents from sourced file
function desource {
  if [ ! -f "$1" ]; then
    echo "Invalid file: $1"
    return
  fi
  declare tmp
  while read -r line; do
    if [ "${line:0:1}" == '#' ]; then continue; fi
    file=$(echo "$line" | perl -nle 'print $1 if m{(?>source )(.+)}' | sed 's|"||g')
    if [ -n "$file" ]; then
      case "${file:0:1}" in
        '/') ;;
        '~') ;;
        '$') ;;
          *) file="$(cd `dirname $1` && pwd)/$file"
      esac
      file=$(echo "$file" | sed "s|~|$HOME|" | sed "s|\$HOME|$HOME|")
      file="$(cd `dirname $file` && pwd)/$(basename $file)"
      if [ -f "$file" ]; then
        tmp="$tmp\n$(cat $file)"
      else
        echo "Invalid file: $file"
        return
      fi
    else
      tmp="$tmp\n$line"
    fi
  done < "$1"
  echo -e "$tmp"
}

Basicamente ele lê o arquivo linha por linha inserindo cada linha em uma variável chamada tmp. Se a linha contiver uma instrução source, ela obterá o nome do arquivo, resolvendo o caminho absoluto para o arquivo (e assumindo que quaisquer caminhos relativos sejam relativos ao script passado). Ele verifica se os arquivos existem e, em seguida, adiciona o conteúdo desses arquivos à variável tmp no lugar das instruções de origem.

informação relacionada