10

Я скопировал команду cd C:\foo\bar\ из PowerShell в Cygwin и, как ни странно, ожидал ее выполнения. Сейчас я пытаюсь запустить замену, чтобы заменить все \ на /:

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

Не уверен, почему замена мне не удалась.

Я также попробовал:

$ !!:gs/\\/q

Просто чтобы увидеть, была ли проблема в замене. Это не. Теперь мне любопытно!

Почему я получаю "замена не удалась"?

4 ответа4

6

косая черта также является разделителем вашей команды замены, поэтому вы должны экранировать ее как строку замены, чтобы не завершить замену раньше.

!!:gs/\\/\//

или более просто, как это предлагается в комментариях, используя что-то помимо косой черты в качестве разделителя

!!:gs|\\|/|

Но, похоже, это работает только в tcsh (оболочке, обычно назначаемой там, где я нахожусь), я не могу заставить команду работать в bash так как кажется, что экранирование не работает с первым аргументом :s

4

Краткий ответ: встроенная команда fc (fix) bash

fc - это команда, встроенная в оболочку bash, предназначенная для редактирования и повторного выполнения команд истории.
Он также присутствует в CygWin и работает во всех дистрибутивах Linux, на которых я тестировал:

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

Некоторые объяснения

  • fc - встроенная команда bash для вывода, редактирования или повторного выполнения команд из списка истории.
  • -1 примет последнюю команду в истории. Обратите внимание, что даже !! определяется (читай из man bash) как

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

  • -s заменить шаблон на другой. На этот раз из help fc (встроенная команда, так что help):

    В формате `fc -s [pat = rep ...] [command] 'COMMAND повторно выполняется после выполнения замены OLD = NEW.

    Хорошо, они имеют в виду pat=rep вместо OLD=NEW ...

  • Шаблоны будут читаться оболочкой, поэтому мы должны защищать их с помощью кавычек: '\' и '/' .

Несколько слов о том, почему вы получаете "замена не удалась"

Кажется, что для модификатора s (пока) не реализована подстановка символа обратной косой черты \ , то есть escape. Чтобы быть уверенным, мы должны увидеть код, например, версии gnu расширения истории bash (но была указанная выше команда, чтобы получить то, что вы пытались сделать ... поэтому я считаю это ленивым....)

Некоторые заметки:

  1. Мы склонны думать, что он будет работать каждый RegEx, который мы находим, работая с sed , но это не гарантируется. Обратная косая черта является escape-символом расширения, и проблема здесь. Кроме того, поведение расширения связано с опциями shopt , поэтому мы должны начать видеть в каждом конкретном случае ...

  2. Когда вы вставите строку cd C:\Foo\Bar в оболочку bash, она развернется и отобразится для интерпретатора как cd C:FooBar ; в этом виде он также будет храниться во внутренней переменной $_ .
    Если вы вместо этого вставили cd "C:\Foo\Bar" или cd 'C:\Foo\Bar' в переменную $_ вы должны найти C:\Foo\Bar .
    Так как расширение истории выполняются сразу же после полной линии читать, прежде чем оболочка разбивает его на слова, вы можете захотеть , чтобы начать использовать его с некоторыми bashism более или менее простым, например, с каким - то выводом из (возможно :p или :q , "" , разбор и так далее ...)

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

    Это момент, который нужно помнить, что небезопасно начинать играть с путями и именами файлов , особенно если они приходят из буфера обмена Windows (читайте в общем на странице Почему бы не разобрать ls?, Это по сути связано с возможностью использовать tab , пробелы и переводы строк как правильные символы для имен файлов и каталогов ...).
    Более того, когда вы вставляете текст, захваченный мышью , вы также можете вставить начальный пробел. Это может предотвратить завершение вашей команды в истории (это зависит от параметров оболочки ...). Если так, то ваш следующий !! будет неуправляемой командой ... (см. пример в другом ответе). Это ненужный ощутимый риск .

Заключение

Расширения истории вводят слова из списка истории во входной поток, что позволяет легко повторять команды, вставлять аргументы предыдущей команды в текущую строку ввода или быстро исправлять ошибки в предыдущих командах.

Если это не так просто, я начинаю думать, что мы делаем что-то не так ;-)


До тошноты: небольшой эксперимент

Я включил histverify в оболочке ...

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

затем я нажимаю Enter и в качестве проверенного расширения нахожу

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

затем я нажимаю Enter еще раз, и он эхом

D:FooBar 1A 2A

Похоже, это указывает на то, что substitution failed сгенерирована в расширении истории, обработанном до расширения Brace, поэтому, во- первых, и это подтверждает, что модификатор истории s (пока) не обрабатывал замену символа \ как настоящее регулярное выражение ...

2

Вы можете использовать sed для замены и выполнения результата.

Есть несколько возможных вариантов такой команды, но на моем bash работал только этот:

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

\\ добавлен в !! в случае, если последняя команда заканчивается обратной косой чертой. Без этого оболочка запутается и попросит продолжения линии.

Вы также можете добавить это как функцию bash в файл ~/.bashrc :

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

Вот как это работает на моем Bash на Windows:

удар

Чтобы объяснить, как команда A= назначает правильное значение переменной оболочки A:

  • tail берет последние две строки из истории команд, которая дает строки cd \mnt\c за которыми следует slash . Часть history | tail -n 2 также может быть записан как history 2 .
  • head взять первую строку , которая cd \mnt\c
  • cut вырезает номер строки, предоставленный history
  • sed заменяет все анти-слэши на слэши
  • eval выполняет содержимое переменной A
-1

Я не думаю, что вы можете сделать это. Вам нужно скопировать / пройти снова.

Проблема не связана с тем, что косая черта также является разделителем команды замены, так как команда замены принимает любой разделитель. Речь идет о замене обратной косой черты, которая уже рассматривается как простые бесполезные спуски символов справа от них.

Следовательно, когда вы вызываете команду замены, обратный слеш уже исчез из источника. Просто попробуйте повторно вызвать команду как есть (!!). Вместо того, чтобы печатать точно такую же команду, включая c:\foo , она выполнит команду без уже обработанных обратных косых черт, что приведет к c:foo .

И, очевидно, вы не можете найти или заменить отсутствующий символ. Попытка сделать это вызовет ошибку.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .