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