Автодополнение — одна из самых полезных функций командной строки, позволяющая быстро подбирать подходящие аргументы или команды, просто нажав клавишу Tab. В этой статье я расскажу, как настроить автодополнение для команды ssh, чтобы Bash подсказывал хосты, указанные в файле ~/.ssh/config.

Шаг 0. Понять зачем это нужно

Иногда клиент для подключения к ssh выдает длинную простыню вида super-puper-host.domain.client.cloud.hosting-provider.org, порта 3515 и пользователя awesome-protected-user.

В итоге при подключении на хост через bash надо руками писать что-то вроде

ssh -p 3515 awesome-protected-user@super-puper-host.domain.client.cloud.hosting-provider.org

Что выглядит и воспринимается моим мозгом крайне неудобно. Поэтому я уже давно использую ssh алиасы для этих целей:

Кладу в ~/.ssh/config строки вида

host client-host
    HostName super-puper-host.domain.client.cloud.hosting-provider.org
    User awesome-protected-user
    Port 3515

После чего я в консоли вбиваю `ssh client-host` и подключаюсь на сервер. Все алиасы, которыми пользуюсь регулярно, вбиваются руками на автомате и это крайне удобно. Но я решил этот процесс сделать еще удобнее, чтобы можно было вбить ssh clie, нажать TAB и получить варианты для автоматического завершения.

Свои автокомплиты не писал никогда и пришлось много погуглить, чтобы разобраться как это реализовать и подключить у себя на системе. Работаю на Windows 10 и bash запускается через git bash с использованием C:/Users/{username}/ в качестве домашней директории. Внутри wsl подключаюсь только при острой необходимости и избегаю для рядовых админских задач.

Шаг 1. Настройка автодополнения хостов из ~/.ssh/config

По умолчанию Bash предлагает автодополнение файлов и папок для большинства команд, включая ssh. Чтобы настроить автодополнение хостов, мы используем функцию Bash под названием compgen, которая позволяет генерировать возможные варианты автодополнения.

Ниже приведен код, который позволяет Bash выводить список хостов, заданных в файле ~/.ssh/config, при наборе команды ssh.

Скрипт для автодополнения хостов

Сначала создаем файл ~/.ssh/autocomplete.sh с содержимым

#!/bin/bash

# Функция для автодополнения хостов из файла .ssh/config
_ssh_host_autocomplete() {
    local cword hosts
    cword=${COMP_WORDS[COMP_CWORD]}
    hosts=$(grep -i '^host ' ~/.ssh/config | cut -c 6- | grep -Fv '*')
    COMPREPLY=($(compgen -W "$hosts" -- "$cword"))
}

# Включаем автодополнение для скрипта
complete -o default -o bashdefault -o filenames -F _ssh_host_autocomplete ssh

Как это работает:

  1. Функция _ssh_host_autocomplete_ssh:

    • COMP_WORDS[COMP_CWORD] — это массив, содержащий все введенные в текущей строке аргументы. COMP_CWORD — индекс текущего слова. Мы используем это, чтобы получить слово, которое вводится в данный момент.

    • hosts=$(grep -i '^host ' ~/.ssh/config | cut -c 6- | grep -Fv '*') — грепает все хосты из файла

    • compgen -W "$hosts" -- "$cur_word" — команда compgen используется для создания предложений автодополнения на основе текущего слова и списка хостов. Она выводит подходящие варианты, которые начинаются с введенной строки.

  2. Команда complete:

    • -F _ssh_host_autocomplete_ssh — определяет, что для команды ssh используется наша функция автодополнения _ssh_host_autocomplete_ssh.

Чтобы проверить работоспособность скрипта пишем source ~/.ssh/autocomplete.sh, начинаем вводить ssh cli, жмем TAB и видим магию в виде подсказки автокомплита для быстрого ввода целевого алиаса.

Шаг 2. Автозапуск автокомплита

Теперь дело за малым — прописать в .bashrc строчку source ~/.ssh/autocomplete.sh. Теперь этот автокомплит будет работать сразу после запуска системы.

Шаг 3. Добавление удобного алиаса для получения информации о подключении

Иногда нужно передать другому админу информацию о подключении к серверу проекта. Раньше я открывал .ssh/config руками, искал глазами нужный конфиг, копировал и передавал. После улучшения жизни с автокомплитом сразу захотелось упростить и этот процесс. Сделал так, чтобы можно было написать sshost client-host и сразу получить содержимое из файла именно по этому хосту.

Создал файл ~/ssh/info.sh со следующим содержимым:

#!/bin/bash

# Скрипт для получения параметров подключения для хостов по регулярному выражению
PATTERN=$1

if [ -z "$PATTERN" ]; then
    echo "Usage: $0 <host-pattern>"
    exit 1
fi

SSH_CONFIG="$HOME/.ssh/config"

# Проверяем, существует ли файл .ssh/config
if [ ! -f "$SSH_CONFIG" ]; then
    echo "Error: SSH config file not found."
    exit 1
fi

# Извлекаем все строки для хостов, которые соответствуют переданному регулярному выражению
awk -v pattern="$PATTERN" '
    tolower($1) == "host" && $2 ~ pattern {found=1; print; next}
    found && tolower($1) == "host" {found=0}
    found {print}
' "$SSH_CONFIG"

Внутри .bashrc добавил в конце alias sshost="bash ~/.ssh/info.sh", внутри .ssh/autocomplete.sh добавил строчку complete -o default -o bashdefault -o filenames -F _ssh_host_autocomplete sshost, чтобы для этой команды тоже работал автокомплит.

Скрытый текст

~/.ssh/autocomplete.sh

#!/bin/bash

# Функция для автодополнения хостов из файла .ssh/config
_ssh_host_autocomplete() {
    local cword hosts
    cword=${COMP_WORDS[COMP_CWORD]}
    hosts=$(grep -i '^host ' ~/.ssh/config | cut -c 6- | grep -Fv '*')
    COMPREPLY=($(compgen -W "$hosts" -- "$cword"))
}

# Включаем автодополнение для скрипта
complete -o default -o bashdefault -o filenames -F _ssh_host_autocomplete sshost
complete -o default -o bashdefault -o filenames -F _ssh_host_autocomplete ssh

~/.ssh/info.sh

#!/bin/bash

# Скрипт для получения параметров подключения для хостов по регулярному выражению
PATTERN=$1

if [ -z "$PATTERN" ]; then
    echo "Usage: $0 <host-pattern>"
    exit 1
fi

# Путь к файлу .ssh/config (может быть скорректирован, если он в другом месте)
SSH_CONFIG="$HOME/.ssh/config"

# Проверяем, существует ли файл .ssh/config
if [ ! -f "$SSH_CONFIG" ]; then
    echo "Error: SSH config file not found."
    exit 1
fi

# Извлекаем все строки для хостов, которые соответствуют переданному регулярному выражению
awk -v pattern="$PATTERN" '
    tolower($1) == "host" && $2 ~ pattern {found=1; print; next}
    found && tolower($1) == "host" {found=0}
    found {print}
' "$SSH_CONFIG"

~/.bashrc (добавленные в конце файла строки)

# предыдущее содержимое
alias sshost="bash ~/.ssh/info.sh"
source ~/.ssh/autocomplete.sh

Дальше перезапускаю bash или выполняю команду source ~/.bashrc и теперь у меня:

  • при вводе ssh сразу предлагается автокомплит из вариантов алиасов серверов

  • если введу команду sshost, передам алиас то получу в ответ конфиг, который сразу можно передать разработчику

Заключение

С помощью простой настройки автодополнения в Bash вы можете значительно упростить работу с SSH. Теперь, когда вы будете набирать команду ssh, система предложит только те хосты, которые указаны в файле ~/.ssh/config, что избавит вас от лишних файловых подсказок и ускорит работу.

Эта настройка идеально подходит для тех, кто часто работает с множеством серверов и хочет ускорить подключение через SSH, избегая ошибок в именах хостов.

Благодарю за внимание и буду рад новыми подписчиками на свой Telegram канал @gmoreva.

Комментарии (15)


  1. mkpv
    09.09.2024 10:26
    +3

    Не смотрели в сторону fzf? Он умеет в автокомплит SSH из коробки.


    1. amorev Автор
      09.09.2024 10:26
      +1

      буду изучать что это такое) первый раз от вас про него узнал только что


  1. mavir
    09.09.2024 10:26
    +3

    А какой линукс используете? Так как в Ubuntu из коробки есть автоподстановка для ssh. И не только названия хостов, но и, например, опций. Так же чтение подключаемых конфигов инструкцией include


    1. amorev Автор
      09.09.2024 10:26

      Я на windows сижу с 2018. До этого на ubuntu работал, но достали ошибки диска (`fsck -f /dev/sda1` достало вводить после каждой гибернации)

      На wsl у меня 22.04 ubuntu, но ей пользуюсь редко.

      Круто, что из коробки уже это есть, но я не нашел инфы по этой теме


      1. Johan_Palych
        09.09.2024 10:26
        +3

        Должен быть установлен:
        sudo apt install bash-completion
        https://github.com/scop/bash-completion/tree/main/completions
        Autocomplete server names for SSH and SCP
        https://unix.stackexchange.com/questions/136351/autocomplete-server-names-for-ssh-and-scp
        Autocomplete server names for SSH and SCP for Git Bash
        https://gist.github.com/nhthai2005/f956a0e6cfc3b38f72f0df1a239a4e68

        ls /usr/share/bash-completion/completions/ | grep ssh


        1. amorev Автор
          09.09.2024 10:26
          +1

          это все для wsl может быть справедливо. А я нативно на windows запускаю git bash


  1. Shvedov
    09.09.2024 10:26

    Хм, а зачем баш для этого? ssh прекрасно запускается и без него. Для PowerShell уже всё давно придуманно для этого.


    1. amorev Автор
      09.09.2024 10:26

      Мне как то гит баш привычнее


  1. square
    09.09.2024 10:26
    +1

    Кто пользуется OMZ настоятельно рекомендую плагин zsh-ssh, по табу выпадает список хостов с возможностью добавить описания к ним. В принципе без OMZ тоже вроде работает, но не пробовал


  1. phikus
    09.09.2024 10:26

    Для тренировки норм, но после zsh, где это работает искаропки и где хосты можно стрелочками листать это прям не


    1. amorev Автор
      09.09.2024 10:26

      у меня так zsh не завелся с такими параметрами. Плюс на винде zsh сходит с ума, если заходишь в папку с git(


  1. Habetdin
    09.09.2024 10:26
    +1

    мои тщетные попытки заставить его не предлагать мне файлы из текущей директории, которые начинаются по этому паттерну. Если знаете как это сделать, чтобы работало, то буду признателен.

    @amorev Попробуйте убрать все лишние опции и начать с нуля, добавляя только нужные :)

    Немного переделал для себя autocomplete.sh, чтобы предлагались в т.ч. и множественные алиасы (Host foo bar baz), но игнорировались wildcard-конфигурации (Host *, Host *.host.dev):

    #!/bin/bash
    
    _autocomplete_ssh_host() {
        local cword hosts
        cword=${COMP_WORDS[COMP_CWORD]}
        hosts=$(grep -i '^host ' ~/.ssh/config | cut -c 6- | grep -Fv '*')
        COMPREPLY=($(compgen -W "$hosts" -- "$cword"))
    }
    
    complete -F _autocomplete_ssh_host ssh
    


    1. amorev Автор
      09.09.2024 10:26

      Это очень круто! Спасибо большое) Статью обновлю! Ваш способ заметно круче моего!


  1. fearan
    09.09.2024 10:26

    Круто!

    Только вот в реальном мире .ssh/config обычно начинается со строки

    Include ~/.ssh/config.prod


    1. amorev Автор
      09.09.2024 10:26

      Не сталкивался с таким реальном миром:) но думаю и тут что-то можно придумать)