Was ist bei Bash-Variablen der Unterschied zwischen $myvar und „$myvar“? (Bestimmtes merkwürdiges Verhalten)

Was ist bei Bash-Variablen der Unterschied zwischen $myvar und „$myvar“? (Bestimmtes merkwürdiges Verhalten)

Allgemeine Frage:

Ich weiß, dass die Variable in Bash myvarauf zwei Arten verwendet werden kann:

# Define a variable:
bash$ myvar="two words"

# Method one to dereference:
bash$ echo $myvar
two words

# Method two to dereference:
bash$ echo "$myvar"
two words

Im obigen Fall ist das Verhalten identisch. Dies liegt an der Funktionsweise echo. In anderen Unix-Dienstprogrammen macht es einen großen Unterschied, ob die Wörter mit Anführungszeichen gruppiert werden:

bash$ myfile="Cool Song.mp3"
bash$ rm "$myfile"            # Deletes "Cool Song.mp3".
bash$ rm $myfile              # Tries to delete "Cool" and "Song.mp3".

Ich frage mich, was die tiefere Bedeutung dieses Unterschieds ist. Und vor allem: Wie kann ich genau sehen, was an den Befehl übergeben wird, damit ich sehen kann, ob es richtig zitiert wird?

Spezifisches ungerades Beispiel:

Ich werde einfach den Code mit dem beobachteten Verhalten schreiben:

bash$ mydate="--date=format:\"%Y-%m-%d T%H\""
bash$ git log "$mydate"    # This works great.
bash$ git log $mydate
fatal: ambiguous argument 'T%H"': unknown revision or path not in the working tree.

Warum brauche ich die Anführungszeichen? Was genau sieht git-log, nachdem die Variable ohne Anführungszeichen dereferenziert wurde?

Aber sehen Sie sich nun Folgendes an:

bash$ nospace="--date=format:\"%Y-%m-%d\""
bash$ git log $nospace        # Now THIS works great.
bash$ git log "$nospace"      # This kind of works, here is a snippet:

# From git-log output:
Date:   "2018-04-12"

Igitt, warum enthält die gedruckte Ausgabe jetzt Anführungszeichen? Es sieht so aus, als würden Anführungszeichen, wenn sie unnötig sind, nicht entfernt, sondern als wörtliche Anführungszeichen interpretiert, genau dann, wenn sie nicht notwendig sind.

Was wird Git als Argumente übergeben?Ich wünschte, ich wüsste, wie ich das herausfinden könnte.

Um die Sache noch komplizierter zu machen, habe ich ein Python-Skript geschrieben, argparsedas einfach alle Argumente ausgibt (so wie Bash sie interpretiert, also mit Anführungszeichen, wo Bash denkt, dass sie Teil des Arguments sind, und mit Wörtern, die gruppiert oder nicht gruppiert sind, wie Bash es für richtig hält), und das Python- argparseSkript verhält sich sehr rational. Leider glaube ich, argparsedass es stillschweigend ein bekanntes Problem mit Bash behebt und so das durcheinandergebrachte Zeug verschleiert, das Bash ihm übergibt. Das ist nur eine Vermutung, ich habe keine Ahnung. Vielleicht vermasselt git-log heimlich, was Bash ihm übergibt.

Oder vielleicht weiß ich einfach überhaupt nicht, was los ist.

Danke.

Bearbeitet Bearbeiten:Lassen Sie mich dies jetzt sagen, bevor es irgendwelche Antworten gibt: Ich weiß, dass ichVielleichtSetzen Sie das Ganze in einfache Anführungszeichen und maskieren Sie die doppelten Anführungszeichen nicht. Das funktioniert bei meinem anfänglichen Problem mit git-log tatsächlich etwas besser, aber ich habe es in einigen anderen Kontexten getestet und es ist ungefähr genauso unvorhersehbar und unzuverlässig. Irgendetwas Seltsames ist mit dem Anführen innerhalb von Variablen im Gange. Ich werde nicht einmal all die seltsamen Dinge posten, die mit einfachen Anführungszeichen passiert sind.

Bearbeitung 2 – Das funktioniert auch nicht:Ich hatte gerade diese wunderbare Idee, aber sie funktioniert überhaupt nicht:

bash$ mydate="--date=format:%Y-%m-%d\ T%H"
bash$ git log "$mydate"

# Git log output has this:
Date:   2018-04-12\ T23

Es steht also nicht in Anführungszeichen,AberEs hat einen wörtlichen Backslash in der Datumszeichenfolge. Auch git log $mydateohne Anführungszeichen treten Fehler auf, mit dem Backslash-Leerzeichen in der Variable.

Antwort1

Anderer Ansatz:

Wenn Sie ausführen git log --format="foo bar", werden diese Anführungszeichen von Git nicht interpretiert – sie werden von der Shell entfernt (und schützen den zitierten Text vor dem Aufteilen). Dies führt zu einem einzigen Argument:

  • --format=foo bar

Wenn jedoch nicht zitiertVariablenexpandiert werden, durchlaufen die Ergebnisse eine Worttrennung, abernichtdurch Entfernen der Anführungszeichen. Wenn Ihre Variable also enthält --format="foo bar", wird sie in diese Argumente erweitert:

  • --format="foo
  • bar"

Dies kann wie folgt überprüft werden:

  • printf '%s\n' $variable

... sowie jedes einfache Skript, das die empfangenen Argumente druckt.

  • #!/usr/bin/env perl
    für $i (0..$#ARGV) {
        drucken ($i+1)." = ".$ARGV[$i]."\n";
    }
    
  • #!/usr/bin/env python3
    System importieren
    für i, arg in enumerate(sys.argv):
        drucken(i, "=", arg)
    

Wenn Sie Bash immer zur Verfügung haben, ist der bevorzugte Workaround die Verwendung vonAnordnungVariablen:

myvar=( --format="foo bar" )

Dabei wird die übliche Analyse während der Zuweisung und nicht während der Erweiterung durchgeführt. Sie verwenden diese Syntax, um den Inhalt der Variablen zu erweitern, wobei jedes Element sein eigenes Argument erhält:

git log "${myvar[@]}"

Antwort2

Warum funktioniert Ihr ursprünglicher Befehl nicht?

bash$ mydate="--date=format:\"%Y-%m-%d T%H\""
bash$ git log "$mydate"    # This works great.
bash$ git log $mydate
fatal: ambiguous argument 'T%H"': unknown revision or path not in the working tree.

Du fragst:

Warum brauche ich die Anführungszeichen? Was genau sieht git-log, nachdem die Variable ohne Anführungszeichen dereferenziert wurde?

Wenn Sie keine doppelten Anführungszeichen verwenden $mydate, wird die Variable wörtlich erweitert und die Shell-Zeile sieht vor der Ausführung wie folgt aus:

git log --date=format:"%Y-%m-%d T%H"
                      ^————————————^—————— literal quotes

Hier haben Sie (unnötigerweise) wörtliche Anführungszeichen hinzugefügt, indem Sie \"in der Variablenzuweisung verwendet haben.

Da der BefehlWorttrennung, giterhält drei Argumente, log, --date-format:"%Y-%m%-dund T%H", und beschwert sich daher, dass kein Commit oder Objekt mit dem Namen gefunden wurde T%H".


Was ist der richtige Ansatz?

Wenn Sie Argumente zusammenhalten möchten und dieses Argument Leerzeichen enthält, müssen Sie es in Anführungszeichen setzen.Setzen Sie Variablen grundsätzlich immer in Anführungszeichen.

Dies funktioniert auch, wenn sich innerhalb der Variable ein Leerzeichen befindet:

mydate="--date=format:%Y-%m-%d T%H"
git log "$mydate"

Das dritte Argument für gitist nun $mydate, einschließlich des Leerzeichens, das Sie ursprünglich angegeben haben. Alle Anführungszeichen werden von der Shell entfernt, bevor sie an übergeben werden git.

Sie benötigen die zusätzlichen Anführungszeichen einfach nicht. Wenn Sie nur gitein Argument sehen möchten, setzen Sie dieses Argument bei der Übergabe der Variablen in Anführungszeichen "$mydate".


Außerdem fragen Sie:

bash$ nospace="--date=format:\"%Y-%m-%d\""
bash$ git log $nospace        # Now THIS works great.
bash$ git log "$nospace"      # This kind of works, here is a snippet:

# From git-log output:
Date:   "2018-04-12"

Ihre Frage:

Igitt, warum enthält die gedruckte Ausgabe jetzt Anführungszeichen?

Denn du hast wieder miteinbezogenwörtlichAnführungszeichen im Argument (durch Escapen), die beispielsweise in „echte“ Anführungszeichen umgewandelt werden, wenn Sie vergessen, die Variable in Ihrem eigentlichen Befehl in Anführungszeichen zu setzen. Ich sage „vergessen“, weil die Verwendung nicht in Anführungszeichen gesetzter Variablen in Shell-Befehlen normalerweise nur Ärger einbringt – und hier wird ein Fehler rückgängig gemacht, den Sie beim Angeben der Variable gemacht haben.

PS: Ich weiß, das ist alles verwirrend, aber das ist Bash, und es folgt einigen klaren Regeln. Hier gibt es keinen Fehler. EinVerwandter Aufsatzzu Dateinamen in der Shell ist ebenfalls sehr aufschlussreich, da es die Frage der Leerzeichenbehandlung in Bash anspricht.

verwandte Informationen