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