Wie kann ich ein Skript auf einem Remotecomputer über SSH ausführen, aber lokale Quelldateien

Wie kann ich ein Skript auf einem Remotecomputer über SSH ausführen, aber lokale Quelldateien

Ich habe ein lokales Skript, das ich auf einem Remotecomputer ausführen möchte, ohne es auf den Computer zu kopieren. Daher verwende ich: ssh user@remote < local-script.sh

Das funktioniert, aber wenn ich eine Quellanweisung in local-script.sh hinzufüge, um beispielsweise eine andere Datei als Quelle zu verwenden source ./another-local-script.sh, sucht local-script.sh, wenn es auf dem Remote-Remote ausgeführt wird, nach der Quelldatei auf dem Remote-Remote. Gibt es eine Möglichkeit, dies so zu lösen, dass die Quelldateien zuerst lokal aufgelöst werden?

Antwort1

Sie können dies nicht transparent tun.

Sie können Ihr Bash-Skript so manipulieren, dass es Dateien von Ihrem lokalen Computer per Reverse Tunnel abruft, bevor Sie sie beziehen. Das ist allerdings nicht besonders sauber.

Viel besser ist es, einfach alle benötigten Dateien zu übertragen und sie dann wie folgt auszuführen:

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

Antwort2

Wenn Sie nur eine begrenzte Anzahl von Eingabedateien haben, deren Inhalt Sie kontrollieren, können Sie awkoder ähnliches verwenden, um den sourceBefehl im Standardeingabestream durch die Quelldatei zu ersetzen. Beispiel:

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

wobei „desource“ das Skript ist

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

Dies gleicht nur Zeilen ab, deren erstes Wort „source“ ist, und nimmt das zweite Wort als einzufügende Datei. getlineliest eine Zeile aus dieser Datei (in $0) und gibt am Dateiende 0 zurück. Die printZeile wird einfach durch die nicht übereinstimmenden Zeilen kopiert.

Dies ist offensichtlich in seiner Anwendung stark eingeschränkt und erfordert beispielsweise einige Rekursionsarbeit, wenn die eingefügte Datei auch sourceBefehle enthält.


alternative Getline mit einer Variable statt $0:

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

alternatives Skript mit sed. Es liest die Datei zweimal, daher ist für die Verwendung der Dateiname erforderlich (lassen Sie das "<" weg, d. h.: 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

Dies verwendet sed ein erstes Mal, um die sourceZeilen abzugleichen, die Zeilennummer (=) auszugeben und nur den Dateinamen zu belassen (s/// verwendet dasselbe Muster erneut). Das zweite sed nimmt die Zeilennummer, hängt die nächste Zeile (N) an und ersetzt die neue Zeile und den folgenden Dateinamen (.* ist der Rest der Zeile) durch das, was zu einem sed-Befehl wird, um die gewünschte Datei zu lesen und die ursprüngliche Zeile zu löschen. Dabei trwerden die Semikolons im Befehl in neue Zeilen umgewandelt. Der resultierende Befehl wird an ein drittes sed für die ursprüngliche Datei weitergegeben.

Antwort3

Danke für die Hilfe, @meuh, aber da ich Ihre Beispiele unter Mac OS XI nicht zum Laufen bringen konnte, habe ich eine Funktion gefunden, die das Problem behebt, obwohl ich sicher bin, dass viele von Ihnen dies verbessern könnten:

# 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"
}

Im Grunde liest es die Datei Zeile für Zeile durch und fügt jede Zeile in eine Variable namens tmp ein. Wenn die Zeile eine Quellanweisung enthält, ruft es den Dateinamen ab und löst den absoluten Pfad zur Datei auf (und geht davon aus, dass alle relativen Pfade relativ zum übergebenen Skript sind). Es führt Prüfungen durch, um sicherzustellen, dass die Dateien vorhanden sind, und fügt dann den Inhalt dieser Dateien anstelle der Quellanweisungen in die Variable tmp ein.

verwandte Informationen