/
huginn
/
termux-dotfiles
Обзор
Документация
Войти
/
huginn
/
termux-dotfiles
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
dev
bash/bashrc
1 095 строк
33 KB
huginn
Added proxy settings.
18 июл 2026, 14:29
18 июл 2026, 14:29
38e3dbe
Код
Авторство
О чём код?
# shellcheck shell=bash # path: $XDG_CONFIG_HOME/bash/bashrc # ОПЦИИ SHELL {{{1 # vi {{{2 # использовать режим vi для редактирования командной строки set -o vi # }}}2 # }}}1 # ОПЦИИ BASH {{{1 # autocd {{{2 # переходить в каталог, указанный как команда shopt -s autocd # cdable_vars {{{2 # рассматривать аргумент команды cd, который не является каталогом, как имя переменной, содержащей путь к каталогу для перехода shopt -s cdable_vars # cdspell {{{2 # редактировать ошибки в аргументе cd shopt -s cdspell # checkjobs {{{2 # не выходить при незавершённых заданиях shopt -s checkjobs # direxpand {{{2 # выполнять расширение слов в именах каталогов при автодополнении, изменяя буфер readline shopt -s direxpand # dirspell {{{2 # пытаться исправить ошибки в именах каталогов при автодополнении слов, если указанный изначально каталог не существует shopt -s dirspell # dotglob {{{2 # включать имена каталогов, начинающиеся с точки, в расширение имени пути shopt -s dotglob # extglob {{{2 # включить расширенное сопоставление шаблонов shopt -s extglob # globstar {{{2 # сопоставлять шаблон `**' при расширении имени пути со всеми вложенными файлами и каталогами shopt -s globstar # histappend {{{2 # при выходе добавлять список истории в файл истории, указанный в переменной HISTFILE, вместо его перезаписи shopt -s histappend # histreedit {{{2 # при использовании readline предоставлять возможность повторно отредактировать неудачную подстановку истории shopt -s histreedit # histverify {{{2 # при использовании readline помещать подстановку readline в буфер readline для дальнейшего редактирования вместо немедленного выполнения shopt -s histverify # lastpipe {{{2 # запускать последнюю команду конвейера не в фоновом режиме в текущем окружении оболочки, если управление заданиями не активно shopt -s lastpipe # mailwarn {{{2 # вывести сообщение `The mail in mailfile has been read', если к файлу, проверяемому оболочкой на наличие почты, обращались с момента последней проверки shopt -s mailwarn # nullglob {{{2 # при отсутствии совпадений расширять шаблоны до пустой строки, а не до буквального значения shopt -s nullglob # }}}2 # }}}1 # Функции проверки окружения {{{1 # андроид {{{2 _andr() { [[ -v ANDROID_DATA ]] } # андроид без proot {{{2 _andr_no_proot() { [[ -v ANDROID_DATA && -v PREFIX ]] } # mt manager {{{2 _mt_manager() { [[ $PREFIX == *"bin.mt.plus"* ]] } # андроид proot {{{2 _andr_proot() { [[ -v ANDROID_DATA && ! -v PREFIX ]] } # термукс без proot {{{2 _termux_no_proot() { [[ -v TERMUX_VERSION ]] } # bash 5.3 {{{2 _bash_53() { : "${BASH_VERSINFO[0]}${BASH_VERSINFO[1]}" (( $_ == 53 )) } # }}}2 # }}}1 # ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ {{{1 # Мои переменные {{{2 # BASHRC_LOADED {{{3 # в разных эмуляторах или системах ~/.bashrc читается из разных файлов, поэтому устанавливаем переменную, что файл прочитан, для проверки в ~/.profile # плюс проверка, что bashrc читается из файла, а не напрямую из командной строки if ((SHLVL == 1)) && [[ -v BASH_SOURCE[1] ]]; then BASHRC_LOADED=1 export BASHRC_LOADED fi # DISTRO {{{3 # хост или дистрибутив if _termux_no_proot; then DISTRO="termux" elif _mt_manager; then DISTRO="mtmanager" else : '\h' DISTRO="${_@P}" fi export DISTRO # STORAGES {{{3 # устанавливаем массив дополнительных хранилищ, если они есть while read -r _ _storage _; do [[ $_storage == "/storage/"+([-A-F0-9]) && -r $_storage ]] && STORAGES[_idx++]="$_storage" done < /proc/self/mounts unset _storage _idx ((${#STORAGES[@]})) && export STORAGES # }}}3 # }}}2 # Переменные BASH {{{2 # SHELL {{{3 # если SHELL не экспортирована if [[ ${SHELL@a} != *'x'* ]]; then SHELL="$0" export SHELL fi # BASH_LOADABLES_PATH {{{3 # путь к загружаемым дополнительным встроенным командам, если это termux без proot if _termux_no_proot && [[ $BASH_LOADABLES_PATH != *"$PREFIX/usr/lib/bash"* ]]; then BASH_LOADABLES_PATH="$BASH_LOADABLES_PATH:$PREFIX/usr/lib/bash" export BASH_LOADABLES_PATH fi # BASH_ENV {{{3 # файл инициализации для скриптов; если недоступен, чужой сбрасываем if [[ -r $XDG_CONFIG_HOME/bash/bash_env ]]; then BASH_ENV="$XDG_CONFIG_HOME/bash/bash_env" export BASH_ENV else unset BASH_ENV fi # CDPATH {{{3 # каталоги для поиска cd CDPATH=".:$HOME" if _andr; then if [[ -v PREFIX ]]; then CDPATH+=":$PREFIX" fi if [[ $HOME != @("/sdcard"|"/storage/emulated/0"|"/storage/self/primary") ]]; then CDPATH+=':/sdcard' fi fi # если есть файл инициализации для скриптов, можно экспортировать, т. к. там CDPATH сбрасывается if [[ -v BASH_ENV ]]; then export CDPATH fi # FCEDIT {{{3 # редактор по умолчанию для fc if command -v vim >/dev/null; then FCEDIT='vim' export FCEDIT fi # HISTCONTROL {{{3 # управление историей команд HISTCONTROL='ignoreboth:erasedups' # HISTFILE {{{3 # файл истории if [[ ! -r $XDG_STATE_HOME/bash/bash_history ]]; then if [[ ! -d $XDG_STATE_HOME/bash ]]; then mkdir -p "$XDG_STATE_HOME/bash" fi : > "$XDG_STATE_HOME/bash/bash_history" fi HISTFILE="$XDG_STATE_HOME/bash/bash_history" # HISTFILESIZE {{{3 # размер файла истории HISTFILESIZE=50000 # HISTIGNORE {{{3 # список шаблонов команд, не добавляемых в историю HISTIGNORE=" *":"exit":"logout":"clear":"ls":"ll":"cd":"time &" # HISTSIZE {{{3 # размер списка истории HISTSIZE=50000 # HISTTIMEFORMAT {{{3 # добавление временно́й метки к командам в истории HISTTIMEFORMAT="%d.%m.%y %H:%M:%S -> " # INPUTRC {{{3 # файл инициализации readline if [[ -r $XDG_CONFIG_HOME/bash/inputrc ]]; then INPUTRC="$XDG_CONFIG_HOME/bash/inputrc" fi # LANG, LC {{{3 # язык if command -v locale >/dev/null; then LANG='ru_RU.UTF-8' export LANG # разделитель в числах — точка LC_NUMERIC=C export LC_NUMERIC # нормальная сортировка имён файлов, диапазоны и т. д. LC_COLLATE=C export LC_COLLATE fi # PATH {{{3 # пути к скриптам # регулярки проверяют, есть ли уже такой путь в PATH; сделано, чтоб каталоги не задваивались при source .bashrc if [[ ! $PATH =~ .*(^|:)"$HOME/.local/bin"(:|$) ]]; then PATH="$HOME/.local/bin:$PATH" fi if [[ ! $PATH =~ .*(^|:)"$HOME/bin"(:|$) ]]; then PATH="$HOME/bin:$PATH" fi export PATH # USER {{{3 # имя пользователя, если не установлено (termux не устанавливает эту переменную) if [[ ! -v USER ]]; then : '\u' USER="${_@P}" export USER fi # }}}3 # }}}2 # Переменные программ {{{2 # DISPLAY {{{3 # графический интерфейс DISPLAY=":1" export DISPLAY # EDITOR {{{3 # редактор файлов по умолчанию if command -v vim >/dev/null; then EDITOR='vim' export EDITOR fi # GPG {{{3 # каталог gpg if command -v gpg >/dev/null; then GNUPGHOME="$XDG_CONFIG_HOME/gnupg" export GNUPGHOME fi # GREP_COLORS {{{3 # цветной вывод # grep GREP_COLORS='sl=:cx=38;5;102:mt=01;32:fn=01;35:ln=38;5;173:bn=38;5;173:se=36' export GREP_COLORS # JQ_COLORS {{{3 # цвета вывода jq if command -v jq &>/dev/null; then export JQ_COLORS='0;40:0;31:0;35:0;38;5;173:0;32:1;33:1;36:1;34' fi # LESS {{{3 # опции less по умолчанию # устанавливается здесь и без промпта для того, чтобы учесть промпт, установленный с man; полностью строка обрабатывается в файле lesskey LESS=' --RAW-CONTROL-CHARS --ignore-case --LONG-PROMPT --tabs=2 --mouse --incsearch --line-num-width=2 --no-histdups --save-marks --use-color -Dd+3$Du+6$DP3.0$DB1$DC5$DE1.0$DM173$DN102$DS0.2$DW-.238$Ds+4$ ' export LESS # LESSKEYIN {{{3 # файл инициализации LESS if [[ -r $XDG_CONFIG_HOME/less/lesskey ]]; then export LESSKEYIN="$XDG_CONFIG_HOME/less/lesskey" fi # LESSQUIET {{{3 # подавление дополнительного вывода lesspipe if command -v lesspipe &>/dev/null; then LESSQUIET=1 export LESSQUIET fi # LS_COLORS {{{3 # цветное отображение различных типов файлов # ls, dir, vdir if [[ -r $XDG_CONFIG_HOME/dircolors/dircolors ]]; then eval "$(dircolors -b "$XDG_CONFIG_HOME/dircolors/dircolors")" else eval "$(dircolors -b)" fi # добавляем исполняемые файлы в зависимости от каталога; т. к. в хранилищах андроида они не имеют смысла _precmd_set_ls_colors() { case $PWD in "/storage"*|"/sdcard"*) LS_COLORS="${LS_COLORS%ex=*}""ex=00" ;; *) LS_COLORS="${LS_COLORS%ex=*}""ex=01;32" ;; esac export LS_COLORS } # MANLESS {{{3 # опции для less # установка промпта для man MANLESS='Man\:\ \$MAN_PN\ ?lt%lt-%lb?L(%L).\ :byte\ %bB?s/%s..' export MANLESS # MANOPT {{{3 # опции man # --warnings=!w отключает предупреждения troff, которые портят вывод в man плагине vim MANOPT='--warnings=!w' export MANOPT # MANPAGER {{{3 # программа для просмотра манов MANPAGER='less' export MANPAGER # MANROFFOPT {{{3 # опции groff при использовании man # -c нужна для цветного вывода less MANROFFOPT='-c' export MANROFFOPT # PROXY {{{3 # установка всех переменных прокси http_proxy="http://127.0.0.1:28511" HTTP_PROXY="$http_proxy" https_proxy="$http_proxy" HTTPS_PROXY="$http_proxy" all_proxy="$http_proxy" ALL_PROXY="$http_proxy" declare -A proxy_no=( ["yandex.ru"]="" ["mail.ru"]="" ["cloudbeeline.ru"]="" ) no_proxy="localhost,127.0.0.1" for _p in "${!proxy_no[@]}"; do no_proxy+=",$_p,.$_p" done NO_PROXY="$no_proxy" export http_proxy HTTP_PROXY https_proxy HTTPS_PROXY all_proxy ALL_PROXY no_proxy NO_PROXY proxy_no unset _p # PULSE_SERVER {{{3 if [[ ! -v PULSE_SERVER ]]; then PULSE_SERVER="127.0.0.1" export PULSE_SERVER fi # NPM_CONFIG_USERCONFIG {{{3 # конфиг npm if command -v npm &>/dev/null; then NPM_CONFIG_USERCONFIG="$XDG_CONFIG_HOME/npm/npmrc" export NPM_CONFIG_USERCONFIG fi # QT_QPA_PLATFORMTHEME {{{3 # тема для Qt if [[ $DISTRO == "ubuntu" ]]; then QT_QPA_PLATFORMTHEME=qt5ct export QT_QPA_PLATFORMTHEME fi # QUOTING_STYLE {{{3 # метод экранирования имён файлов # shell-escape — заключать имена в одинарные кавычки, если они содержат метасимволы оболочки, а также отображать непечатаемые символы в стиле $'' # ls, dir, vdir QUOTING_STYLE=shell-escape export QUOTING_STYLE # SIMPLE_BACKUP_SUFFIX {{{3 # суффикс резервных копий # cp, install, ln, mv SIMPLE_BACKUP_SUFFIX='.bak' export SIMPLE_BACKUP_SUFFIX # TERMINFO {{{3 # каталог для terminfo (разные терминалы) if [[ -d $XDG_DATA_HOME/terminfo ]]; then TERMINFO="$XDG_DATA_HOME/terminfo" export TERMINFO fi # TEXT_VIMCOLOR_ANSI {{{3 # установка цветов для vimcolor if command -v vimcolor &>/dev/null; then TEXT_VIMCOLOR_ANSI='Comment=ANSI102;Constant=ANSI173;String=ANSI2;Boolean=ANSI95;Identifier=ANSI1;Function=ANSI4;Statement=ANSI6;PreProc=ANSI5;Type=ANSI3;Special=ANSI2;Underlined=underline;Error=ANSI7 ANSI1;Todo=ANSI0 ANSI3;' export TEXT_VIMCOLOR_ANSI fi # TIME_STYLE {{{3 # формат отображения времени # ls, dir, vdir TIME_STYLE='+%d.%m.%y' export TIME_STYLE # TMUX_TMPDIR {{{3 # временный каталог tmux if command -v tmux &>/dev/null; then if [[ ! -d $XDG_CACHE_HOME/tmux ]]; then mkdir -p "$XDG_CACHE_HOME/tmux" fi TMUX_TMPDIR="$XDG_CACHE_HOME/tmux" export TMUX_TMPDIR fi # TZ {{{3 # временная зона TZ='Europe/Moscow' export TZ # VERSION_CONTROL {{{3 # метод создания резервных копий # existing — создавать нумерованный бэкап, если такой уже существует, иначе — обычный # cp, install, ln, mv VERSION_CONTROL=existing export VERSION_CONTROL # }}}3 # }}}2 # }}}1 # МОДУЛИ {{{1 if _termux_no_proot; then declare load_path="$PREFIX/lib/bash" elif _andr_proot; then declare load_path="/usr/lib/bash" fi declare -a modules # сетевое соединение modules+=( "accept" ) # разбивка на поля modules+=( "csv" ) # разбивка на поля с экранированием modules+=( "dsv" ) # установка атрибутов файловых дескрипторов modules+=( "fdflags" ) # информация о файловых атрибутах modules+=( "finfo" ) # арифметика с плавающей точкой modules+=( "fltexpr" ) # чтение в ассоциативный массив modules+=( "kv" ) # печать modules+=( "print" ) # создание дочерней оболочки modules+=( "push" ) # печать даты и времени modules+=( "strftime" ) # конвертация даты и времени modules+=( "strptime" ) for m in "${modules[@]}"; do if [[ -x $load_path/$m ]]; then enable -f $m $m 2>/dev/null fi done unset load_path m modules # }}}1 # ПРИГЛАШЕНИЯ {{{1 # Переменные приглашений {{{2 # статус выхода последней команды {{{3 # устанавливается в первом элементе PROMPT_COMMAND (_ps1_all_cmds) declare _ps1_exit_code # время начала команды {{{3 # устанавливается в trap DEBUG последней командой как "${EPOCHREALTIME//[.,]}" (_ps1_set_timer_start) declare _ps1_timer_start # время окончания команды {{{3 # устанавливается в первом элементе PROMPT_COMMAND как "${EPOCHREALTIME//[.,]}" (_ps1_all_cmds) declare _ps1_timer_end # общее время выполнения команды {{{3 # устанавливается в функции в PROMPT_COMMAND (_ps1_all_cmds -> _ps1_time_cmd) declare _ps1_timer # количество файлов и каталогов текущем каталоге в формате dirs/files {{{3 # устанавливается в функции в PROMPT_COMMAND (_ps1_all_cmds -> _ps1_file_count) declare _ps1_num_files # текущий каталог, обрезанный по ширине, в формате /p/a/t/h/to_dir {{{3 # устанавливается в функции в PROMPT_COMMAND (_ps1_all_cmds -> _ps1_trim_pwd) declare _ps1_trim_path # дополнительный перевод строки после статуса выхода или таймера, если есть {{{3 # устанавливается в функции в PROMPT_COMMAND (_ps1_all_cmds -> _ps1_print_nl) declare _ps1_new_line # дополнительный буфер (tmux, vim, etc) {{{3 # устанавливается в функции в PROMPT_COMMAND (_ps1_all_cmds -> _ps1_set_buf) declare _ps1_buf # номер строки, от которого отсчитывается текущая строка в PS2 # устанавливается в функции в PROMPT_COMMAND (_ps2_get_start_lineno) declare -i _ps2_start_lineno # сохранение переменной $_ {{{3 # устанавливается в первой функции в trap DEBUG declare _preexec_lastarg # цвета промпта {{{3 # устанавливаются в функции в PROMPT_COMMAND (_ps1_all_cmds -> _ps_set_colors) declare _ps1_err_clr _ps1_time_clr _ps1_user_clr _ps1_uh_sep_clr _ps1_host_clr _ps1_pwd_clr _ps1_fnum_clr _ps1_sep_clr _ps1_cmdnum_clr _ps1_buf_clr _ps1_sh_clr _ps1_euid_clr _ps0_start_clr _ps2_lineno_clr _ps_bold_fmt _ps_reset_fmt # }}}3 # }}}2 # Функции приглашений {{{2 # _ps1_err_exit_code {{{3 # отображение ненулевого статуса выхода _ps1_err_exit_code() { if (( _ps1_exit_code )); then _ps1_exit_code="?>$_ps1_exit_code " else _ps1_exit_code="" fi } # _ps1_file_count {{{3 # подсчёт файлов в текущем каталоге _ps1_file_count() { local -a all_num dirs_num all_num=( * ) dirs_num=( */ ) _ps1_num_files="${#dirs_num[@]}/$((${#all_num[@]} - ${#dirs_num[@]}))" } # _ps1_trim_pwd {{{3 # обрезка каталогов в pwd по ширине _ps1_trim_pwd() { local -i width local -a arr_dirs local -i idx=1 local -i dlen width=$((COLUMNS - ( ${#USER} + ${#HOSTNAME} + ${#_ps1_num_files} + 3) )) # последнее число — разные символы-разделители в PS : "${PWD/#"$HOME"/"~"}" : "${_/#"/storage/emulated/0"/"/sdcard"}" _ps1_trim_path="${_/#"/storage/self/primary"/"/sdcard"}" # оставляем первую букву от каталогов по порядку, пока не впишемся в ширину ((${#_ps1_trim_path} <= width)) && return IFS=/ read -r -a arr_dirs <<< "$_ps1_trim_path" dlen=$((${#arr_dirs[@]} - 1)) # текущий каталог не сокращаем while ((${#_ps1_trim_path} > width && idx < dlen)); do arr_dirs[idx]="${arr_dirs[idx]::1}" # первая буква printf -v _ps1_trim_path '/%s' "${arr_dirs[@]:1}" _ps1_trim_path="${arr_dirs[0]}$_ps1_trim_path" ((idx++)) done # если всё равно шире, обрезаем по ширине и в начале ставим многоточие if ((${#_ps1_trim_path} > width)); then : "${_ps1_trim_path:(3-width)}" _ps1_trim_path="${_ps1_trim_path/*/..."$_"}" fi } # _ps1_time_cmd {{{3 # время выполнения команды (расхождение с реальным на пару сотых) # из EPOCHREALTIME удаляем разделители, по сути умножаем на 10^6, соответственно реальное число будет 10^-6; сделано для эмуляции арифметики с плавающей точкой _ps1_time_cmd() { printf -v _ps1_timer '%.2fs' "$((_ps1_timer_end - _ps1_timer_start))e-6" # если меньше единицы, то не отображаем ((${_ps1_timer%[.,]*})) || _ps1_timer="" } # _ps1_print_nl {{{3 # перевод строки # добавляем, если не пусты статус выхода или таймер _ps1_print_nl() { if [[ -n $_ps1_exit_code || -n $_ps1_timer ]]; then _ps1_new_line='\n' else _ps1_new_line="" fi } # _ps1_set_buf {{{3 # дополнительный буфер # вторая проверка, чтоб значения не задваивались _ps1_set_buf() { [[ -v TMUX && $_ps1_buf != *tmux* ]] && _ps1_buf+='tmux-' [[ -v VIM && $_ps1_buf != *vim* ]] && _ps1_buf+='vim-' } # _ps_set_colors {{{3 # установка цветов ps1 _ps_set_colors() { local -A arr_fmt arr_fmt["black"]="\e[30m" arr_fmt["red"]="\e[31m" arr_fmt["green"]="\e[32m" arr_fmt["yellow"]="\e[33m" arr_fmt["blue"]="\e[34m" arr_fmt["magenta"]="\e[35m" arr_fmt["cyan"]="\e[36m" arr_fmt["white"]="\e[37m" arr_fmt["orange"]="\e[38;5;173m" arr_fmt["bluegray"]="\e[38;5;59m" arr_fmt["lightgray"]="\e[38;5;102m" arr_fmt["gray"]="\e[38;5;238m" arr_fmt["black_bg"]="\e[40m" arr_fmt["red_bg"]="\e[41m" arr_fmt["green_bg"]="\e[42m" arr_fmt["yellow_bg"]="\e[43m" arr_fmt["blue_bg"]="\e[44m" arr_fmt["magenta_bg"]="\e[45m" arr_fmt["cyan_bg"]="\e[46m" arr_fmt["white_bg"]="\e[47m" arr_fmt["orange_bg"]="\e[48;5;173m" arr_fmt["bluegray_bg"]="\e[48;5;59m" arr_fmt["lightgray_bg"]="\e[48;5;102m" arr_fmt["gray_bg"]="\e[48;5;238m" arr_fmt["bold"]="\e[1m" arr_fmt["nobold"]="\e[22m" arr_fmt["italic"]="\e[3m" arr_fmt["underline"]="\e[4m" arr_fmt["revers"]="\e[7m" arr_fmt["reset"]="\e[0m" # статус выхода if [[ -n $_ps1_exit_code ]]; then _ps1_err_clr="${arr_fmt["red"]}" else _ps1_err_clr="" fi # таймер if [[ -n $_ps1_timer ]]; then _ps1_time_clr="${arr_fmt["yellow"]}" else _ps1_time_clr="" fi # имя пользователя _ps1_user_clr="${arr_fmt["yellow"]}" # разделитель между пользователем и хостом if [[ -v SSH_CONNECTION ]]; then _ps1_uh_sep_clr="${arr_fmt["magenta"]}" else _ps1_uh_sep_clr="${arr_fmt["yellow"]}" fi # хост _ps1_host_clr="${arr_fmt["yellow"]}" # текущий каталог _ps1_pwd_clr="${arr_fmt["blue"]}" # число файлов _ps1_fnum_clr="${arr_fmt["cyan"]}" # основной разделитель _ps1_sep_clr="${arr_fmt["lightgray"]}" # номер в истории _ps1_cmdnum_clr="${arr_fmt["orange"]}" # буфер if [[ -n $_ps1_buf ]]; then _ps1_buf_clr="${arr_fmt["green"]}" else _ps1_buf_clr="" fi # оболочка _ps1_sh_clr="${arr_fmt["green"]}" # если не оболочка входа, отключаем жирный [[ ${0:1} != '-' && $SHLVL -gt 1 ]] && _ps1_sh_clr+="${arr_fmt["nobold"]}" # знак euid ($|#) if ((EUID)); then _ps1_euid_clr="${arr_fmt["green"]}" else _ps1_euid_clr="${arr_fmt["red"]}" fi # время начала команды _ps0_start_clr="${arr_fmt["green"]}" # номер многострочной команды _ps2_lineno_clr="${arr_fmt["gray"]}" # жирный _ps_bold_fmt="${arr_fmt["bold"]}" # сброс _ps_reset_fmt="${arr_fmt["reset"]}" } # _ps1_all_cmds {{{3 # общая функция для определения параметров промпта, должна быть первой в PROMPT_COMMAND # _ps1_all_cmds _ps1_all_cmds() { _ps1_exit_code=$? _ps1_timer_end="${EPOCHREALTIME//[.,]}" _ps1_err_exit_code _ps1_time_cmd _ps1_print_nl _ps1_file_count _ps1_trim_pwd _ps1_set_buf _ps_set_colors } # _ps2_get_start_lineno {{{3 # установка отсчёта строк для PS2 _ps2_get_start_lineno() { local -i lineno="$1" _ps2_start_lineno=$((lineno - 1)) } # }}}3 # }}}2 # Установка PS {{{2 # PS0 {{{3 # блочный курсор, чтобы в vim и т.д. начиналось с блочного PS0='\[\e[2 q\]' # перевод строки PS0+='\n' # время начала выполнения # shellcheck disable=SC2016 PS0+='\[${_ps0_start_clr@E}\]-- \D{%T} --\[${_ps_reset_fmt@E}\]' # перевод строки PS0+='\n' # PS1 {{{3 ### нулевая строка: ненулевой статус выхода и время выполнения больше 1 секунды # статус выхода, если не нулевой PS1='\[${_ps1_err_clr@E}\]$_ps1_exit_code' # время выполнения, если больше 1с PS1+='\[${_ps1_time_clr@E}\]$_ps1_timer' # перевод строки и дополнительный перевод строки, если есть статус выхода или время выполнения PS1+='${_ps1_new_line@E}\n' ### первая строка: юзер@хост:путь:число_файлов # юзер PS1+='\[${_ps_bold_fmt@E}${_ps1_user_clr@E}\]\u' # @ PS1+='\[${_ps1_uh_sep_clr@E}\]@' # хост PS1+='\[${_ps1_host_clr@E}\]\h' # разделитель PS1+='\[${_ps1_sep_clr@E}\]:' # путь: PS1+='\[${_ps1_pwd_clr@E}\]$_ps1_trim_path' # разделитель PS1+='\[${_ps1_sep_clr@E}\]:' # число каталогов/файлов PS1+='\[${_ps1_fnum_clr@E}\]$_ps1_num_files' # перевод строки PS1+='\[${_ps_reset_fmt@E}\]\n' ### вторая строка: номер_команды:[буфер]-оболочка-$ # номер команды в истории PS1+='\[${_ps_bold_fmt@E}${_ps1_cmdnum_clr@E}\]\!' # разделитель PS1+='\[${_ps1_sep_clr@E}\]:' # буфер PS1+='\[${_ps1_buf_clr@E}\]$_ps1_buf' # оболочка PS1+='\[${_ps1_sh_clr@E}\]${0##?(*/)?(-)}\[${_ps_bold_fmt@E}\]-' # euid PS1+='\[${_ps1_euid_clr@E}\]\$ \[${_ps_reset_fmt@E}\]' # PS2 {{{3 # вывод номера строки PS2='\[${_ps2_lineno_clr@E}\]$((LINENO - _ps2_start_lineno))\[${_ps_reset_fmt@E}\] ' # PS4 {{{3 # в bash_env # }}}3 # }}}2 # }}}1 # PRECMD {{{1 # Закладки {{{2 # вначале всегда идут закладки (на андроид), к ним по порядку добавляются посещённые каталоги # переменная для отслеживания смены каталога declare _bmrk_oldpwd="" # реальный стек каталогов declare -a _bmrk_stack # ассоциативный массив для быстрой проверки существования каталога в стеке declare -A _bmrk_stack_invert # на андроид добавляем в закладки внутреннюю память, sd-карту, флешки, если есть # внутренняя память _andr && { _bmrk_stack[0]="" # оставляем первый элемент пустым, т. к. в DIRSTACK это всегда текущий каталог _bmrk_stack[1]="/sdcard" } # доп. хранилища _add_start_bmrk() { local _storage local -i idx=2 if ((${#STORAGES[@]})); then for _storage in "${STORAGES[@]}"; do _bmrk_stack[idx++]="$_storage" done fi } if _andr; then _add_start_bmrk fi for _elem in "${_bmrk_stack[@]:1}"; do pushd -n "" >/dev/null 2>&1 # просто добавляем в DIRSTACK пустые элементы по количеству нужных _bmrk_stack_invert["$_elem"]="" done # добавляем домашний каталог, чтоб он не сохранялся в стеке _bmrk_stack_invert["$HOME"]="" # изменяем DIRSTACK на реальный стек DIRSTACK=( "${_bmrk_stack[@]}" ) unset _elem unset -f _add_start_bmrk # добавление новых каталогов в стек _bmrk_add() { if [[ "$_bmrk_oldpwd" != "$OLDPWD" ]]; then if [[ ! -v _bmrk_stack_invert["$OLDPWD"] ]]; then pushd -n "" >/dev/null 2>&1 _bmrk_stack+=( "$OLDPWD" ) _bmrk_stack_invert["$OLDPWD"]="" DIRSTACK=( "${_bmrk_stack[@]}" ) fi _bmrk_oldpwd="$OLDPWD" fi } # Общая история для всех сессий {{{2 _precmd_shared_history() { history -n history -w history -c history -r } # Установка заголовка сессии в имя оболочки {{{2 _precmd_set_title() { local cur_sh="${0##*/}" : "${cur_sh#-}" # удаляем дефис из начала, если оболочка входа printf '\e]0;%s\007' "${_@U}" >&2 } # Сброс таймера команды {{{2 _precmd_reset_timer() { _ps1_timer_start="" } # PROMPT_COMMAND {{{2 PROMPT_COMMAND[0]='_ps1_all_cmds' PROMPT_COMMAND[1]='_ps2_get_start_lineno "$LINENO"' PROMPT_COMMAND[2]='_precmd_shared_history' PROMPT_COMMAND[3]='_bmrk_add' PROMPT_COMMAND[4]='_precmd_set_title' PROMPT_COMMAND[5]='_precmd_set_ls_colors' PROMPT_COMMAND[6]='_precmd_reset_timer' export PROMPT_COMMAND # }}}2 # }}}1 # PREEXEC {{{1 # _preexec_save_lastarg {{{2 # сохранение $_ # _preexec_save_lastarg "$_" _preexec_save_lastarg() { _preexec_lastarg="$1" } # _preexec_set_cmd {{{2 # определение имени текущей команды declare _preexec_cur_cmd _preexec_set_cmd() { local -a arr_cmd local opt _preexec_cur_cmd_f read -r -a arr_cmd <<< "$BASH_COMMAND" # так arr_cmd=( "${arr_cmd[@]#\\}" ) case ${arr_cmd[0]} in "time") _preexec_cur_cmd="${arr_cmd[1]}" ;; "fg") if [[ -n ${arr_cmd[1]} ]]; then _preexec_cur_cmd="${arr_cmd[1]}" else read -ra arr_cmd < <(jobs) _preexec_cur_cmd="${arr_cmd[2]}" fi ;; "command") case ${arr_cmd[1]} in "-p") [[ -n ${arr_cmd[2]} ]] && _preexec_cur_cmd="${arr_cmd[2]}" ;; "-"[vV]) ;; *) _preexec_cur_cmd="${arr_cmd[1]}" ;; esac ;; "builtin") if [[ -n ${arr_cmd[1]} ]]; then _preexec_cur_cmd="${arr_cmd[1]}" fi ;; "exec") set -- "${arr_cmd[@]:1}" while getopts ':lca:' opt; do case $opt in "a") _preexec_cur_cmd="$OPTARG" _preexec_cur_cmd_f=1 break ;; *) ;; esac done shift $(( OPTIND - 1 )) [[ -z $_preexec_cur_cmd_f ]] && _preexec_cur_cmd="$1" ;; *) _preexec_cur_cmd="${arr_cmd[0]}" ;; esac } # _preexec_set_env {{{2 # установка окружения в зависимости от запускаемой оболочки _preexec_set_env() { case $_preexec_cur_cmd in "sh"|"dash") if [[ -r "$XDG_CONFIG_HOME/dash/shrc" ]]; then ENV="$XDG_CONFIG_HOME/dash/shrc" export ENV fi ;; "mksh") if [[ -r "$XDG_CONFIG_HOME/mksh/mkshrc" ]]; then ENV="$XDG_CONFIG_HOME/mksh/mkshrc" export ENV fi ;; esac } # _preexec_set_title {{{2 # установка заголовка сессии в имя команды _preexec_set_title() { local cmd_type cmd_type="$(type -t "$_preexec_cur_cmd")" case $cmd_type in "") # неверная команда return ;; "function") # если служебная, ничего не делаем [[ $_preexec_cur_cmd == _* ]] && return printf '\e]0;%s\007' "${_preexec_cur_cmd@U}" >&2 ;; "keyword") # заголовок сессии устанавливаем в BASH printf '\e]0;%s\007' "BASH" >&2 ;; "file"|"builtin") printf '\e]0;%s\007' "${_preexec_cur_cmd@U}" >&2 ;; "alias") printf '\e]0;%s\007' "${BASH_ALIASES["$_preexec_cur_cmd"]%% *}" >&2 ;; *) ;; esac } # _ps1_set_timer_start {{{2 # старт таймера плюс восстановление $_; должна быть последней в trap DEBUG # _ps1_set_timer_start "$_preexec_lastarg" _ps1_set_timer_start() { [[ -z "$_ps1_timer_start" && $_preexec_cur_cmd != "_bind"* ]] && _ps1_timer_start="${EPOCHREALTIME//[.,]}" } # _logout_actions {{{2 # установка заголовка сессии при выходе, если оболочка была вызвана из другой программы _logout_actions() { if ((SHLVL > 1)); then read -r _preexec_cur_cmd < /proc/$PPID/comm _preexec_set_title fi } # }}}2 # }}}1 # ПРИВЯЗКИ КЛАВИШ {{{1 [[ -r "$XDG_CONFIG_HOME/bash/bash_bind" ]] && source "$XDG_CONFIG_HOME/bash/bash_bind" # }}}1 # ПСЕВДОНИМЫ И ФУНКЦИИ {{{1 [[ -r "$XDG_CONFIG_HOME/bash/bash_aliases" ]] && source "$XDG_CONFIG_HOME/bash/bash_aliases" # }}}1 # ЦВЕТОВАЯ СХЕМА {{{1 # shellcheck disable=SC1003 # если андроид, но не термукс if _andr && [[ ! -r /data/data/com.termux ]]; then _set_colorscheme() { local -a color local -i idx color[0]="000000" color[1]="d54e53" color[2]="b9ca4a" color[3]="e7c547" color[4]="7aa6da" color[5]="c397d8" color[6]="70c0b1" color[7]="eaeaea" color[8]="2a2a2a" color[9]="d54e53" color[10]="b9ca4a" color[11]="e7c547" color[12]="7aa6da" color[13]="c397d8" color[14]="70c0b1" color[15]="eaeaea" color[173]="e78c45" color[59]="4d5057" color[238]="424242" color[102]="969896" for idx in "${!color[@]}"; do printf '\e]4;%d;rgb:%s/%s/%s\e\\' "$idx" "${color[idx]:0:2}" "${color[idx]:2:2}" "${color[idx]:4:2}" done } _set_colorscheme unset -f _set_colorscheme fi # }}}1 # ОБРАБОТЧИКИ {{{1 # выход из дочерней оболочки {{{2 trap '_logout_actions' EXIT # preexec {{{2 # должна быть последней командой в .bashrc! trap ' _preexec_save_lastarg "$_" _preexec_set_cmd _preexec_set_env _preexec_set_title _ps1_set_timer_start "$_preexec_lastarg" ' DEBUG # }}}2 # }}}1 # vim: ft=bash fdm=marker