2

Допустим, у меня есть следующая команда:

/long/path/to/bin -a foo -b 2 -c 3 -d 4 foo | gunzip -c | less

И теперь я хочу заменить foo позади 4 на bar , так что это становится:

/long/path/to/bin -a foo -b 2 -c 3 -d 4 bar | gunzip -c | less

Я также знаю, что я мог сделать:

!:0-8 bar !:10-$

Есть ли способ, которым я могу просто заменить один аргумент? Что-то вроде:

!:9:bar

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

1 ответ1

2

Вы можете использовать виджет ZLE, чтобы сделать это.

Определить виджет

Введите это в командной строке (или вставьте в ваш ~/.zshrc , тогда он будет постоянным):

accept-line-with-histmod() {
  if [[ $BUFFER[1,3] == "?\!:" ]]; then
    local loc mod
    loc=${${(s.:.)BUFFER}[2]}
    mod=${${(s.:.)BUFFER}[3]}
    zle up-history
    BUFFER="${${(z)BUFFER}[1,$((loc))]} $mod ${${(z)BUFFER}[$((loc+2)),-1]}"
    CURSOR=$#BUFFER
  else
    zle accept-line
  fi
}
zle -N accept-line-with-histmod
bindkey "^M" accept-line-with-histmod

Как это использовать?

Синтаксис немного отличается от того, что вы предложили, но не сильно (я добавил вопросительный знак впереди, чтобы облегчить синтаксический анализ). Демо ($ это подсказка):

$ /long/path/to/bin -a foo -b 2 -c 3 -d 4 foo | gunzip -c | less
[...]
$ ?!:9:bar
$ /long/path/to/bin -a foo -b 2 -c 3 -d 4 bar | gunzip -c | less

Как это устроено!

Этот виджет привязан к клавише ENTER , заменив привязку ключа по умолчанию для accept-line , поэтому он выполняется каждый раз, когда вы нажимаете Enter.

Сначала виджет проверяет, начинается ли командная строка с ?!: ; переменная $BUFFER содержит полную командную строку. Если нет, обычный виджет accept-line выполняется, что приводит к поведению по умолчанию.

Но если строка начинается с ?!: , То аргументы (?!:POSITION:REPLACEMENT TEXT) сохраняются в переменных $loc и $mod , соответственно. [$ {$ {(S:.).BUFFER} [2]} разбивает командную строку (BUFFER) на двоеточия ((s.:.)) И принимает второе слово ([2])]

Теперь он вызывает последнюю строку из истории (zle up-history), которая теперь находится в $BUFFER . (Если вы хотите обрабатывать более старые события, то вы должны развернуть виджет здесь!)

Затем он в основном делает то же самое, что вы сделали в своем примере, построив текущую командную строку из старой. (Смещение 1 связано с тем, что индекс массивов начинается с 1, а индекс в расширении истории - с 0).

Наконец, курсор помещается в конец новой встроенной командной строки, ожидая подтверждения подтверждения выполнения.

Замечания: Очевидно, что нет обработки ошибок, поэтому будьте осторожны! Я также не проверял это с очень сложными командными строками. Это может или не может работать с теми.


В качестве альтернативы вас может заинтересовать синтаксис поиска и замены расширения истории:

$ echo foo
$ !:s/foo/bar

Или r встроенный

$ echo foo
$ r foo=bar

Тем не менее, это невозможно с вами пример в вопросе, так как foo там не является уникальным.

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