Замініть зворотну косу рису для перекидання вперед в останню команду


10

Я скопіював команду cd C:\foo\bar\з PowerShell в Cygwin і нерозумно очікував її виконання. Я зараз намагаюся запустити заміну , щоб замінити всі \з /:

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

Не впевнений, чому мені не вдалося замінити.

Я також спробував:

$ !!:gs/\\/q

Просто, щоб побачити, чи була проблема в заміні. Це не. Тепер мені цікаво!

Чому я отримую "заміну не вдалося"?


2
Зараз у мене немає цигуна під рукою, але я подумав, що шляхи до Windows працюють ... Ви намагалися співати - цитуйте аргумент? Тобтоcd 'C:\foo\bar'
mpy

@mpy Це теж працює! Не знав, що я не затримався із заміною.
Таннер Фолкнер

1
Msgstr "Буде відкрито для відповіді, використовуючи також zsh". Ваш оригінал !!:gs/\\/\/працює в zsh…
Каміль Маціоровський

@TannerFaulkner fc -s '\'='/' -1працює під типом Ubuntu LTS за замовчуванням. Дайте мені знати, чи працює він також під Cygwin. У відповіді я розмістив більше слів.
Хастур

1
Я думаю, що загальна поведінка баш тут полягає в тому, що косоокі риси обробляються як втечі, перш ніж відбувається заміна !!:gs/\\/\//. @Hastur «s fc -s '\'='/' -1отримує очікувану поведінку. це може бути помилка в баші.
кіхото

Відповіді:


6

пряма косою рисою є також роздільником команди вашої заміни, тому ви повинні уникати її як рядок заміни, щоб не закінчувати заміну достроково

!!:gs/\\/\//

або більш чітко, як це пропонується в коментарях, використовуючи щось, крім косої риски, як роздільника

!!:gs|\\|/|

Але це, здається, працює в tcsh(оболонка, яка зазвичай призначається там, де я є), я не можу отримати команду працювати, bashоскільки, здається, що втеча не працює на першому аргументі:s


Немає радості :( та сама помилка i.imgur.com/QFQR0xF.png
Tanner Faulkner

1
Лише зауваження: !!:gs|\\|/може бути трохи читабельніше.
mpy

1
Я не міг змусити його працювати в басі. Перший аргумент :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 повторно виконується після заміни OLD = NEW.

    Гаразд вони означають pat=repзамість OLD=NEW...

  • Шаблони будуть прочитані оболонкою, тому нам доведеться захищати їх цитатами: '\'і '/'.

Ще кілька слів про те, чому ви отримуєте "заміну не вдалося"

Здається, що для s модифікатора ще (поки) не реалізована підміна символу зворотної косої риси \, тобто вихідного. Щоб бути впевненим, нам слід побачити код, наприклад, gnu-версії розширення історії bash (але була вищевказана команда, щоб отримати те, що ви намагалися зробити ... тому я вважаю ледачим ....).

Деякі примітки:

  1. Ми думаємо, що він буде працювати з кожним RegEx, з яким ми знаходимо роботу sed, але це не гарантується. Зворотна косою рисою є характер втечі розширення, і проблема тут. Більше того, поведінка розширення пов'язана з 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?) , Це, по суті, пов'язане з можливістю використання вкладки, пробіли та нові рядки як правильні символи для імен файлів та каталогів ...).
    Крім того, коли ви вставляєте текст, захоплений мишею , ви можете також вставити провідний пробіл. Це може уникнути того, що ваша команда закінчиться в історії (це залежить від параметрів оболонки ...). Якщо так, ваша наступна !!команда буде контрольованою командою ... (див. Приклад в іншій відповіді ).Це зайвий відчутний ризик .

Висновок

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

Якщо це непросто, я починаю думати, що ми робимо щось не так ;-)


Рекламна нудота: невеликий експеримент

Я ввімкнув 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для підстановки та виконання результату.

Існує кілька можливих варіантів такої команди, але на моєму баші працював лише цей:

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

Привіт, Гаррі. На жаль, можу сказати, що в моєму 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,. Але сама заміна втечі зворотньої косої риси виглядає болем.
А. Луїзо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.