Sostituisci barra rovesciata con barra rovesciata nell'ultimo comando


10

Ho copiato il comando cd C:\foo\bar\da PowerShell a Cygwin e mi aspettavo scioccamente che fosse eseguito. Sto ora cercando di eseguire una sostituzione per sostituire tutto \con /:

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

Non sono sicuro del motivo per cui la sostituzione non è riuscita.

Ho anche provato:

$ !!:gs/\\/q

Solo per vedere se il problema era la sostituzione. Non è. Adesso sono curioso!

Perché visualizzo "sostituzione fallita"?


2
Non ho Cygwin a portata di mano in questo momento, ma ho pensato che i percorsi di Windows stanno funzionando ... hai provato a citare l'argomento? cd 'C:\foo\bar'
Vale a

@mpy Anche questo funziona davvero! Non sapevo che non ero bloccato con la sostituzione.
Tanner Faulkner,

1
"Sarebbe aperto a una risposta anche usando zsh". Il tuo originale !!:gs/\\/\/funziona in zsh ...
Kamil Maciorowski il

@TannerFaulkner fc -s '\'='/' -1funziona con la bash predefinita di Ubuntu LTS. Fammi sapere se funziona anche con Cygwin. Ho pubblicato più parole nella risposta.
Hastur,

1
penso che il comportamento bash generale qui sia che le barre rovesciate vengano elaborate come escape prima che avvenga la sostituzione !!:gs/\\/\//. @Hastur fc -s '\'='/' -1ottiene il comportamento previsto. questo potrebbe essere un bug in bash.
donchisciottesco

Risposte:


6

una barra in avanti è anche il delimitatore del comando sostitutivo, quindi è necessario evitarlo come stringa di sostituzione in modo da non terminare anticipatamente la sostituzione

!!:gs/\\/\//

o più chiaramente come suggerito nei commenti, usando qualcosa che non sia la barra come delimitatore

!!:gs|\\|/|

Ma questo sembra funzionare solo tcsh(la shell comunemente assegnata dove sono), non riesco a far funzionare il comando in bashquanto sembra che l'escaping non funzioni sul primo argomento di:s


Nessuna gioia :( Stesso errore i.imgur.com/QFQR0xF.png
Tanner Faulkner

1
Solo un'osservazione: !!:gs|\\|/potrebbe essere un po 'più leggibile.
mpy,

1
Non sono riuscito a farlo funzionare a Bash. Il primo argomento di non :ssembra essere una vera regex
apposto il

4

Risposta breve: l'integrato fc(comando di correzione) di bash

fcè il comando, incorporato nella shell bash, creato per modificare e rieseguire i comandi della cronologia.
È presente anche su CygWin e funziona su tutte le distribuzioni Linux su cui ho testato:

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

Alcune spiegazioni

  • fcè il comando incorporato bash per elencare o modificare e rieseguire i comandi dall'elenco cronologico.
  • -1prenderà l'ultimo comando nella storia. Si noti che anche !!definito (leggere man bash) come

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

  • -sper sostituire un modello con un altro. Questa volta da help fc(comando integrato così help):

    Con il formato `fc -s [pat = rep ...] [command] ', COMMAND viene rieseguito dopo l'esecuzione della sostituzione OLD = NEW.

    Ok intendono pat=repinvece di OLD=NEW...

  • Gli schemi verranno letti dalla shell, quindi dobbiamo proteggerli con citazione: '\'e '/'.

Qualche parola in più sul perché stai ricevendo "sostituzione fallita"

Sembra che per il s modificatore non sia (ancora) implementata la sostituzione del carattere barra rovesciata \, ovvero quella di fuga. Per essere sicuri dovremmo vedere il codice, ad esempio, della versione gnu dell'espansione della cronologia bash (ma c'era il comando sopra per ottenere quello che stavi cercando di fare ... quindi lo prendo pigro ....).

Alcune note:

  1. Siamo indotti a pensare che funzionerà con ogni RegEx con cui ci troviamo a lavorare sed, ma non è garantito. La barra rovesciata è il carattere di fuga dell'espansione e il problema è qui. Inoltre, il comportamento dell'espansione è correlato alle shoptopzioni, quindi dovremmo iniziare a vedere caso per caso ...

  2. Quando si incolla la stringa cd C:\Foo\Barnella shell bash, questa verrà espansa e verrà visualizzata come interprete come cd C:FooBar; in questa forma verrà anche memorizzato nella $_variabile interna.
    Se invece hai incollato cd "C:\Foo\Bar"o cd 'C:\Foo\Bar'nella $_ variabile dovresti trovare C:\Foo\Bar.
    Poiché l'espansione della Cronologia viene eseguita immediatamente dopo la lettura di una riga completa, prima che la shell la spezzi in parole, potresti essere tentato di iniziare a usarla con un po 'di bashismo più o meno semplice, ad esempio, con una derivazione da (forse aggiungendo :po :q, "", analisi e così via ...)

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

    Questo è il momento per ricordare che non è sicuro iniziare a giocare con il percorso e i nomi dei file , soprattutto se provengono dagli Appunti di Windows (leggi in generale la pagina Perché non analizzare ls? , è essenzialmente correlato alla possibilità di utilizzare la scheda, spazi e newline come caratteri corretti per i nomi dei file e quelli della directory ...).
    Inoltre, quando si incolla un testo acquisito con il mouse , è possibile incollare anche uno spazio iniziale. Questo potrebbe evitare che il tuo comando finisca nella cronologia (dipende dalle opzioni della shell ...). In tal caso il tuo seguito !!sarà un comando non controllato ... (vedi un esempio in un'altra risposta ).Questo è un rischio tangibile non necessario .

Conclusione

Le espansioni della cronologia introducono parole dall'elenco della cronologia nel flusso di input, semplificando la ripetizione dei comandi, inserendo gli argomenti di un comando precedente nella riga di input corrente o correggendo rapidamente gli errori nei comandi precedenti.

Se non è facile, comincio a pensare che stiamo facendo qualcosa di sbagliato ;-)


Ad nauseam: un piccolo esperimento

Ho abilitato histverifynella shell quindi ...

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

poi premo Entere come espansione verificata trovo

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

poi premo di Enternuovo ed echeggia

D:FooBar 1A 2A

Questo sembra indicare che substitution failedviene generato nell'espansione della storia elaborata prima dell'espansione Brace , quindi prima di tutto , e sembra confermare che il smodificatore della storia non ha (ancora) elaborato la sostituzione del \personaggio come una vera regex. ..


2

È possibile utilizzare sedper sostituire ed eseguire il risultato.

Esistono diverse varianti possibili per un tale comando, ma sulla mia base solo questo ha funzionato:

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

La \\si aggiunge alla !!è nel caso dell'ultimo comando terminato con una barra rovesciata. Senza di essa, la shell si confonderà e chiederà la continuazione della linea.

Puoi anche aggiungere questo come funzione bash nel file ~/.bashrc:

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

Ecco come funziona sul mio Bash su Windows:

bash

Per spiegare come il A=comando assegna il valore corretto alla variabile shell A:

  • tailprende le ultime due righe dalla cronologia dei comandi, che fornisce le linee cd \mnt\cseguite da slashsé. La parte di history | tail -n 2può anche essere scritta come history 2.
  • head prendere la prima linea che è cd \mnt\c
  • cut taglia il numero di riga fornito da history
  • sed sostituisce tutte le anti-barre con barre
  • eval esegue il contenuto della variabile A

Ciao Harry. Mi dispiace dire che sulla mia versione GNU bash 4.3.11 (1) A=$(echo "!!\\" | sed 's,\\,/,g');eval $Anon funziona quando il comando è terminato con una barra rovesciata. Sei costretto ad aggiungere uno spazio, ad es. Ad C:\Foo\Bar\ altro, come hai detto, la shell attenderà il proseguimento della linea. Puoi anche premere Invio due volte. Nel primo caso si ottiene C:/Foo/Bar /(con uno spazio prima del /; nel secondo genererà un errore. La funzione funziona bene.
Hastur

Ho scritto la funzione perché (1) È molto più facile da usare e (2) Il one-liner sembrava troppo dipendente dall'implementazione.
harrymc,

-1

Non penso che tu possa farlo. Devi copiare / incollare di nuovo.

Il problema non è correlato alla barra essendo anche il separatore del comando sostitutivo, poiché il comando sostitutivo accetta qualsiasi separatore. Il problema riguarda le barre rovesciate da sostituire, che sono già state trattate come semplici inutili scappamenti di carattere alla loro destra.

Quindi, quando si chiama il comando sostitutivo, la barra rovesciata è già scomparsa dalla fonte. Prova a richiamare il comando così com'è ( !!). Invece di stampare lo stesso identico comando incluso c:\foo, eseguirà il comando meno barre rovesciate già elaborate che risultano c:foo.

E ovviamente non puoi trovare né sostituire un personaggio mancante. Cercare di farlo genererà un errore.


1
Bene. Questo è finalmente sbagliato dal momento che questo test funziona davvero: echo c:\Fooseguito da !!:gs,F,\\f,. Ma sostituire la fuga backslash stessa sembra essere un dolore.
A. Loiseau,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.