Вопрос или проблема
Очень удобно, что вы можете вводить сложные команды в $EDITOR (vim
в моем случае) с помощью Ctrl XE в Bash.
Тем не менее, на мой взгляд, еще лучше было бы, если бы команда не выполнялась немедленно, а просто вставлялась в командную строку, чтобы я мог внести окончательные правки, основываясь на предыдущих командах/выводе. Есть ли простой способ добиться этого?
Не могу сказать, назовете ли вы это простым. Вот быстрое и грубое подтверждение концепции:
# в Bash
_edit_wo_executing() {
local editor="${EDITOR:-nano}"
tmpf="$(mktemp)"
printf '%s\n' "$READLINE_LINE" > "$tmpf"
"$editor" "$tmpf"
READLINE_LINE="$(<"$tmpf")"
READLINE_POINT="${#READLINE_LINE}"
rm -f "$tmpf" # -f для тех, у кого есть alias rm='rm -i'
}
bind -x '"\C-x\C-e":_edit_wo_executing'
Теперь Ctrl xe должно делать то, что вы хотите.
Примечания:
-
Я использовал только базовую логику для установки
editor
; отрегулируйте это под свои нужды. -
READLINE_POINT="${#READLINE_LINE}"
должен помещать курсор в самое конец. Bash 5 ожидает длину в символах, и приведенный выше код работает; но Bash 4 ожидает байты. Чтобы обрабатывать не-ASCII текст в Bash 4, используйтеREADLINE_POINT="$(printf '%s' "$READLINE_LINE" | wc -c)"
(Я узнал из этого ответа).
-
При нажатии клавиши функция выполняется в вашей текущей оболочке, поэтому (в отличие от сценария) ваши алиасы будут работать.
-f
вrm -f
был предложен пользователем, который используетalias rm='rm -i'
. В общем, вы можете захотеть выполнить неалиасированныйrm
.
Хотя это не прямо отвечает на ваш вопрос, в следующих оболочках поведение, которое вы запросили, подтверждено как стандартное:
- Zsh
- Fish
- Nushell
В этих (и, безусловно, других) оболочках закрытие редактора вставит буфер редактора в текущую командную строку без его выполнения.
У меня возникла проблема с ответом Камила. Та же проблема, о которой комментировал Леви, что bash выполняет только первое слово, но исправление, предложенное Себастьяном Карлосом, не сработало для меня. Для меня проблема заключалась в перенаправлении из временного файла в переменную readline READLINE_LINE="$(<"$tmpf")"
.
Я исправил это, заменив перенаправление на cat
_edit_wo_executing() {
local editor="${EDITOR:-nvim}"
tmpf="$(mktemp)"
printf '%s\n' "$READLINE_LINE" > "$tmpf"
"$editor" "$tmpf"
READLINE_LINE="$(cat "$tmpf")"
READLINE_POINT="${#READLINE_LINE}"
\rm -f "$tmpf" # -f для тех, у кого есть alias rm='rm -i'
}
в в этой похожей теме, @karlicoss демонстрирует, как выйти из vim с ненулевым кодом выхода, что заставит Readline не выполнять команду, даже если :w
уже было выполнено. Я не осмеливаюсь повторять 3-байтовое решение из страха быть покараным администраторами за плагиат.
редактировать: Это не объясняет, как вывести из редактора в текущую строку в readline, но объясняет, как “ctrl x+E не выполняя команду немедленно”, что и привело меня к этой теме.
Ответ или решение
Вопрос, который вы задали, касается возможности редактирования команд в Bash через редактор, не выполняя их немедленно. Ниже представлены подробные пояснения и решение, которое может помочь вам достичь этой цели.
Использование редактора в комбинации с Bash
По умолчанию в Bash, когда вы используете комбинацию клавиш Ctrl + X
, Ctrl + E
, команда, которую вы написали, выполняется сразу же после закрытия редактора (например, vim
). Однако, если вам необходимо сначала отредактировать команду, а затем вставить её в командную строку без выполнения, это можно реализовать с помощью создания функции в Bash.
Решение: Не выполнять команду немедленно
Вот пример простой функции, которая позволит вам редактировать команду в редакторе и вставить её обратно в командную строку:
_edit_wo_executing() {
local editor="${EDITOR:-nano}" # Используйте редактор, заданный в переменной окружения, в противном случае - nano
tmpf="$(mktemp)" # Создайте временный файл
printf '%s\n' "$READLINE_LINE" > "$tmpf" # Запишите текущее содержимое строки командной строки в временный файл
"$editor" "$tmpf" # Откройте временный файл в редакторе
READLINE_LINE="$(cat "$tmpf")" # Поместите содержимое временного файла обратно в переменную READLINE_LINE
READLINE_POINT="${#READLINE_LINE}" # Установите курсор в конец строки
\rm -f "$tmpf" # Удалите временный файл
}
bind -x '"\C-x\C-e":_edit_wo_executing' # Привязка функции к комбинации клавиш Ctrl + X, Ctrl + E
Пояснения к коду
-
Редактор по умолчанию: В коде используется переменная
EDITOR
, чтобы определить, какой редактор будет использоваться. Если переменная не установлена, будет использоватьсяnano
. -
Временный файл: Функция создаёт временный файл с помощью
mktemp
, в который записывается текущее содержимое командной строки. -
Открытие редактора: После создания временного файла, он открывается в указанном редакторе.
-
Получение строки: После редактирования, содержимое временного файла считывается и подставляется обратно в
READLINE_LINE
. -
Удаление временного файла: Временный файл удаляется через команду
rm
.
Советы и дополнительные примечания
- Проверьте, что у вас установлен редактор (например,
nvim
,vim
,nano
и т.д.) перед использованием этой функции. Это можно сделать, установив переменнуюEDITOR
в вашем файле конфигурации Bash (~/.bashrc
или~/.bash_profile
). - Убедитесь, что переменная
READLINE_POINT
устанавливается в конец строки для удобства редактирования. - Если вы используете нестандартные настройки Bash или хотите, чтобы ваша команда работала в различных версиях Bash, обязательно протестируйте функцию в средах, где вы собираетесь её использовать. Некоторые из assistive tools могут вести себя по-разному в зависимости от версии Bash.
Альтернативы в других оболочках
Если данное решение кажется сложным, стоит отметить, что в других оболочках, таких как Zsh
, Fish
и Nushell
, подобная функция реализована по умолчанию. В таких оболочках редактирование команд в редакторе заканчивается их вставкой в командную строку, что может сильно упростить использование.
Заключение
Создание функции для редактирования команд в Bash без немедленного выполнения — это полезная задача. Представленный код служит начальной точкой, которую вы можете адаптировать под свои нужды. Использование редактора позволяет более тщательно формировать команды, что в свою очередь может привести к более точному выполнению команд в вашей командной строке.