Ersetzen Sie den Schrägstrich durch den Schrägstrich im letzten Befehl

10

Ich habe den Befehl cd C:\foo\bar\von PowerShell nach Cygwin kopiert und albern erwartet, dass er ausgeführt wird. Ich versuche nun einen Wechsel zu laufen alle ersetzen \mit /:

$ !!:gs/\\/\/
bash: :gs/\\/\/: substitution failed

Ich bin mir nicht sicher, warum die Substitution fehlgeschlagen ist.

Ich habe auch versucht:

$ !!:gs/\\/q

Nur um zu sehen, ob der Ersatz das Problem war. Es ist nicht. Jetzt bin ich neugierig!

Warum bekomme ich "Substitution fehlgeschlagen"?

Tanner Faulkner
quelle
2
Ich habe momentan kein Cygwin zur Hand, aber ich dachte, dass Windows-Pfade funktionieren ... haben Sie versucht, das Argument einzeln zu zitieren? Iecd 'C:\foo\bar'
mpy
@mpy Das funktioniert eigentlich auch! Ich wusste nicht, dass ich nicht an Substitution gebunden bin.
Tanner Faulkner
1
"Wäre auch offen für eine Antwort mit zsh". Ihr Original !!:gs/\\/\/funktioniert in zsh…
Kamil Maciorowski
@TannerFaulkner fc -s '\'='/' -1funktioniert unter der Ubuntu LTS-Standard-Bash. Lassen Sie mich wissen, ob es auch unter Cygwin funktioniert. Ich habe mehr Wörter in der Antwort gepostet.
Hastur
1
Ich denke, das allgemeine Bash-Verhalten hier ist, dass die Backslashes als Escape verarbeitet werden, bevor die Substitution in erfolgt !!:gs/\\/\//. @Hastur 's fc -s '\'='/' -1bekommt das erwartete Verhalten. Dies könnte ein Fehler in Bash sein.
Quixotic

Antworten:

6

Ein Schrägstrich ist auch das Trennzeichen Ihres Ersatzbefehls. Sie müssen ihn daher als Ersatzzeichenfolge maskieren, um die Substitution nicht vorzeitig zu beenden

!!:gs/\\/\//

oder klarer, wie in den Kommentaren vorgeschlagen, indem Sie etwas anderes als einen Schrägstrich als Trennzeichen verwenden

!!:gs|\\|/|

Aber dies scheint nur zu funktionieren tcsh(die Shell, die normalerweise dort zugewiesen wird, wo ich bin). Ich kann den Befehl nicht zum Arbeiten bringen, bashda es so aussieht, als würde das Entkommen beim ersten Argument von nicht funktionieren:s

angefügt
quelle
Keine Freude :( Gleicher Fehler i.imgur.com/QFQR0xF.png
Tanner Faulkner
1
Nur eine Bemerkung: !!:gs|\\|/Vielleicht etwas lesbarer.
Mpy
1
Ich konnte es nicht dazu bringen, in Bash zu arbeiten. Das erste :sArgument von scheint kein echter Regex zu sein
am
4

Kurze Antwort: das eingebaute fc(Fix-Kommando) von Bash

fcist der Befehl, built-in in der bash, vorgenommen Bearbeiten und Befehle der Geschichte erneut auszuführen.
Es ist auch auf CygWin vorhanden und funktioniert auf allen Linux-Distributionen, auf denen ich getestet habe:

fc  -s '\'='/' -1

Einige Erklärungen

  • fcist der in bash integrierte Befehl zum Auflisten oder Bearbeiten und erneuten Ausführen von Befehlen aus der Verlaufsliste.
  • -1übernimmt den letzten Befehl in der Geschichte. Beachten Sie, dass sogar !!als definiert (gelesen von man bash) ist

    !! Refer to the previous command. This is a synonym for! -1 '.

  • -sein Muster durch ein anderes ersetzen. Diesmal ab help fc(eingebauter Befehl also help):

    Mit dem Format "fc -s [pat = rep ...] [Befehl]" wird COMMAND erneut ausgeführt, nachdem die Ersetzung OLD = NEW durchgeführt wurde.

    Ok sie meinen pat=repstatt OLD=NEW...

  • Die Muster werden von der Shell gelesen, daher müssen wir sie mit Anführungszeichen schützen: '\'und '/'.

Einige Worte mehr darüber, warum Sie "Substitution fehlgeschlagen" erhalten.

Es scheint, dass für den s Modifikator (noch) nicht die Ersetzung des Backslash-Zeichens implementiert ist \, das ist das Escape- Zeichen . Um sicherzugehen, dass wir zum Beispiel den Code der gnu-Version der Bash-Verlaufserweiterung sehen sollten (aber es gab den obigen Befehl, um zu erhalten, was Sie versucht haben ... also nehme ich es faul ...).

Einige Notizen:

  1. Wir glauben, dass es bei jedem RegEx funktioniert, mit dem wir arbeiten sed, aber es ist nicht garantiert. Der Backslash ist der Escape-Charakter der Erweiterung und das Problem ist hier. Darüber hinaus hängt das Verhalten der Erweiterung mit den shoptOptionen zusammen, sodass wir von Fall zu Fall sehen sollten ...

  2. Wenn Sie den String cd C:\Foo\Barin Ihre Bash-Shell einfügen , wird er erweitert und für den Interpreter als cd C:FooBarangezeigt. In dieser Form wird es auch in der $_internen Variablen gespeichert .
    Wenn Sie stattdessen eingefügt cd "C:\Foo\Bar"oder cd 'C:\Foo\Bar'in die $_ Variable, sollten Sie finden C:\Foo\Bar.
    Da die Verlaufserweiterung unmittelbar nach dem Lesen einer vollständigen Zeile ausgeführt wird, bevor die Shell sie in Wörter zerlegt, könnten Sie versucht sein, sie mit einem mehr oder weniger einfachen Bashismus zu verwenden , z. B. mit einer Ableitung von (möglicherweise Hinzufügen :poder :q, "", Parsen und so weiter ...)

    !!:0 ${_//\\/\/}

    Dies ist der Moment, um sich daran zu erinnern, dass es nicht sicher ist , mit Pfad und Dateinamen zu spielen , insbesondere wenn sie aus der Windows-Zwischenablage stammen (lesen Sie im Allgemeinen die Seite Warum nicht analysieren ls?, Es hängt im Wesentlichen mit der Möglichkeit zusammen, Tabulatoren zu verwenden). Leerzeichen und Zeilenumbrüche als korrekte Zeichen für die Dateinamen und die Verzeichnisnamen ...).
    Wenn Sie einen mit der Maus aufgenommenen Text einfügen , können Sie auch ein führendes Leerzeichen einfügen. Dadurch wird möglicherweise vermieden, dass Ihr Befehl im Verlauf beendet wird (dies hängt von den Shell-Optionen ab ...). Wenn ja, ist Ihre folgende !!Anweisung ein nicht kontrollierter Befehl ... (siehe ein Beispiel in einer anderen Antwort ).Dies ist ein nicht benötigtes materielles Risiko .

Fazit

Geschichte Erweiterungen einführen Worte aus der Verlaufsliste in den Eingangsstrom, so dass es leicht zu wiederholen Befehle, legen Sie die Argumente zu einem vorherigen Befehl in die aktuelle Eingabezeile oder fix Fehler in früheren Befehlen schnell.

Wenn es nicht einfach ist, denke ich, dass wir etwas falsch machen ;-)


Ad nauseam: ein kleines Experiment

Ich habe dann histverifyin der Shell aktiviert ...

shopt -s histverify
echo C:\Foo\Bar
!!:s|C|D| {1,2}A

dann drücke ich Enterund als verifizierte Erweiterung finde ich

echo D:\Foo\Bar {1,2}A

dann drücke ich Enternochmal und es hallt

D:FooBar 1A 2A

Dies scheint darauf hinzudeuten , dass die substitution failedin der Geschichte Expansion vor der verarbeiteten erzeugt Expansion Brace , so vor allem , und es scheint zu bestätigen , dass die sGeschichte Modifikator nicht (noch) die Substitution des Prozesses \Charakter als echte regex. ..

Hastur
quelle
2

Sie können seddas Ergebnis ersetzen und ausführen.

Es gibt mehrere mögliche Varianten für einen solchen Befehl, aber bei meiner Bash hat nur diese funktioniert:

A=$(echo "!!\\" | sed 's,\\,/,g');eval $A

Das \\wird hinzugefügt, !!falls der letzte Befehl mit einem Backslash beendet wird. Ohne sie wird die Shell verwirrt und fragt nach der Fortsetzung der Linie.

Sie können dies auch als Bash-Funktion in die Datei einfügen ~/.bashrc:

function slash {
   A=$(history | tail -n 2 | head -n 1 | cut -c 8- | sed 's,\\,/,g')
   eval $A
}

So funktioniert das auf meinem Bash unter Windows:

Bash

So erklären Sie, wie der A=Befehl der Shell-Variablen den richtigen Wert zuweist A:

  • tailNimmt die letzten beiden Zeilen aus dem Befehlsverlauf, der die Zeilen von cd \mnt\cgefolgt von sich slashselbst angibt. Der Teil von history | tail -n 2kann auch geschrieben werden als history 2.
  • head Nehmen Sie die erste Zeile cd \mnt\c
  • cut schneidet die von gelieferte Zeilennummer aus history
  • sed ersetzt alle Schrägstriche durch Schrägstriche
  • eval führt den Inhalt der Variablen aus A
harrymc
quelle
Hallo Harry. Es A=$(echo "!!\\" | sed 's,\\,/,g');eval $Atut mir leid zu sagen, dass dies auf meiner GNU-Bash-Version 4.3.11 (1) nicht funktioniert, wenn der Befehl mit einem Backslash beendet wurde. Sie sind gezwungen, ein Leerzeichen hinzuzufügen, z. B. C:\Foo\Bar\ , wie Sie sagten, wartet die Shell auf die Fortsetzung der Zeile. Sie können die Eingabetaste auch zweimal drücken. Im ersten Fall erhalten Sie C:/Foo/Bar /(mit einem Leerzeichen vor dem /; im zweiten Fall wird ein Fehler generiert. Die Funktion funktioniert
Hastur
Ich habe die Funktion geschrieben, weil (1) die Verwendung viel einfacher ist und (2) der Einzeiler zu implementierungsabhängig schien.
Harrymc
-1

Ich glaube nicht, dass du das kannst. Sie müssen erneut kopieren / einfügen.

Das Problem hängt nicht mit dem Schrägstrich zusammen, sondern auch mit dem Ersatzbefehlstrennzeichen, da der Ersatzbefehl jedes Trennzeichen akzeptiert. Es geht um die zu ersetzenden Schrägstriche, die bereits als einfache nutzlose Charakterhemmnisse auf der rechten Seite behandelt wurden.

Wenn Sie den Ersatzbefehl aufrufen, ist der Backslash bereits aus der Quelle verschwunden. Versuchen Sie einfach, den Befehl wie er ist ( !!) erneut aufzurufen . Anstatt genau den gleichen Befehl einschließlich zu drucken c:\foo, wird der Befehl abzüglich bereits verarbeiteter Backslashes ausgeführt, die dazu führen c:foo.

Und natürlich können Sie einen fehlenden Charakter nicht finden oder ersetzen. Wenn Sie dies versuchen, wird ein Fehler ausgelöst.

A. Loiseau
quelle
1
Gut. Das ist schließlich falsch, da dieser Test tatsächlich funktioniert: echo c:\Foogefolgt von !!:gs,F,\\f,. Aber das Ersetzen der Backslash-Flucht selbst scheint ein Schmerz zu sein.
A. Loiseau