ocehb: (Default)
[personal profile] ocehb
Перечитал zshexpn, приемы для работы с файлами.

В zsh можно сконфигурировать стиль для дополнений ssh, который по tab генерирует дополнения для, например, имени хоста. Логично брать их из ~/.ssh/config и ~/.ssh/known_hosts.

Форматы пересказывать не буду, их можно посмотрать по манам.
1. Разбор ~/.ssh/config, будем брать имена из строки Host name:
${(s. .)${${"${(@M)${(@f)"$(< ~/.ssh/config)"}:#Host[[:space:]][[:space:][:alnum:]-]##}"}#* }}

Легенда:

  • $(< ~/.ssh/config) читаем файл, результат — массив строк, разбитых по пробелам

  • "$(< ~/.ssh/config)", результат — файл, как он есть, без разбивки

  • ${(@f)"$(< ~/.ssh/config)} — разбиваем контент по строкам, получаем массив строк

  • ${(@M)${(f)"$(< ~/.ssh/config)"}:#Host[[:space:]][[:space:][:alnum:]-]#} — удаляем все, что не попадает под выражение Host name1( name2 ...):

    • В zsh можно использовать классы символов, как [:space:] класс пробелов и [:alnum:] класс алфавитно-цифровых символов. Символ # после класса обозначает повторение, для pcre это совпадает с Host\s+[\w\s]+

    • :# удалят из массива совпадающие с паттерном элементы, флаг M меняет действие на противоположное, удаляет не совпадающие элементы

    • Флаг @ показывает, что имеем дело с массивом.

    Результат:

    
    # print -l ${"${(M@)${(f)"$(< ~/.ssh/config)"}:#Host[[:space:]][[:alnum:][:space:]-]#}"}     
    Host roof
    Host arto home
    Host book
    Host sut
    Host ftp2
    ...
    


  • ${${"${(M@)${(@f)"$(< ~/.ssh/config)"}:#Host[[:space:]][[:alnum:][:space:]-]#}"}##* } — удаляет в каждом элементе массива подстроку сначала и до последнего пробела, оставляя имя хоста:

    # print -l ${${"${(M@)${(@f)"$(< ~/.ssh/config)"}:#Host[[:space:]][[:alnum:][:space:]-]#}"}##* }
    roof
    arto home
    book
    sut
    ftp2
    ...
    


  • (s. .) — делит каждую строку в массиве по пробелу, в результате получим чистый список всех хостов.



Для perl это будет выглядеть так: perl -lne 'm#Host\s+([\w\s]+)# && print $1' ~/.ssh/config, но порождается один дополнительный процесс.

2. Разбор ~/.ssh/known_hosts, хотим брать нормальные имена хостов, ip-адреса и адреса с портом ([host]:port) брать не будем:

# print -l ${${${"${(f)"$(< ~/.ssh/known_hosts)"}"%%[ ,]*}:#\[*\]:*}:#[0-9]#.[0-9]#.[0-9]#.[0-9]#} 
roof.***.ru
book.***.ru
cube.***.ru
ftp2.***.ru
...


Легенда:

  • $(< ~/.ssh/known_hosts) — читаем весь файл, с разбивкой по пробелам

  • "$(< ~/.ssh/known_hosts)" — весь файл без разбивок

  • ${(f)"$(< ~/.ssh/known_hosts)"} — разбиваем контент по строчкам

  • "${(f)"$(< ~/.ssh/known_hosts)"}" — экранируем каждый элемент массива (там могут попадаться спецсимволы, например [ и ]), результат:

    # print -l "${(f)"$(< ~/.ssh/known_hosts)"}"
    roof.***.ru,195.***.130 ssh-dss AAAAB3NzaC1kc3MAAAC...
    book.***.ru,195.***.246 ssh-rsa AAAAB3NzaC1yc2EAAAA...
    ...


  • ${"${(f)"$(< ~/.ssh/known_hosts)"}"%%[ ,]*} — удаляем теги и ключи, всё после пробела или запятой (%%[ ,]*)

  • ${${"${(f)"$(< ~/.ssh/known_hosts)"}"%%[ ,]*}:#\[*\]:*} — удаляем (#:) все элементы массива, совпадающие с паттерном (\[*\]:*, например [www.abc.ru]:22222)

  • ${${${"${(f)"$(< ~/.ssh/known_hosts)"}"%%[ ,]*}:#\[*\]:*}:#[0-9]#.[0-9]#.[0-9]#.[0-9]#} — удляем элементы массива (#:) совпадающие с ip-адресом ([0-9]#.[0-9]#.[0-9]#.[0-9]#)



Теперь можно проверить:

# zstyle ':completion:*:(ssh|scp|sftp):*' hosts ${(o)${${"${(f)"$(< ~/.ssh/known_hosts)"}"%%[ ,]*}:#\[*\]:*}:#[0-9]#.[0-9]#.[0-9]#.[0-9]#}
# ssh <TAB>
roof.***.ru    book.***.ru    cube.***.ru    ftp2.***.ru
...


Profile

ocehb: (Default)
ocehb

January 2021

S M T W T F S
     12
345 6789
10111213141516
17181920212223
24252627282930
31      

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Feb. 10th, 2026 06:33 pm
Powered by Dreamwidth Studios