Zastąp ukośnik odwrotny ukośnikiem w przód w ostatnim poleceniu

10

Skopiowałem polecenie cd C:\foo\bar\z PowerShell do Cygwin i głupio spodziewałem się, że zostanie wykonane. Jestem teraz próbuje uruchomić podstawienie wymienić wszystkich \z /:

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

Nie jestem pewien, dlaczego nie udało mi się zastąpić.

Próbowałem także:

$ !!:gs/\\/q

Żeby sprawdzić, czy problem stanowi zamiennik. To nie jest. Teraz jestem ciekawa!

Dlaczego dostaję komunikat „Nieudana zamiana”?

Tanner Faulkner
źródło
2
W tej chwili nie mam pod ręką cygwina, ale pomyślałem, że ścieżki Windows działają ... próbowałeś zacytować ten argument? Tj.cd 'C:\foo\bar'
mpy
@mpy To też działa! Nie wiedziałem, że nie utknąłem w zastępstwie.
Tanner Faulkner
1
„Byłby otwarty na odpowiedź również za pomocą zsh”. Twój oryginał !!:gs/\\/\/działa w zsh…
Kamil Maciorowski
@TannerFaulkner fc -s '\'='/' -1działa w domyślnej wersji bash Ubuntu LTS. Daj mi znać, jeśli działa również pod Cygwin. W odpowiedzi umieściłem więcej słów.
Hastur
1
myślę, że ogólne zachowanie bash tutaj jest takie, że odwrotne ukośniki są przetwarzane jako znaki ucieczki przed zamianą w !!:gs/\\/\//. @Hastur fc -s '\'='/' -1dostaje oczekiwane zachowanie. to może być błąd w bashu.
donkiszotyczny

Odpowiedzi:

6

ukośnik jest również ogranicznikiem polecenia zastępczego, więc musisz go uciec jako ciąg zastępczy, aby nie zakończyć wcześniejszego podstawiania

!!:gs/\\/\//

lub bardziej wyraźnie, jak sugerowano w komentarzach, używając czegoś oprócz slash jako separatora

!!:gs|\\|/|

Ale to wydaje się działać tylko tcsh(powłoka zwykle przypisywana tam, gdzie jestem), nie mogę uzyskać polecenia do działania, bashponieważ wydaje się, że ucieczka nie działa w przypadku pierwszego argumentu:s

naprawiony
źródło
Bez radości :( Ten sam błąd i.imgur.com/QFQR0xF.png
Tanner Faulkner
1
Tylko uwaga: !!:gs|\\|/może być trochę bardziej czytelna.
mpy
1
Nie mogłem go uruchomić w bash. Pierwszy argument :snie wydaje się być prawdziwym wyrażeniem regularnym
naprawiono
4

Krótka odpowiedź: wbudowane fc(polecenie naprawy) bash

fcto polecenie wbudowane w powłokę bash, przeznaczone do edycji i ponownego uruchamiania poleceń historii.
Jest także obecny w CygWin i działa na wszystkich dystrybucjach Linuksa, na których testowałem:

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

Kilka wyjaśnień

  • fcjest wbudowanym poleceniem bash do wyświetlania lub edycji i ponownego uruchamiania poleceń z listy historii.
  • -1podejmie ostatnie polecenie w historii. Zauważ, że nawet !!jest zdefiniowane (odczytywane z man bash) jako

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

  • -saby zastąpić wzór innym. Tym razem od help fc(wbudowane polecenie więc help):

    W formacie `fc -s [pat = rep ...] [polecenie] 'POLECENIE jest ponownie wykonywane po wykonaniu podstawienia OLD = NOWOŚĆ.

    Ok, mają na myśli pat=repzamiast OLD=NEW...

  • Wzory zostaną odczytane przez powłokę, więc musimy je chronić cytatem: '\'i '/'.

Kilka słów więcej o tym, dlaczego otrzymujesz „podstawienie nie powiodło się”

Wydaje się, że dla s modyfikatora nie jest (jeszcze) zaimplementowane podstawienie znaku odwrotnego ukośnika \, to znaczy ucieczki. Dla pewności powinniśmy zobaczyć kod, na przykład, wersji gnu rozszerzenia historii bash (ale było powyższe polecenie, aby uzyskać to, co próbujesz zrobić ... więc leniwie to robię ...).

Niektóre uwagi:

  1. Jesteśmy przekonani, że będzie działać z każdym RegEx, z którym współpracujemy sed, ale nie jest to gwarantowane. Odwrotny ukośnik jest znakiem ucieczki rozszerzenia i problem jest tutaj. Ponadto zachowanie rozszerzenia jest powiązane z shoptopcjami, więc powinniśmy zacząć widzieć przypadek po przypadku ...

  2. Po wklejeniu łańcucha cd C:\Foo\Bardo powłoki bash zostanie on rozwinięty i pojawi się dla interpretera jako cd C:FooBar; w tej formie będzie również przechowywany w $_zmiennej wewnętrznej.
    Jeśli zamiast tego wkleiłeś cd "C:\Foo\Bar"lub cd 'C:\Foo\Bar'w $_ zmiennej powinieneś znaleźć C:\Foo\Bar.
    Ponieważ ekspansja Historii jest wykonywana natychmiast po przeczytaniu pełnego wiersza, zanim powłoka podzieli go na słowa, możesz mieć pokusę, aby zacząć używać go z mniej lub bardziej oczywistym bazizmem , np. Z pewną pochodną (być może dodając :plub :q, "", parsowanie i tak dalej ...)

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

    To jest moment, aby pamiętać, że nie jest bezpiecznie zaczynać grę ścieżką i nazwami plików , szczególnie jeśli pochodzą one ze schowka systemu Windows (przeczytaj ogólnie stronę Dlaczego nie parsować ls?) , Jest to zasadniczo związane z możliwością użycia tabulacji, spacje i znaki nowej linii jako poprawne znaki dla nazw plików i katalogów ...).
    Ponadto po wklejeniu tekstu przechwyconego myszą możesz również wkleić wiodące miejsce. Dzięki temu twoje polecenie zakończy się w historii (zależy to od opcji powłoki ...). Jeśli tak, twoje !!obserwacje będą niekontrolowanym poleceniem ... (zobacz przykład w innej odpowiedzi ).Jest to niepotrzebne namacalne ryzyko .

Wniosek

Rozszerzenia historii wprowadzają słowa z listy historii do strumienia wejściowego, ułatwiając powtarzanie poleceń, wstawianie argumentów do poprzedniego polecenia w bieżącym wierszu wprowadzania lub szybkie naprawianie błędów w poprzednich poleceniach.

Jeśli nie jest to łatwe, zaczynam myśleć, że robimy coś złego ;-)


Ad nauseam: mały eksperyment

Włączyłem wtedy histverifyw powłoce ...

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

następnie naciskam Enteri jako zweryfikowane rozszerzenie znajduję

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

potem Enterponownie naciskam i echo

D:FooBar 1A 2A

To wydaje się wskazywać, że substitution failedjest generowane w rozszerzeniu historii przetworzonym przed rozszerzeniem Brace , więc przede wszystkim i wydaje się potwierdzać, że smodyfikator historii nie (jeszcze) przetworzył podstawienia \znaku jako prawdziwego wyrażenia regularnego. ..

Hastur
źródło
2

Możesz użyć, sedaby zastąpić i wykonać wynik.

Istnieje kilka możliwych wariantów takiego polecenia, ale na moim bashu tylko ten działał:

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

\\Dodaje się do !!to w przypadku ostatniego polecenia zakończona lewym ukośnikiem. Bez tego pocisk zostanie zdezorientowany i poprosi o kontynuację linii.

Możesz również dodać to jako funkcję bash w pliku ~/.bashrc:

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

Oto jak to działa na moim Bash w systemie Windows:

grzmotnąć

Aby wyjaśnić, w jaki sposób A=polecenie przypisuje odpowiednią wartość do zmiennej powłoki A:

  • tailtrwa dwa ostatnie wiersze z historii poleceń, co daje wiersze cd \mnt\cnastępnie slashsiebie. Część history | tail -n 2można również zapisać jako history 2.
  • head weź pierwszą linię, która jest cd \mnt\c
  • cut wycina numer linii podany przez history
  • sed zastępuje wszystkie anti-ukośniki ukośnikami
  • eval wykonuje zawartość zmiennej A
harrymc
źródło
Cześć Harry. Przykro mi to mówić, że w mojej wersji GNU bash 4.3.11 (1) A=$(echo "!!\\" | sed 's,\\,/,g');eval $Anie działa, gdy polecenie zakończyło się odwrotnym ukośnikiem. Jesteś zmuszony dodać spację, np. Do C:\Foo\Bar\ innej, jak powiedziałeś, powłoka będzie czekać na kontynuację linii. Możesz także nacisnąć dwukrotnie klawisz Enter. W pierwszym przypadku otrzymujesz C:/Foo/Bar /(ze spacją przed /; w drugim generuje błąd. Funkcja działa dobrze.
Hastur
Napisałem tę funkcję, ponieważ (1) Jest znacznie łatwiejsza w użyciu i (2) Jednoliniowa wydawała się zbyt zależna od implementacji.
harrymc
-1

Nie sądzę, że możesz to zrobić. Musisz ponownie skopiować / wkleić.

Problem nie jest związany z ukośnikiem jako separatorem polecenia zastępczego, ponieważ polecenie zastępcze akceptuje dowolny separator. Chodzi o zamianę ukośników, które zostały już potraktowane jako proste bezużyteczne ucieczki postaci po ich prawej stronie.

Dlatego po wywołaniu polecenia zastępczego ukośnik zniknął już ze źródła. Spróbuj ponownie wywołać polecenie tak, jak jest ( !!). Zamiast wypisać dokładnie to samo polecenie, w tym polecenie c:\foo, wykona polecenie minus już przetworzone ukośniki odwrotne, w wyniku czego c:foo.

I oczywiście nie możesz znaleźć ani zastąpić brakującej postaci. Próba zrobienia tego spowoduje błąd.

A. Loiseau
źródło
1
Dobrze. To w końcu jest złe, ponieważ ten test faktycznie działa: echo c:\Foonastępuje !!:gs,F,\\f,. Ale samo zastąpienie odwrotnym ukośnikiem wydaje się być bólem.
A. Loiseau,