Как транспонировать аргументы командной строки с помощью клавиш Emacs?

Вопрос или проблема

При использовании 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.

Оцените материал
Добавить комментарий

Капча загружается...