Übertragen von Umgebungsvariablen durch SSH / Zitieren in bash / sh / csh / tcsh

3

Ich möchte eine Umgebungsvariable über SSH übertragen.

Der "richtige" Weg ist die Verwendung von SendEnv / ~/.ssh/environment, aber dafür muss der Server AcceptEnv oder PermitUserEnvironment unterstützen, was in meinem Fall nicht der Fall ist.

Daher überlege ich mir stattdessen, die Variable auf der Remote-Site wie folgt festzulegen:

FOO=val
export FOO
ssh server export FOO=$FOO'; do_stuff_which_uses_FOO'

Dieser Teil ist einfach. Ich möchte eine generische Lösung, unabhängig vom Inhalt von $ FOO. Z.B

FOO="  '\""
export FOO
QFOO=`quote "$FOO"` # quote will return "\ \ \'\\\""
export QFOO
ssh server export FOO=$QFOO'; do_stuff_which_uses_FOO'

Dies funktioniert unabhängig davon, ob es sich bei der sendenden oder empfangenden Shell um sh oder bash handelt.

Ich brauche es jedoch auch, um mit csh / tcsh zu arbeiten. Und ich werde nicht im Voraus wissen, welche Shell das empfangende Ende ausführt. Das bedeutet, dass ich etwas codieren muss, das sowohl in / bin / sh als auch in / bin / csh funktioniert.

Bisher habe ich es geschafft, es für sh / bash zum Laufen zu bringen:

ssh server // followed by the below quoted
eval `echo $SHELL | grep -E "/(t)?csh" > /dev/null && echo setenv FOO \\\ \\\ \\\\\'\\\\\" || echo export FOO=\\\ \\\ \\\\\'\\\\\";` ; echo "$FOO"

Ich kann es auch für csh / tcsh zum Laufen bringen (der Benutzer cshhat csh als Login-Shell):

ssh csh@server // followed by the below quoted
eval `echo $SHELL | grep -E "/(t)?csh" > /dev/null && echo setenv FOO \\\ \\\ \\\\\'\\\\\" || echo export FOO=\\\ \\\ \\\\\'\\\\\";` ; echo "$FOO"

Wenn $ FOO * ist oder? es funktioniert gut mit BASH:

ssh server eval\ \`echo\ \$SHELL\ \|\ grep\ -E\ \"/\(t\)\?csh\"\ \>\ /dev/null\ \&\&\ echo\ setenv\ FOO\ \\\\\\\\\\\*\\\;\ \|\|\ echo\ export\ FOO=\\\\\\\\\\\*\\\;\`\;echo\ \"\$FOO\"\ a;

Aber es schlägt fehl mit csh:

ssh csh@server eval\ \`echo\ \$SHELL\ \|\ grep\ -E\ \"/\(t\)\?csh\"\ \>\ /dev/null\ \&\&\ echo\ setenv\ FOO\ \\\\\\\\\\\*\\\;\ \|\|\ echo\ export\ FOO=\\\\\\\\\\\*\\\;\`\;echo\ \"\$FOO\"\ a;
No match.
FOO: Undefined variable.

Es scheint * und? sich weigern, mit zitiert zu werden.

Um diese Frage zu beantworten, sollte Ihre Lösung:

  • in der Lage sein, eine Umgebungsvariable auf den Remote-Server zu übertragen
  • Verwenden Sie nicht SendEnv / AcceptEnv / PermitUserEnvironment
  • Funktioniert unabhängig davon, ob es sich bei der Quell-Shell um sh / bash / csh / tcsh handelt
  • Funktioniert unabhängig davon, ob es sich bei der Zielshell um sh / bash / csh / tcsh handelt
  • arbeiten unabhängig vom Inhalt der Umgebungsvariablen. Insbesondere sollte es zumindest funktionieren für: \ n * Leerzeichen '"? <>! $ \ Und jede Kombination davon.

Wenn Sie einen besseren Weg finden, die Variable zu übertragen, als sie in Anführungszeichen zu setzen, ist das auch in Ordnung.

Ole Tange
quelle
Es hört sich so an, als ob wir Ihre Hausaufgaben machen. Warum können Sie die Optionen ssh SendEnv oder AcceptEnv nicht verwenden? Dafür sind sie DESIGNED.
UtahJarhead
Es soll für --env in GNU Parallel verwendet werden. GNU Parallel wird von geschätzten 25000 Benutzern verwendet. Viele dieser Benutzer haben auf den von ihnen verwendeten Systemen keinen Root-Zugriff (häufig handelt es sich um Compute-Cluster). Wenn AcceptEnv keinen Root-Zugang benötigt hätte, wäre ich damit einverstanden. Wie Sie unten sehen können, habe ich eine Lösung gefunden, die für alle außer \ n funktioniert.
Ole Tange
Ah, das macht Sinn. Danke für die Klarstellung.
UtahJarhead

Antworten:

1

Ich habe jetzt ein funktionierendes Modell für alle Charaktere außer \ n:

sub shell_quote_scalar {
    # Quote the string so shell will not expand any special chars
    # Returns:
    #   string quoted with \ as needed by the shell
    my $a = shift;
    $a =~ s/([\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\*\>\<\~\|\; \"\!\$\&\'\202-\377])/\\$1/g;
    $a =~ s/[\n]/'\n'/g; # filenames with '\n' is quoted using \'
    return $a;
}

sub env_quote {
    my $v = shift;
    $v =~ s/([ \n\&\<\>\(\)\;\'\{\}\t\"\$\`\*\174\!\?\~])/\\$1/g;
    return $v;
}

my @qcsh = map { my $a=$_; "setenv $a " . env_quote($ENV{$a})  } @vars;
my @qbash = map { my $a=$_; "export $a=" . env_quote($ENV{$a}) } @vars;

$Global::envvar =
    join"",
    (q{echo $SHELL | grep -E "/t?csh" > /dev/null && }
     . join(" && ", @qcsh)
     . q{ || }
     . join(" && ", @qbash)
     .q{;});

print shell_quote_scalar($Global::envvar);
Ole Tange
quelle
0

Können Sie die Variable einfach base64-codieren? Der einfachste Weg, damit umzugehen, besteht wahrscheinlich darin, die Daten einfach als binär zu behandeln.

export QFOO=`echo $FOO | base64 --wrap=0`
ssh server "export FOO=`echo \"$QFOO\" | base64 --decode --wrap=0`; <command>"

Möglicherweise müssen Sie mit dem Zitat in der sshZeile herumspielen, aber das ist der Kern der Sache. Dies sollte Shell-Spezifika in Bezug auf das Zitieren entfernen, könnte aber einige mit Unterbefehlen einführen (ich bin mit nichts außerhalb von nicht viel vertraut bash).

Darth Android
quelle
Keine schlechte Idee. Obwohl ich wahrscheinlich nicht davon ausgehen kann, dass base64 installiert ist (base64 ist Teil von GNU coreutils und wurde erst 2006 eingeführt - daher können wir nicht davon ausgehen, dass es auf Nicht-GNU-Systemen und auf GNU-Systemen vor 2006 installiert ist). Ich kann jedoch davon ausgehen, dass eine alte Version von Perl installiert ist. Verwenden Sie also (Un-) Pack mit der Vorlage "u *", das sollte machbar sein.
Ole Tange
Nicht codierte Zeichenfolgen enthalten *. ARGH! Aber hexadezimal codiert sollte funktionieren.
Ole Tange
Ich kann keinen Weg finden, dies zum Laufen zu bringen: Da der Befehl sowohl unter csh als auch unter bash funktionieren muss, muss ich ihn auswerten. Und sobald ich es auswerte dann das * und? wird Probleme in csh verursachen. echo setenv U perl -e '($a="212223c2a425262f28293d410a417b5b5d7d5e7e2a41") =~ s/(..)/chr hex $1/eg;print $a'
Ole Tange
@OleTange Ist opensslverfügbar? hints.macworld.com/article.php?story=20030721010526390
Darth Android
Nee. Aber selbst wenn base64 verfügbar wäre, würde dies das Problem nicht beheben: Ich muss immer noch die Auswertung durchführen und es ist dieser Schritt, der (t) csh einbricht, wenn die Variable * enthält, oder? (Bash funktioniert gut und csh / tcsh auch, solange die Variable kein * oder? Enthält.)
Ole Tange