Заменить обратную косую черту для прямой косой черты в последней команде

10

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

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

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

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

$ !!:gs/\\/q

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

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

Таннер Фолкнер
источник
2
У меня сейчас нет cygwin, но я подумал, что пути Windows работают ... Вы пытались процитировать аргумент? Т.е.cd 'C:\foo\bar'
mpy
@mpy Это на самом деле тоже работает! Не знал, что я не застрял с заменой.
Таннер Фолкнер
1
Msgstr "Был бы открыт для ответа, используя также zsh". Ваш оригинал !!:gs/\\/\/работает в zsh…
Камиль Мачоровски
@TannerFaulkner fc -s '\'='/' -1работает по умолчанию в bash для Ubuntu LTS. Дайте мне знать, если он работает и под Cygwin. Я написал больше слов в ответе.
Хастур
1
я думаю, что общее поведение bash здесь заключается в том, что обратные слеши обрабатываются как escape-символы, прежде чем произойдет замещение !!:gs/\\/\//. @Hastur 's fc -s '\'='/' -1получает ожидаемое поведение. это может быть ошибка в bash.
донкихотским

Ответы:

6

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

!!:gs/\\/\//

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

!!:gs|\\|/|

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

инфиксальный
источник
Никакой радости :( Та же ошибка i.imgur.com/QFQR0xF.png
Таннер Фолкнер
1
Просто замечание: !!:gs|\\|/может быть немного более читабельным.
mpy
1
Я не мог заставить его работать в Bash. Первый аргумент, :sпохоже, не является настоящим регулярным выражением
добавлено
4

Краткий ответ: встроенная fc(исправить команду) 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...

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

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

Похоже, что для 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.
    Поскольку расширение «История» выполняется сразу после прочтения полной строки, перед тем, как оболочка разбивает ее на слова, у вас может возникнуть соблазн начать использовать его с некоторым более или менее простым башизмом , например, с некоторой производной от (может быть, добавления :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генерируется в расширении истории, обработанном до расширения Брэйс , поэтому, во- первых , и это подтверждает, что sмодификатор истории (еще) не обрабатывал замену \символа как реального регулярного выражения. ..

Hastur
источник
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
harrymc
источник
Привет, Гарри. Сожалею, что на моем GNU bash версии 4.3.11 (1) A=$(echo "!!\\" | sed 's,\\,/,g');eval $Aне работает, когда команда заканчивается обратной косой чертой. Вы вынуждены добавить пробел, например, к C:\Foo\Bar\ другому, как вы сказали, оболочка будет ожидать продолжения строки. Вы также можете нажать Enter дважды. В первом случае вы получите C:/Foo/Bar /(с пробелом перед /, во втором он будет генерировать ошибку , функция работает отлично..
Хастура
Я написал функцию, потому что (1) ее гораздо проще использовать, и (2) однострочник казался слишком зависимым от реализации.
harrymc
-1

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

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

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

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

А. Луазо
источник
1
Что ж. Это, наконец, неправильно, так как этот тест на самом деле работает: echo c:\Fooзатем !!:gs,F,\\f,. Но подмена самого побега с обратной косой черты кажется болью.
А. Луазо