Вопрос или проблема
При использовании Bash с установленными клавиатурными сокращениями emacs, сочетание клавиш transpose-words (M-t
) не меняет местами аргументы, а “слова” (по собственному определению слов).
Так что если у меня есть это:
vimdiff project-number-One/Vagrantfile project-number-Two/Vagrantfile.old
и мой курсор находится между первым и вторым аргументом, когда я нажимаю optiont, у меня вместо этого получается
vimdiff project-number-One/project Vagrantfile-number-Two/Vagrantfile.old
что, очевидно, не то, что я хочу. Как я могу поменять местами аргументы?
В bash различные команды имеют разные представления о словах. C-w
удаляет до предыдущего пробела, но большинство других команд, включая M-t
, используют слова, ограниченные знаками препинания.
С курсором между первым и вторым аргументом, C-w C-e SPC C-y
поменяет местами два слова.
Если вы хотите назначить клавишу для перестановки слов, разделенных пробелами, это более сложно. Смотрите смешанное поведение клавиш emacs-стиля в bash. Вот немного протестированный код.
transpose_whitespace_words () {
local prefix=${READLINE_LINE:0:$READLINE_POINT} suffix=${READLINE_LINE:$READLINE_POINT}
if [[ $suffix =~ ^[^[:space:]] ]] && [[ $prefix =~ [^[:space:]]+$ ]]; then
prefix=${prefix%${BASH_REMATCH[0]}}
suffix=${BASH_REMATCH[0]}${suffix}
fi
if [[ $suffix =~ ^[[:space:]]+ ]]; then
prefix=${prefix}${BASH_REMATCH[0]}
suffix=${suffix#${BASH_REMATCH[0]}}
fi
if [[ $prefix =~ ([^[:space:]]+)([[:space:]]+)$ ]]; then
local word1=${BASH_REMATCH[1]} space=${BASH_REMATCH[2]}
prefix=${prefix%${BASH_REMATCH[0]}}
if [[ $suffix =~ [^[:space:]]+ ]]; then
suffix=${suffix#${BASH_REMATCH[0]}}
READLINE_LINE=${prefix}${BASH_REMATCH[0]}$space$word1$suffix
READLINE_POINT=$((${#READLINE_LINE} - ${#suffix}))
fi
fi
}
bind -x '"\e\C-t": transpose_whitespace_words'
Все это проще в zsh…
Для быстрого и простого решения добавьте это в ваш inputrc (выберите подходящие для вас клавиши):
"\e\C-b": shell-backward-kill-word
"\eh": shell-backward-word
"\e\C-f": shell-forward-word
# Меняет местами два предыдущих аргумента (control + alt + t)
"\e\C-t": "\e\C-b\eh\C-y"
# Меняет местами предыдущий аргумент с следующим (control + alt + p)
"\e\C-p": "\e\C-b\e\C-f\C-y"
В случае shell-*
версий этих функций слова разделяются неэкранированными метасимволами оболочки.
Символ, который, когда не заключен в кавычки, разделяет слова. Метасимвол – это пробел, табуляция, новая строка или один из следующих символов: ‘|’, ‘&’, ‘;’, ‘(’, ‘)’, ‘<’, или ‘>’.
Примечание: Курсор должен находиться после второго аргумента перед нажатиями Ctrl+Alt+t, чтобы он эффективно переместил аргумент перед курсором к началу строки.
$ true foo/bar.xyz even/without\ quotes.ok "too/too far.away"
^
$ true foo/bar.xyz "too/too far.away" even/without\ quotes.ok
^
$ true "too/too far.away" foo/bar.xyz even/without\ quotes.ok
^
Примечание: Курсор должен находиться после первого аргумента перед нажатиями Ctrl+Alt+p, чтобы он эффективно переместил аргумент перед курсором к концу строки.
$ true "too/too far.away" foo/bar.xyz even/without\ quotes.ok
^
$ true foo/bar.xyz "too/too far.away" even/without\ quotes.ok
^
$ true foo/bar.xyz even/without\ quotes.ok "too/too far.away"
^
Если ваш курсор находится здесь:
vimdiff projectOne/Vagrantfile projectTwo/Vagrantfile
^
Нажмите Alt + BTTBBTFTBBTT
Или просто:
Нажмите Ctrl + W, Ctrl + E, вставьте пробел и нажмите Ctrl + Y
Вам нужно нажать BTTBBT
вместо одного T
.
Ответ Рейниса привел меня к
"\e\C-t": shell-transpose-words
shell-transpose-words
не задокументирован в man bash
(по крайней мере, не в Fedora 40), но он есть в списке, распечатываемом bind -P
. Вы можете использовать его с курсором после двух аргументов, чтобы поменять их местами, или между ними.
Например:
cat "my file" another\ file█
нажмите C-M-t
cat another\ file "my file"█
Ответ или решение
Как транспонировать аргументы командной строки с использованием клавиатурных сочетаний Emacs в Bash
Работа в командной строке может быть не так простой, как кажется на первый взгляд. Когда вы используете клавиатурные сочетания Emacs в Bash, возникает проблема с транспонированием аргументов: стандартная команда M-t
(transpose-words) не работает так, как ожидается, поскольку она комбинирует только «слова», определенные в контексте, и игнорирует разбиение на аргументы.
Проблема с транспонированием
При использовании Bash и нажатии комбинации M-t
между двумя аргументами, расположенными в строке, эта команда перемещает только «слова». Это означает, что если ваш курсор находится между двумя аргументами, транспонирование может завершиться неожидаемым результатом. Например, если у вас есть строка:
vimdiff project-number-One/Vagrantfile project-number-Two/Vagrantfile.old
И курсор установлен между этими двумя аргументами, нажатие M-t
приведет к следующему:
vimdiff project-number-One/project Vagrantfile-number-Two/Vagrantfile.old
Это не удовлетворяет потребностям пользователей, которые ожидают, что аргументы будут правильно транспонированы.
Решение через пользовательские функции
Создание функции для транспонирования аргументов: Чтобы добиться желаемого результата, можно создать пользовательскую функцию, которая будет корректно обрабатывать разбиение по пробелам.
Вот пример базовой функции, которая будет выполнять транспонирование аргументов:
transpose_whitespace_words () {
local prefix=${READLINE_LINE:0:$READLINE_POINT}
local suffix=${READLINE_LINE:$READLINE_POINT}
if [[ $suffix =~ ^[^[:space:]] ]] && [[ $prefix =~ [^[:space:]]+$ ]]; then
prefix=${prefix%${BASH_REMATCH[0]}}
suffix=${BASH_REMATCH[0]}${suffix}
fi
if [[ $suffix =~ ^[[:space:]]+ ]]; then
prefix=${prefix}${BASH_REMATCH[0]}
suffix=${suffix#${BASH_REMATCH[0]}}
fi
if [[ $prefix =~ ([^[:space:]]+)([[:space:]]+)$ ]]; then
local word1=${BASH_REMATCH[1]} space=${BASH_REMATCH[2]}
prefix=${prefix%${BASH_REMATCH[0]}}
if [[ $suffix =~ [^[:space:]]+ ]]; then
suffix=${suffix#${BASH_REMATCH[0]}}
READLINE_LINE=${prefix}${BASH_REMATCH[0]}$space$word1$suffix
READLINE_POINT=$((${#READLINE_LINE} - ${#suffix}))
fi
fi
}
bind -x '"\e\C-t": transpose_whitespace_words'
Эта функция может быть добавлена в ваш .bashrc
файл, и затем привязанная к сочетанию клавиш, например, Alt + Ctrl + T
. После этого, при нажатии этой комбинации в нужной позиции, вы сможете удобно транспонировать аргументы.
Непосредственные ускорения через inputrc
Если вы хотите простое и эффективное решение, вы можете добавить следующие команды в ваш файл ~/.inputrc
:
"\e\C-b": shell-backward-kill-word
"\eh": shell-backward-word
"\e\C-f": shell-forward-word
"\e\C-t": "\e\C-b\eh\C-y"
"\e\C-p": "\e\C-b\e\C-f\C-y"
С помощью этих сочетаний клавиш вы сможете быстро перемещать и транспонировать аргументы.
Заключение
Использование Emacs-стиля облагораживает работу в командной строке, но принуждает пользователей преодолевать некоторые ограничения. К счастью, включение пользовательских функций и созданных сочетаний клавиш может значительно улучшить производительность и упростить работу во время интенсивного использования командной строки. Воспользуйтесь вышеописанными подходами для эффективного управления аргументами командной строки и улучшения рабочего процесса в Bash.