Пишем функцию быстрого доступа к сложным командам для Zsh и Bash

Пишем функцию быстрого доступа к сложным командам для Zsh и Bash

Приветствую!

В этой статье мы создадим функцию для оболочек Zsh и Bash, которая позволит быстро и удобно получить доступ к подготовленному списку сложных, длинных и трудно запоминаемых команд🤯. Ну и добавим немного интерактивности с помощью клавиши Tab😉.

Подписывайтесь на наш телеграм @r4ven_me📱, чтобы не пропустить новые публикации на сайте😉. А если есть вопросы или желание пообщаться по тематике – заглядывайте в Вороний чат @r4ven_me_chat🧐.

В чем суть?

⚠️Рассматриваемая ниже функция для Zsh является частью моего файла .zshrc, про тонкую настройку которого я рассказывал в предыдущей статье🔗. В своей конфигурации я использую Zsh совместно с фреймворком Oh-My-Zsh.

Думаю многие пользователя Linux🐧 в своей ежедневной рутине пользуются alias‘ми (псевдонимами) оболочки для быстрого доступа к сложным командам🤯. Я из их числа😌

Но в какой-то момент список таких алиасов становится слишком большим и запоминать их становится все труднее. Да, их можно выводить с помощью одноименной команды alias с последующей фильтрацией, но лично мне это не удобно🤷‍♂️.

Немного заскучав подумав, как можно упростить себе жизнь, решил создать простую, но удобную функцию оболочки, которую достаточно поместить в условный .zshrc или .bashrc.
Что-то типа быстрых команд/кнопок в графических SSH клиентах, наподобие Xshell или Asbru только в консоли🖥️.

Вот что делает эта функция:

  • содержит массив из коротких и понятных ключей-имен, в значении которых расположены, не редко зубодробительные, команды;
  • выводит весь список доступных команд с помощью ключа -h;
  • умеет в интерактивность с помощью “пролистывания” вариантов клавишей Tab (только Zsh);
  • после нажатия Enter команда не выполняется, а вводится в качестве следующей команды, но без выполнения, чтобы ее можно было отредактировать при необходимости.

Ниже небольшая демонстрация работы функции, созданная с помощью asciinema плеера:

⚠️Для корректного отображения цветов отключите темный режим на сайте.

Имя команды-функции — cmd.

Ниже приведу примеры кода для оболочек Zsh и Bash, который нужно добавить в ваш .*rc файл и наполнить массив cmd_list своими командами в формате Zsh:

short_name "long_command"

или в формате Bash:

[short_name]="long_command"

Сразу предупреждаю, что чувствительные для оболочки спец. символы, используемые в мудреных командах — нужно экранировать. В каких-то случаях с помощью кавычек, в иных обратной косой чертой. Пример: \$

А теперь переходим к коду🧑‍💻

Добавление функции в файл .zshrc/.bashrc

В зависимости от используемого вами командного интерпретатора, добавьте код из соответствующего блока ниже👇 в ваш конфигурационный файл .*rc🗒️.

Код для файла .zshrc

Открываем на редактирование конфигурационный файл Zsh:

nvim ~/.zshrc

И добавляем в него следующий код:

# Функция cmd для запуска кастомных команд
# Принимает один аргумент - имя команды
cmd() {
    # Локальная переменная для хранения имени команды
    local cmd_name="${1}"
    # Ассоциативный массив для хранения списка команд
    typeset -A cmd_list

    # Ключи - это имена команд, значения - сами команды
    cmd_list=(
        ps_top5_cpu "ps --sort=-%cpu -eo user,pid,ppid,state,comm | head -n6"
        ps_top5_mem "ps --sort=-%mem -eo user,pid,ppid,state,comm | head -n6"
        cron_add '{ crontab -l; echo "0 3 * * 0 ls -l &> dirs.txt"; } | crontab -'
        du_top20 'du -h / 2> /dev/null | sort -rh | head -n 20'
        journal_vacuum 'journalctl --vacuum-size=800M'
        lsof_opened 'lsof +D /opt'
        ss_listen "ss -tuln | awk '{print \$5}' | grep -Eo ':[0-9]+$' | sort -t: -k2 -n -u"
        sed_replace "sed -i 's/old_text/new_text/g' file.txt"
        find_chmod_f "find /path -type f -exec chmod 644 {} \\;"
        find_chmod_d "find /path -type d -exec chmod 755 {} \\;"
        ossl_encrypt_tar 'tar -czf - /var/log/apt | openssl enc -aes-256-cbc -pbkdf2 -e -out ./logs.tar.gz.enc'
        ossl_decrypt_file 'openssl enc -aes-256-cbc -pbkdf2 -d -in ./logs.tar.gz.enc -out ./logs.tar.gz'
        urandom_str "cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1"
        dig "dig r4ven.me +short +answer +identify"
        icmp_ignore "echo 1 | sudo tee /proc/sys/net/ipv4/icmp_echo_ignore_all"
        docker_network "docker network create --opt com.docker.network.bridge.name=br-mynetwork --opt com.docker.network.enable_ipv6=false --driver bridge --subnet 172.22.23.0/24 --gateway 172.22.23.1 my_network"
        tcpdump_dhost_dport "sudo tcpdump -i any -nn -q dst host 10.11.12.13 and dst port 443"
        tcpdump_wrtie_pacp "sudo tcpdump -nn -i any host 10.11.12.13 -w ./tcpdump.pcap"
        tcpdump_read_pcap "sudo -u tcpdump tcpdump -qns 0 -X -r ./tcpdump.pcap | less"
    )

    # Проверка наличия команды в массиве, или если запрошена справка
    if [[ -z ${cmd_list[$cmd_name]} || -z "$cmd_name" || "$cmd_name" == "-h" ]]; then
        # Вывод списока доступных команд
        echo "AVAILABLE COMMANDS:\n"
        printf "%-20s %s\n" "Key" "Command"
        echo "----------------------------"
        # Перебор всех ключей в массиве с их выводом
        for key in "${(@k)cmd_list}"; do
            printf "%-20s %s\n" "$key" "${cmd_list[$key]}"
            # echo "------------------"
        done | sort
        return 0
    else
        # Если команда найдена, происходит ее вставка в командную строку (без выполнения)
        print -zr "${cmd_list[$cmd_name]}"
        return 0
    fi
}

# Функция для автодополнения команд
_cmd_completion() {
    # Выполнение функции cmd, но только для получения ключей
    local -a keys
    keys=($(cmd -h | awk 'NR>4 {print $1}'))  # Извлечение ключей из вывода справки
    compadd "$@" -- "${keys[@]}"
}

# Регистрация функции автодополнения для команды/функции cmd
compdef _cmd_completion cmd

Сохраняем и зарываем файл. Теперь при каждом новом сеансе будет доступна новая команда — cmd. Чтобы изменения оболочки стали доступны в текущем сеансе выполните:

exec zsh

Теперь попробуйте ввести команду:

cmd -h

Должен выводиться список доступных команд:

Теперь попробуйте “протабать” cmd, чтобы вывести список доступных коротких ключей-имен команд и выбрать нужную:

Как по мне, очень удобно👍.

Код для файла .bashrc

Для Bash код немного другой, но принцип тот же. Аналогично открываем конфиг оболочки:

nvim ~/.bashrc

И добавляем код:

# Функция cmd для запуска кастомных команд
# Принимает один аргумент - имя команды
cmd() {
    # Локальная переменная для хранения имени команды
    local cmd_name="${1}"
    # Ассоциативный массив для хранения списка команд
    declare -A cmd_list

    # Ключи - это имена команд, значения - сами команды
    cmd_list=(
        [ps_top5_cpu]="ps --sort=-%cpu -eo user,pid,ppid,state,comm | head -n6"
        [ps_top5_mem]="ps --sort=-%mem -eo user,pid,ppid,state,comm | head -n6"
        [cron_add]='{ crontab -l; echo "0 3 * * 0 ls -l"; } | crontab -'
        [du_top20]='du -h / 2> /dev/null | sort -rh | head -n 20'
        [journal_vacuum]='journalctl --vacuum-size=800M'
        [lsof_opened]='lsof +D /opt'
        [ss_listen]="ss -tuln | awk '{print \$5}' | grep -Eo ':[0-9]+$' | sort -t: -k2 -n -u"
        [sed_replace]="sed -i 's/old_text/new_text/g' file.txt"
        [find_chmod_d]="find /path -type f -exec chmod 644 {} \\;"
        [find_chmod_f]="find /path -type f -exec chmod 755 {} \\;"
        [ossl_encrypt_tar]='tar -czf - /var/log/apt | openssl enc -aes-256-cbc -pbkdf2 -e -out ./logs.tar.gz.enc'
        [ossl_decrypt_file]='openssl enc -aes-256-cbc -pbkdf2 -d -in ./logs.tar.gz.enc -out ./logs.tar.gz'
        [urandom_str]="cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1"
        [dig]="dig r4ven.me +short +answer +identify"
        [icmp_ignore]="echo 1 | sudo tee /proc/sys/net/ipv4/icmp_echo_ignore_all"
        [docker_network]="docker network create --opt com.docker.network.bridge.name=br-mynetwork --opt com.docker.network.enable_ipv6=false --driver bridge --subnet 172.22.23.0/24 --gateway 172.22.23.1 my_network"
        [tcpdump_dhost_dport]="sudo tcpdump -i any -nn -q dst host 10.11.12.13 and dst port 443"
        [tcpdump_wrtie_pacp]="sudo tcpdump -nn -i any host 10.11.12.13 -w ./tcpdump.pcap"
        [tcpdump_read_pcap]="sudo -u tcpdump tcpdump -qns 0 -X -r ./tcpdump.pcap | less"
    )

    # Проверка наличия команды в массиве, или если запрошена справка
    if [[ -z ${cmd_list[$cmd_name]} || -z "$cmd_name" || "$cmd_name" == "-h" ]]; then
        # Вывод списока доступных команд
        echo "AVAILABLE COMMANDS:"
        printf "%-20s %s\n" "Key" "Command"
        echo "----------------------------"
        # Перебор всех ключей в массиве с их выводом
        for key in "${!cmd_list[@]}"; do
            printf "%-20s %s\n" "$key" "${cmd_list[$key]}"
        done | sort
        return 0
    else
        # Проверяем, запущен ли скрипт в интерактивной оболочке Bash и есть ли подключенный терминал
        if [[ -n "$BASH" && -t 0 ]]; then
            # Получаем команду из списка команд по заданному имени и выводим ее
            local command_to_insert="${cmd_list[$cmd_name]}"
            bind '"\e[0n": "'"${command_to_insert}"'"'
            printf '\e[5n'
        else
            echo "${cmd_list[$cmd_name]}"
        fi
        return 0
    fi
}

# Функция для автодополнения команд
_cmd_completion() {
    # Выполнение функции cmd, но только для получения ключей
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts=$(cmd -h | awk 'NR>4 {print $1}')

    # Если текущее слово (cur) не пустое, ищем соответствующие варианты автодополнения
    if [[ ${cur} == * ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi
}

# Регистрация функции автодополнения для команды/функции cmd
complete -F _cmd_completion cmd

Применяем изменения для текущей сессии оболочки:

exec bash

Выводем справку:

cmd -h

И попробуйте “протабать” команду cmd. К сожалению Bash сможет только вывести список доступных команд, но не переключать их интерактивно, как это умеет Zsh🤷‍♂️.

Послесловие

Сегодня наш арсенал of Linux power user получил очередной полезный инструмент в виде shell-функции, которая по сути является маленькой программой☝️.

Мне кажется получился наглядный пример программирования оболочки, в том смысле: как научить ее выполнять нужное действие еще с привязкой горячей клавиши Tab🎹. Ну и тут хорошо заметна разница в синтаксисе оболочек Zsh и Bash при выполнении одинаковой задачи.

Не забудьте подписаться на наш телеграм канал @r4ven_me, чтобы не пропустить новых заметок на сайте😇. И конечно добавляйтесь в чат @r4ven_me_chat, где мы в своем микросообществе обсуждаем всякое из АйТи💬.

Спасибо, что читаете Вороний блог🐦‍⬛. Всех благ!

Подписаться
Уведомить о
0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии