Entziehen Sie sich einer Variablen, um sie als Inhalt eines anderen Skripts zu verwenden

18

In dieser Frage geht es nicht darum, wie ein ordnungsgemäß maskiertes Zeichenfolgenliteral geschrieben wird. Ich konnte keine verwandte Frage finden, bei der es nicht darum geht, Variablen für den direkten Verbrauch innerhalb eines Skripts oder durch andere Programme zu entziehen.

Mein Ziel ist es, ein Skript zu ermöglichen, andere Skripte zu generieren. Dies liegt daran, dass die Tasks in den generierten Skripten auf einem anderen Computer zwischen 0 und n Mal ausgeführt werden und sich die Daten, aus denen sie generiert werden, möglicherweise ändern, bevor sie (erneut) ausgeführt werden, sodass die Vorgänge direkt über ein Netzwerk ausgeführt werden nicht arbeiten.

Bei einer bekannten Variablen, die Sonderzeichen wie einfache Anführungszeichen enthalten kann, muss diese als vollständig maskiertes Zeichenfolgenliteral ausgegeben werden. Eine Variable, die beispielsweise Folgendes fooenthält, bar'bazsollte im generierten Skript folgendermaßen aussehen:

qux='bar'\''baz'

Das würde durch Anhängen "qux=$foo_esc"an die anderen Zeilen der Schrift geschrieben werden. Ich habe es mit Perl so gemacht:

foo_esc="'`perl -pe 's/('\'')/\\1\\\\\\1\\1/g' <<<"$foo"`'"

aber das scheint übertrieben.

Ich habe keinen Erfolg damit gehabt, es mit Bash alleine zu tun. Ich habe viele Variationen davon ausprobiert:

foo_esc="'${file//\'/\'\\\'\'}'"
foo_esc="'${file//\'/'\\''}'"

Aber entweder erscheinen zusätzliche Schrägstriche in der Ausgabe (wenn ich das tue echo "$foo") oder sie verursachen einen Syntaxfehler (weitere Eingaben werden von der Shell erwartet).

Walf
quelle
aliasund / oder setziemlich universell
mikeserv
@mikeserv Entschuldigung, ich bin mir nicht sicher, was du meinst.
Walf
alias "varname=$varname" varnameodervar=value set
mikeserv
2
@mikeserv Das ist nicht genug Kontext, damit ich verstehe, was Ihr Vorschlag tun soll, und auch nicht, wie ich ihn als generische Methode zum Ausblenden einer Variablen verwenden würde. Es ist ein gelöstes Problem, Kumpel.
Walf

Antworten:

25

Bash bietet für genau diesen Fall eine Option zur Parametererweiterung :

${parameter@Q}Die Erweiterung ist eine Zeichenfolge, die den Wert eines Parameters darstellt , der in einem Format angegeben ist, das als Eingabe wiederverwendet werden kann.

Also in diesem Fall:

foo_esc="${foo@Q}"

Dies wird in Bash 4.4 und höher unterstützt. Es gibt verschiedene Optionen für andere Formen der Erweiterung und zum gezielten Generieren vollständiger Zuweisungsanweisungen ( @A).

Michael Homer
quelle
7
Ordentlich, habe aber nur 4.2 was gibt bad substitution.
Walf
3
Das Z-Shell-Äquivalent ist "${foo:q}".
JdeBP
Rette wirklich mein Leben! "${foo@Q}"funktioniert!
Hao
@JdeBP Das Z-Shell-Äquivalent funktioniert nicht. Irgendwelche anderen Ideen für zsh?
Steven Shaw
1
Ich fand die Antwort: "$ {(@ qq) foo}"
Steven Shaw
10

Bash bietet einen printfeingebauten %qFormatbezeichner, der Shell- Escape-Vorgänge auch in älteren Versionen (<4.0) von Bash ausführt:

printf '[%q]\n' "Ne'er do well"
# Prints [Ne\'er\ do\ well]

printf '[%q]\n' 'Sneaky injection $( whoami ) `ls /root`'
# Prints [Sneaky\ injection\ \$\(\ whoami\ \)\ \`ls\ /root\`]

Dieser Trick kann auch verwendet werden, um Datenfelder von einer Funktion zurückzugeben:

function getData()
{
  printf '%q ' "He'll say hi" 'or `whoami`' 'and then $( byebye )'
}

declare -a DATA="( $( getData ) )"
printf 'DATA: [%q]\n' "${DATA[@]}"
# Prints:
# DATA: [He\'ll\ say\ hi]
# DATA: [or\ \`whoami\`]
# DATA: [and\ then\ \$\(\ byebye\ \)]

Beachten Sie, dass sich das integrierte Bash von printfdem printfDienstprogramm unterscheidet, das mit den meisten Unix-ähnlichen Betriebssystemen geliefert wird. Wenn der printfBefehl aus irgendeinem Grund das Dienstprogramm anstelle des eingebauten aufruft, können Sie ihn builtin printfstattdessen immer ausführen .

Dejay Clayton
quelle
Ich bin mir nicht sicher , wie das hilft , wenn das, was ich brauchen würde , gedruckt sei 'Ne'\''er do well', usw., dh Zitate in der Ausgabe enthalten.
Walf
1
@Walf Ich glaube, du verstehst nicht, dass die beiden Formen gleichwertig sind und beide genau so sicher sind wie die anderen. Eg [[ 'Ne'\''er do well' == Ne\'er\ do\ well ]] && echo 'equivalent!'will Echoequivalent!
Dejay Clayton
Das habe ich vermisst: P Ich bevorzuge jedoch die zitierte Form, da sie in einem Syntax-Highlighting-Viewer / -Editor einfacher zu lesen ist.
Walf
@Walf es scheint, als wäre Ihr Ansatz ziemlich gefährlich, wenn man bedenkt, dass in Ihrem Beispiel Perl die Übergabe eines Wertes wie 'hello'zum falschen Wert führt ''\''hello'', der eine unnötig führende leere Zeichenfolge (die ersten beiden einfachen Anführungszeichen) und ein unangemessenes nachgestelltes einfaches Anführungszeichen enthält.
Dejay Clayton
1
@Walf ist zur Verdeutlichung $'escape-these-chars'die ANSI-C- Anführungszeichenfunktion von Bash, mit der alle Zeichen in der angegebenen Zeichenfolge maskiert werden. Somit kann leicht eine Stringliteral erstellen , die eine neue Zeile im Dateinamen enthält (zB die $'first-line\nsecond-line')Verwendung \ninnerhalb dieses Konstrukts.
Dejay Clayton
7

Ich glaube, ich habe nicht RTFM. Das kann so gemacht werden:

q_mid=\'\\\'\'
foo_esc="'${foo//\'/$q_mid}'"

Dann echo "$foo_esc"gibts das erwartete'bar'\''baz'


Wie ich es tatsächlich benutze, ist mit einer Funktion:

function esc_var {
    local mid_q=\'\\\'\'
    printf '%s' "'${1//\'/$mid_q}'"
}

...

foo_esc="`esc_var "$foo"`"

Ändern Sie dies, um die printfintegrierte Lösung von Dejay zu verwenden:

function esc_vars {
    printf '%q' "$@"
}
Walf
quelle
3
Ich würde @ michael-homers Lösung verwenden, wenn meine Version dies unterstützt.
Walf
3

Es gibt verschiedene Lösungen, um einen var-Wert anzugeben:

  1. Alias
    In den meisten Shells (wo Alias ​​verfügbar ist) (außer csh, tcsh und wahrscheinlich anderen csh-ähnlichen):

    $ alias qux=bar\'baz
    $ alias qux
    qux='bar'\''baz'

    Ja, das funktioniert in vielen shähnlichen Schalen wie Bindestrich oder Asche.

  2. set
    Auch in den meisten Shells (wieder nicht csh):

    $ qux=bar\'baz
    $ set | grep '^qux='
    qux='bar'\''baz'
  3. gesetzte
    In einigen Shells (mindestens ksh, bash und zsh):

    $ qux=bar\'baz
    $ typeset -p qux
    typeset qux='bar'\''baz'             # this is zsh, quoting style may
                                         # be different for other shells.
  4. Exportieren
    Zuerst machen Sie:

    export qux=bar\'baz

    Dann benutze:
    export -p | grep 'qux=' export -p | grep 'qux='
    export -p qux

  5. Zitat
    echo "${qux@Q}"
    echo "${(qq)qux}" # von ein bis vier qs kann verwendet werden.

Isaac
quelle
Der Alias-Ansatz ist clever und scheint von POSIX vorgegeben zu sein. Für maximale Portabilität denke ich, dass dies der richtige Weg ist. Ich glaube , dass die Vorschläge Beteiligung grepmit exportoder setbrechen können auf Variablen eingebettete Zeilenumbrüche enthalten.
jw013