---
category: ru
type: paper
hastr: true
layout: paper
tags: zshrc, настройка, linux
title: О zshrc
short: about-zshrc
description: Это моя первая статья в блоге (я думаю, мне нужно что-нибудь для тестов =)). Существует множество похожих статей и, я думаю, не буду отличаться от большинства. Я просто хочу показать мой <code>.zshrc</code> и объяснить, что в нем есть и зачем оно нужно. Также, любые комментарии или дополнения приветствуются. <a href="http://archlinux.org.ru/forum/topic/12752/" title="Тема на форуме">Оригинал</a> статьи.
---
<h2><a href="#prepare" class="anchor" name="prepare"><span class="octicon octicon-link"></span></a>Подготовка</h2>
<p>Сначала установите необходимый минимум:</p>

{% highlight bash %}
pacman -Sy pkgfile zsh zsh-completions zsh-syntax-highlighting
{% endhighlight %}

<p><a href="https://www.archlinux.org/packages/pkgfile/" title="Пакет Archlinux">pkgfile</a> очень полезная утилита. Данная команда также установит шелл, дополнения к нему и подсветку синтаксиса.</p>

<h2><a href="#configuration" class="anchor" name="configuration"><span class="octicon octicon-link"></span></a>Настройка шелла</h2>
<p>Все доступные опции приведены <a href="http://zsh.sourceforge.net/Doc/Release/Options.html" title="Документация zsh">здесь</a>.</p>

<p>Указываем файл с историей, число команд хранящихся в кэше текущего сеанса и число команд, хранящихся в файле:</p>

{% highlight bash %}
# history
HISTFILE=~/.zsh_history
HISTSIZE=500000
SAVEHIST=500000
{% endhighlight %}

<p>Я не могу запомнить все комбинации <code>Ctrl+</code>, поэтому я назначаю клавиши на их стандартное использование:</p>

{% highlight bash %}
# bindkeys
bindkey '^[[A'  up-line-or-search        # up arrow for back-history-search
bindkey '^[[B'  down-line-or-search      # down arrow for fwd-history-search
bindkey '\e[1~' beginning-of-line        # home
bindkey '\e[2~' overwrite-mode           # insert
bindkey '\e[3~' delete-char              # del
bindkey '\e[4~' end-of-line              # end
bindkey '\e[5~' up-line-or-history       # page-up
bindkey '\e[6~' down-line-or-history     # page-down
{% endhighlight %}

<p>Но здесь важно, что стрелки <code>вверх</code>/<code>вниз</code> служат для навигации по истории с учетом <b>уже введенной части</b> команды. А <code>PgUp</code>/<code>PgDown</code> <b>проигнорируют</b> уже введенную часть команды.</p>

<p>Автодополнение команд:</p>

{% highlight bash %}
# autocomplete
autoload -U compinit
compinit
zstyle ':completion:*' insert-tab false
zstyle ':completion:*' max-errors 2
{% endhighlight %}

<p>Подключается полное автодополнение команд. <code>insert-tab false</code> включит автодополнение для <b>невведенной</b> команды (не знаю, зачем). <code>max-errors</code> устанавливает максимальное число опечаток, которые могут быть исправлены.</p>

<p>Приглашение:</p>

{% highlight bash %}
# promptinit
autoload -U promptinit
promptinit
{% endhighlight %}

<p>Включим цвета:</p>

{% highlight bash %}
# colors
autoload -U colors
colors
{% endhighlight %}

<p>Различные опции.</p>
<p>Смена директории без ввода <code>cd</code>:</p>

{% highlight bash %}
# autocd
setopt autocd
{% endhighlight %}

<p>Корректировка опечаток (и шаблон вопроса):</p>

{% highlight bash %}
# correct
setopt CORRECT_ALL
SPROMPT="Correct '%R' to '%r' ? ([Y]es/[N]o/[E]dit/[A]bort) "
{% endhighlight %}

<p>Отключаем е#$%ую пищалку:</p>

{% highlight bash %}
# disable beeps
unsetopt beep
{% endhighlight %}

<p>Включаем калькулятор:</p>

{% highlight bash %}
# calc
autoload zcalc
{% endhighlight %}

<p>Дополнение истории (<b>а не перезапись</b> файла):</p>

{% highlight bash %}
# append history
setopt APPEND_HISTORY
{% endhighlight %}

<p>Не сохранять дубликаты в историю:</p>

{% highlight bash %}
# ignore dups in history
setopt HIST_IGNORE_ALL_DUPS
{% endhighlight %}

<p>...и дополнительные пробелы:</p>

{% highlight bash %}
# ignore spaces in history
setopt HIST_IGNORE_SPACE
{% endhighlight %}

<p>...и пустые линии тоже:</p>

{% highlight bash %}
# reduce blanks in history
setopt HIST_REDUCE_BLANKS
{% endhighlight %}

<p>Включаем <code>pkgfile</code>:</p>

{% highlight bash %}
# pkgfile
source /usr/share/doc/pkgfile/command-not-found.zsh
{% endhighlight %}

<h2><a href="#highlighting" class="anchor" name="highlighting"><span class="octicon octicon-link"></span></a>Подсветка синтаксиса</h2>

{% highlight bash %}
# highlighting
source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
ZSH_HIGHLIGHT_HIGHLIGHTERS=(main brackets pattern)
# brackets
ZSH_HIGHLIGHT_STYLES[bracket-level-1]='fg=blue,bold'
ZSH_HIGHLIGHT_STYLES[bracket-level-2]='fg=red,bold'
ZSH_HIGHLIGHT_STYLES[bracket-level-3]='fg=yellow,bold'
ZSH_HIGHLIGHT_STYLES[bracket-level-4]='fg=magenta,bold'
# cursor
#ZSH_HIGHLIGHT_STYLES[cursor]='bg=blue'
# main
# default
ZSH_HIGHLIGHT_STYLES[default]='none'                                 # стандартный цвет
# unknown
ZSH_HIGHLIGHT_STYLES[unknown-token]='fg=red'                         # неизвестная команда
# command
ZSH_HIGHLIGHT_STYLES[reserved-word]='fg=magenta,bold'                # зарезервированное слово
ZSH_HIGHLIGHT_STYLES[alias]='fg=yellow,bold'                         # алиас
ZSH_HIGHLIGHT_STYLES[builtin]='fg=green,bold'                        # built-in функция (например, echo)
ZSH_HIGHLIGHT_STYLES[function]='fg=green,bold'                       # функция, определенная в шелле
ZSH_HIGHLIGHT_STYLES[command]='fg=green'                             # обычная команда
ZSH_HIGHLIGHT_STYLES[precommand]='fg=blue,bold'                      # пре-команда (например, sudo в sudo cp ...)
ZSH_HIGHLIGHT_STYLES[commandseparator]='fg=yellow'                   # разделитель команд, && || ;
ZSH_HIGHLIGHT_STYLES[hashed-command]='fg=green'                      # команда, найденная в путях (hashed)
ZSH_HIGHLIGHT_STYLES[single-hyphen-option]='fg=blue,bold'            # флаги типа -*
ZSH_HIGHLIGHT_STYLES[double-hyphen-option]='fg=blue,bold'            # флаги типа --*
# path
ZSH_HIGHLIGHT_STYLES[path]='fg=cyan,bold'                            # станлартный путь
ZSH_HIGHLIGHT_STYLES[path_prefix]='fg=cyan'                          # префикс пути
ZSH_HIGHLIGHT_STYLES[path_approx]='fg=cyan'                          # примерный путь
# shell
ZSH_HIGHLIGHT_STYLES[globbing]='fg=cyan'                             # шаблон (например, /dev/sda*)
ZSH_HIGHLIGHT_STYLES[history-expansion]='fg=blue'                    # подстановка из истории (команда, начинающаяся с !)
ZSH_HIGHLIGHT_STYLES[assign]='fg=magenta'                            # присвоение
ZSH_HIGHLIGHT_STYLES[dollar-double-quoted-argument]='fg=cyan'        # конструкции типа "$VARIABLE"
ZSH_HIGHLIGHT_STYLES[back-double-quoted-argument]='fg=cyan'          # конструкции типа \"
ZSH_HIGHLIGHT_STYLES[back-quoted-argument]='fg=blue'                 # конструкции типа `command`
# quotes
ZSH_HIGHLIGHT_STYLES[single-quoted-argument]='fg=yellow,underline'   # конструкции типа 'text'
ZSH_HIGHLIGHT_STYLES[double-quoted-argument]='fg=yellow'             # конструкции типа "text"
# pattern
#ZSH_HIGHLIGHT_PATTERNS+=('rm -rf *' 'fg=white,bold,bg=red')
# root
#ZSH_HIGHLIGHT_STYLES[root]='bg=red'
{% endhighlight %}

<p>В первой строке включаем подсветку. Затем включаем основную подсветку, а также подсветку скобок и шаблонов. Шаблоны указываются ниже (<code>rm -rf *</code> в примере). Также может быть включена подсветка команд от <code>root</code> и курсора <code>cursor</code>. Синтаксис настроек понятен, <code>fg</code> цвет шрифта, <code>bg</code> цвет фона.</p>

<h2><a href="#prompt" class="anchor" name="prompt"><span class="octicon octicon-link"></span></a>$PROMPT и $RPROMPT</h2>
<p>Я хочу использовать один файл <code>.zshrc</code> для рута и обычного пользователя:</p>

{% highlight bash %}
# PROMPT && RPROMPT
if [[ $EUID == 0 ]]; then
# [root@host dir]#
  PROMPT="%{$fg_bold[white]%}[%{$reset_color%}\
%{$fg_bold[red]%}%n%{$reset_color%}\
%{$fg_bold[white]%}@%{$reset_color%}\
%{$fg_no_bold[red]%}%m %{$reset_color%}\
%{$fg_bold[yellow]%}%1/%{$reset_color%}\
%{$fg_bold[white]%}]# %{$reset_color%}"
else
# [user@host dir]$
  PROMPT="%{$fg_bold[white]%}[%{$reset_color%}\
%{$fg_bold[green]%}%n%{$reset_color%}\
%{$fg_bold[white]%}@%{$reset_color%}\
%{$fg_no_bold[green]%}%m %{$reset_color%}\
%{$fg_bold[yellow]%}%1/%{$reset_color%}\
%{$fg_bold[white]%}]$ %{$reset_color%}"
fi
{% endhighlight %}

<p><code>fg</code> цвет шрифта, <code>bg</code> цвет фона. <code>_bold</code> и <code>_no_bold</code> регулируют оттенок. Команды должны быть обрамлены в <code>%{ ... %}</code>, чтобы не показывались. Доступные цвета:</p>

{% highlight bash %}
black
red
green
yellow
blue
magenta
cyan
white
{% endhighlight %}

<p>Доступные переменные:</p>

{% highlight bash %}
%n  - имя пользователя
%m  - хостнейм (выставляется только в начале сессии)
%M  - хостнейм
%l  - текущая tty
%?  - код возврата предыдущего приложения
%#  - # для рута и % для обычных пользователей
%T  - время (HH:MM)
%*  - время (HH:MM:SS)
%D  - дата (YY-MM-DD)
%d  - текущая директория
%~  - то же, домашняя директория будет заменена на ~
%1/ - то же, но только последняя директория
{% endhighlight %}

<p>RPROMPT (необходим пакет <code>acpi</code>):</p>

{% highlight bash %}
precmd () {
  # battery charge
  function batcharge {
    bat_perc=`acpi | awk {'print $4;'} | sed -e "s/\s//" -e "s/%.*//"`
    if [[ $bat_perc < 15 ]]; then
      col="%{$fg_bold[red]%}"
    elif [[ $bat_perc < 50 ]]; then
      col="%{$fg_bold[yellow]%}"
    else
      col="%{$fg_bold[green]%}"
    fi
    echo "%{$fg_bold[white]%}["$col$bat_perc"%{$fg_bold[white]%}%%]%{$reset_color%}"
  }
  # last command
  returncode="%(?.%{$fg[green]%}.%{$fg[red]%})%?%{$resetcolor%}"
  RPROMPT="%{$fg_bold[white]%}[%{$reset_color%}\
%{$fg_bold[cyan]%}%T%{$reset_color%}\
%{$fg_bold[white]%}] %{$reset_color%}"\
$(batcharge)\
"%{$fg_bold[white]%}[%{$reset_color%}"\
$returncode\
"%{$fg_bold[white]%}]%{$reset_color%}"
{% endhighlight %}

<p>Мой RPROMPT показывает текущее время, заряд батареи и код возврата последнего приложения. <code>precmd()</code> необходимо для автоматического обновления. Конструкция <code>$(if.true.false)</code> является условным оператором в <code>zsh</code>.</p>

<h2><a href="#aliases" class="anchor" name="aliases"><span class="octicon octicon-link"></span></a>Аллиасы</h2>
<p><b>Копируйте только те аллиасы, которые Вам необходимы.</b> Если какой-либо аллиас использует приложение, которое не установлено, это приведет к сбою загрузки конфигурационного файла.</p>

<p>Полезная (или не очень) функция:</p>

{% highlight bash %}
show_which() {
  OUTPUT=$(which $1 | cut -d " " -f7-)
  echo "Running '$OUTPUT'" 1>&2
}
{% endhighlight %}

<p>Первая группа аллиасов:</p>

{% highlight bash %}
## alias
# цветной grep
alias grep='grep --colour=auto'
# замена top на htop
alias top='show_which top && htop'
# chromium с различными прокси серверами (i2p и tor в наличии)
alias chrommsu='show_which chrommsu && chromium --proxy-server=cache.msu:3128'
alias chromtor='show_which chromtor && chromium --proxy-server="socks://localhost:9050" --incognito'
alias chromi2p='show_which chromi2p && chromium --proxy-server="http=127.0.0.1:4444;https=127.0.0.1:4445" --incognito'
# человеческие df и du
alias df='show_which df && df -k --print-type --human-readable'
alias du='show_which du && du -k --total --human-readable'
# замена less и zless на vimpager
alias less='vimpager'
alias zless='vimpager'
{% endhighlight %}

<p>ls аллиасы (смотри <a href="http://unixhelp.ed.ac.uk/CGI/man-cgi?ls" title="Мануал">man ls</a>):</p>

{% highlight bash %}
alias ls='show_which ls && ls --color=auto --group-directories-first'
alias ll='show_which ll && ls -l --human-readable'
alias lr='show_which lr && ls --recursive'
alias la='show_which la && ll --almost-all'
alias lx='show_which lx && ll -X --ignore-backups'
alias lz='show_which lz && ll -S --reverse'
alias lt='show_which lt && ll -t --reverse'
alias lm='show_which lm && la | more'
{% endhighlight %}

<p>Аллиасы для быстрого просмотра файлов из консоли (просто набери имя файла!):</p>

{% highlight bash %}
# alias -s
alias -s {avi,mpeg,mpg,mov,m2v,mkv}=mpv
alias -s {mp3,flac}=qmmp
alias -s {odt,doc,xls,ppt,docx,xlsx,pptx,csv}=libreoffice
alias -s {pdf}=okular
autoload -U pick-web-browser
alias -s {html,htm}=opera
{% endhighlight %}

<p>"sudo" аллиасы:</p>

{% highlight bash %}
# sudo alias
if [[ $EUID == 0 ]]; then
  alias fat32mnt='show_which fat32mnt && mount -t vfat -o codepage=866,iocharset=utf8,umask=000'
  alias synctime='show_which synctime && { ntpd -qg; hwclock -w; date; }'
else
  alias fat32mnt='show_which fat32mnt && sudo mount -t vfat -o codepage=866,iocharset=utf8,umask=000'
  alias umount='show_which umount && sudo umount'
  alias mount='show_which mount && sudo mount'
  alias netctl='show_which netctl && sudo netctl'
  alias synctime='show_which synctime && { sudo ntpd -qg; sudo hwclock -w; date; }'
  alias wifi-menu='show_which wifi-menu && sudo wifi-menu'
  alias dhcpcd='show_which dhcpcd && sudo dhcpcd'
  alias journalctl='show_which journalctl && sudo journalctl'
  alias systemctl='show_which systemctl && sudo systemctl'
  alias modprobe='show_which modprobe && sudo modprobe'
  alias rmmod='show_which rmmod && sudo rmmod'
  alias staging-i686-build='show_which staging-i686-build && sudo staging-i686-build'
  alias staging-x86_64-build='show_which staging-x86_64-build && sudo staging-x86_64-build'
fi
{% endhighlight %}

<p>Некоторые глобальные аллиасы. Если они включены, команда <code>cat foo g bar</code> будет эквивалентна <code>cat foo | grep bar</code>:</p>

{% highlight bash %}
# global alias
alias -g g="| grep"
alias -g l="| less"
alias -g t="| tail"
alias -g h="| head"
alias -g dn="&> /dev/null &"
{% endhighlight %}

<h2><a href="#functions" class="anchor" name="functions"><span class="octicon octicon-link"></span></a>Функции</h2>
<p>Специальная функция для <code>xrandr</code>:</p>

{% highlight bash %}
# function to contorl xrandr
# EXAMPLE: projctl 1024x768
projctl () {
  if [ $1 ] ; then
    if [ $1 = "-h" ]; then
      echo "Usage:   projctl [ off/resolution ]"
      return
    fi
    if [ $1 = "off" ]; then
      echo "Disable VGA1"
      xrandr --output VGA1 --off --output LVDS1 --mode 1366x768
    else
      echo "Using resolution: $1"
      xrandr --output VGA1 --mode $1 --output LVDS1 --mode $1
    fi
  else
    echo "Using default resolution"
    xrandr --output VGA1 --mode 1366x768 --output LVDS1 --mode 1366x768
  fi
}
{% endhighlight %}

<p>К сожалению, я не могу запомнить флаги <code>tar</code>, поэтому я использую специальные функции:</p>

{% highlight bash %}
# function to extract archives
# EXAMPLE: unpack file
unpack () {
  if [[ -f $1 ]]; then
    case $1 in
      *.tar.bz2)   tar xjfv $1                             ;;
      *.tar.gz)    tar xzfv $1                             ;;
      *.tar.xz)    tar xvJf $1                             ;;
      *.bz2)       bunzip2 $1                              ;;
      *.gz)        gunzip $1                               ;;
      *.rar)       unrar x $1                              ;;
      *.tar)       tar xf $1                               ;;
      *.tbz)       tar xjvf $1                             ;;
      *.tbz2)      tar xjf $1                              ;;
      *.tgz)       tar xzf $1                              ;;
      *.zip)       unzip $1                                ;;
      *.Z)         uncompress $1                           ;;
      *.7z)        7z x $1                                 ;;
      *)           echo "I don't know how to extract '$1'" ;;
    esac
  else
    case $1 in
      *help)       echo "Usage: unpack ARCHIVE_NAME"       ;;
      *)           echo "'$1' is not a valid file"         ;;
    esac
  fi
}
# function to create archives
# EXAMPLE: pack tar file
pack () {
  if [ $1 ]; then
    case $1 in
      tar.bz2)     tar -cjvf $2.tar.bz2 $2                 ;;
      tar.gz)      tar -czvf $2.tar.bz2 $2                 ;;
      tar.xz)      tar -cf - $2 | xz -9 -c - > $2.tar.xz   ;;
      bz2)         bzip $2                                 ;;
      gz)          gzip -c -9 -n $2 > $2.gz                ;;
      tar)         tar cpvf $2.tar  $2                     ;;
      tbz)         tar cjvf $2.tar.bz2 $2                  ;;
      tgz)         tar czvf $2.tar.gz  $2                  ;;
      zip)         zip -r $2.zip $2                        ;;
      7z)          7z a $2.7z $2                           ;;
      *help)       echo "Usage: pack TYPE FILES"           ;;
      *)           echo "'$1' cannot be packed via pack()" ;;
    esac
  else
    echo "'$1' is not a valid file"
  fi
}
{% endhighlight %}

<p>Специальная функция для <code>su</code>:</p>

{% highlight bash %}
su () {
  CHECKSU=0
  for FLAG in $*; do
    [[ $FLAG == "-" ]] && CHECKSU=1
    [[ $FLAG == "-l" ]] && CHECKSU=1
    [[ $FLAG == "--login" ]] && CHECKSU=1
  done
  if [[ $CHECKSU == 0 ]]; then
    echo "Use 'su -', Luke"
    /usr/bin/su - $*
  else
    /usr/bin/su $*
  fi
}
{% endhighlight %}

<p>Функция, которая заменяет оригиналькую команду <code>rm</code>. Если Вы наберете <code>rm</code>, это будет эквивалентно перемещению в корзину, также, Вы можете легко восстановить удаленный файл:</p>

{% highlight bash %}
rm () {
  # error check
  [ $# -eq 0 ] && { echo "Files are not set!"; return 1 }
  echo "$@" | grep -qe '-h\|--help' && { echo "Usage: rm FILE..."; return 0 }
  echo "$@" | grep -q "-" && echo "Warning: this function doesn't support any flags"
  # set trash path
  TRASHDIR="$HOME/.local/share/Trash"
  TRASHFILE="${TRASHDIR}/files"
  TRASHINFO="${TRASHDIR}/info"
  for DIRECTORY in "${TRASHDIR}" "${TRASHFILE}" "${TRASHINFO}"; do
    if [ -e "${DIRECTORY}" ]; then
      [ -d "${DIRECTORY}" ] || { echo "'${DIRECTORY}' is a file"; return 1 }
    else
      mkdir -p -m755 "${DIRECTORY}"
    fi
  done
  # confirm
  CONFIRM=""
  echo -n "You realy want to remove '$@'? [y/n] "; read -k1 CONFIRM; echo
  [[ ! $CONFIRM =~ [yY] ]] && return 1
  # move
  for FILE in "$@"; do
    DESTFILE="$(basename -- "${FILE}")"
    SUFFIX='';
    ITER=0;
    while [ -e "${TRASHFILE}/${DESTFILE}${SUFFIX}" ]; do
      SUFFIX="_${ITER}";
      ITER=$(expr ${ITER} + 1)
    done
    echo "Remove '${FILE}'"
    if [ "$(dirname -- "$(realpath -- "${FILE}")")" == "${TRASHFILE}" ]; then
      /usr/bin/rm -rf -- "${FILE}"
      /usr/bin/rm -rf -- "${TRASHINFO}/${DESTFILE}.trashinfo"
    else
      mv -- "${FILE}" "${TRASHFILE}/${DESTFILE}${SUFFIX}" || return 1
      echo "[Trash Info]\nPath=$(realpath -- "${FILE}")\nDeletionDate=$(date +%Y-%m-%dT%H:%M:%S)" > "${TRASHINFO}/${DESTFILE}${SUFFIX}.trashinfo" || return 1
    fi
  done
}
{% endhighlight %}

<p>Функция для автоматических обновлений путей после установки пакетов:</p>

{% highlight bash %}
pacman () {
  /usr/bin/sudo /usr/bin/pacman $* && echo "$*" | grep -q "S\|R\|U" && rehash
}
yaourt () {
  /usr/bin/yaourt $* && echo "$*" | grep -q "S\|R\|U" && rehash
}
# for testing repo
yatest () {
  /usr/bin/yaourt --config /etc/pactest.conf $* && echo "$*" | grep -q "S\|R\|U" && rehash
}
{% endhighlight %}

<h2><a href="#variables" class="anchor" name="variables"><span class="octicon octicon-link"></span></a>Переменные</h2>
<p>Рекомендуется хранить свои переменные в <code>~/.zshenv</code>. Но я все храню в одном файле.</p>

<p>Пути, маска создаваемых файлов, редактор и пейджер:</p>

{% highlight bash %}
# path
export PATH="$PATH:$HOME/.local/bin"
# umask
umask 022
# editor
export EDITOR="vim"
export PAGER="vimpager"
{% endhighlight %}

<p>Хэши. Если они включены, команда <code>~global</code> будет эквивалентна команде <code>/mnt/global</code>:</p>

{% highlight bash %}
# hash
hash -d global=/mnt/global
hash -d windows=/mnt/windows
hash -d iso=/mnt/iso
hash -d u1=/mnt/usbdev1
hash -d u2=/mnt/usbdev2
{% endhighlight %}

<h2><a href="#screenshot" class="anchor" name="screenshot"><span class="octicon octicon-link"></span></a>Скриншот</h2>
<div class="thumbnails">
  {% assign scrdesc = "Как оно выглядит" %}
  {% assign scrname = "zshrc_demo" %}
  {% include prj_scr.html %}
</div>

<h2><a href="#file" class="anchor" name="file"><span class="octicon octicon-link"></span></a>Файл</h2>
<p><a href="https://raw.github.com/arcan1s/dotfiles/master/zshrc" title="Github" type="text/plain">Мой</a> <code>.zshrc</code>.</p>
