<file.txt
. So you can call less
by typing a lot less characters: just set READNULLCMD=less
.[
you get []
with a cursor in the middle. In shells, this is also possible (even in bash): you just need to use something like binkey -s "[" $'\Cv[]\C-b'
: the equivalent of this command may well be placed in .inputrc. A more versatile solution for zsh is to use ZLE widgets: insert-double-brackets() { LBUFFER="${LBUFFER}[[ " RBUFFER=" ]]${RBUFFER}" } zle -N insert-double-brackets bindkey ',H' insert-double-brackets
Here, the LBUFFER variable contains the entire command line up to the cursor, and the RBUFFER variable contains the whole after. The second command creates a widget, the third assigns it to the combination ,H
: thus input ,H
turns into [[ ]]
with the cursor in the middle.alias hp='hg push'
. Alias ​​in zsh have two additional features: suffix alias, which allow you to automatically open files without entering a program (example: alias -s txt=vim
turns the foo.txt
command into vim foo.txt
) and global ones. I have never used the first ones, but I find the second ones quite useful.if
, command delimiters. alias -g NN='&>/dev/null' alias -g L='|less' alias -g G='|grep'
In this example, three alias are defined: one silences the command, another uses less to show the output of the command, the third filters input. Example use: write hg cat -r default file.csv G 42 L
equivalent to hg cat -r default file.csv | grep 42 | less
hg cat -r default file.csv | grep 42 | less
hg cat -r default file.csv | grep 42 | less
, but much shorter. To submit the G
command to the input, it is literally necessary to use shielding: \G
or 'G'
. Note that \G
and 'G'
also form words, and they can also have alias: alias -g "'G'=|grep"
, but I hope you are sane enough to not use this fact.case
script, where there was also a condition of the form L)
, and it did not work because of its transformation into a completely different condition. Therefore, global alias should be determined most recently, after you have already downloaded all the add-ons. To load add-ons after detection, disable the ALIASES
setting: use something like source() { setopt localoptions setopt noaliases builtin source "${@[@]}" } .() { setopt localoptions setopt noaliases builtin . "${@[@]}" }
And so for each option of loading add-ons (besides source
and .
There is also at least autoload
, about the effectiveness of just such functions for which I’m not sure what I’ve done). Global alias, however, are dangerous only in an interactive session, scripts with #!/bin/zsh
will not be affected.cat /bin/test
(more precisely, cat any-binary-file
), you can get various strange effects: for example, replacing part of the characters entered further with symbols for drawing graphics. Most effects are eliminated by writing blindly echo $'\ec'
, but this is the thing that I would like to automate. The hook precmd
will help us in this, allowing you to run your function right before the shell is displayed. The problems that I sometimes see, if I accidentally output a binary file to the terminal, my editor (Vim) crashes, or I just run wine (for some reason it switches the keyboard transmit mode and does not return): graphic symbols instead of normal, alternate screen becomes main (= there is no scrollback (input history)), the arrows stop working (the keyboard transmit is marked here), the cursor is not displayed. To solve them, the following function was created: _echoti() { emulate -L zsh (( ${+terminfo[$1]} )) && echoti $1 } term_reset() { emulate -L zsh [[ -n $TTY ]] && (( $+terminfo )) && { _echoti rmacs # _echoti sgr0 # _echoti cnorm # _echoti smkx # «keyboard transmit mode» echo -n $'\e[?47l' # alternate screen # See https://github.com/fish-shell/fish-shell/issues/2139 for smkx } } zmodload zsh/terminfo && precmd_functions+=( term_reset ) ttyctl -f
. After its introduction to type echo $'\ec'
I almost no longer have to.ttyctl -f
: this built-in zsh feature blocks some changes to the terminal settings: those settings that are set using stty
, and not those that can be set using special sequences (escape sequences).hg mv
instead of just moving around the mv
type. Secondly, you can use an “intuitive” version like noglob zmv -W *.c *.cpp
(to get rid of noglob
, use alias
; in the following examples, noglob
implied). Zmv does not use regular expressions for work, but expressions more suitable for the glob task. You can also use virtually any expression as the second argument: zmv -w test_*.c 'test/${1/_foo/_bar}'
will turn test_foo_1.c
into test_bar_1.c
. Here, parameters like $N
provide access to the “capturing groups” analog from regular expressions, and -w
turns test_*.c
into test_(*).c
-f
: ignore target file. Those. if the test.cpp
file exists, then the zmv -W *.c *.cpp
command will refuse to move any files if test.c
is among them. -f
will force zmv to do this, but, however, will not pass the -f
argument to mv
.-i
: clarify the need before each move. For an affirmative answer, you need to press y
or Y
, for refusal you need to press something else. Note: you only need to press y
or Y
You do not need to press the input, it will be perceived as a failure for the next file.-n
: print all commands that zmv will execute without actually executing.-Q
: enable glob qualifiers. Due to the fact that glob qualifier is easily confused with the capturing group, they are disabled by default. Glob qualifier is a part of glob that specifies the result: there are qualifiers for determining the sort order, including some settings for one glob, and also the filters that are most useful in these circumstances, such as “reveal only symbolic links”.-s
: pass additional argument -s
to the command. Used in conjunction with -L
, or equivalent using zln
instead of zmv
.-v
: print executable commands as they are executed.-o
arg : specify additional arguments for the command. So, to pass the mv
argument --force
you need to use zmv -o--fore
. Can only be used once.-p
prog : use this program instead of mv. The command must understand --
: it will be executed as prog -- source target
.-P
prog : similar to the previous argument, but for commands that do not understand --
. The program will be called as prog source target
.-w
: automatically add capturing groups for all wildcards, described above.-W
: same as the previous argument, but using the $N
parameters created for capturing groups automatically occurs for wildcards in the right argument.-C
, -L
and -M
: similar to -pcp
, -pln
and -pmv
respectively: allows you to use copy, create symbolic links or move regardless of the name of the function (by default there are two additional functions that use the same code as zmv : zcp and zln). aplayer() { emulate -L zsh setopt extendedglob setopt nullglob local -a args args=() local -A mediadirs mediadirs=() for arg in $@ ; do if [[ ${arg[0]} == '-' ]] ; then continue fi if test -f $arg ; then mediadirs[${arg:A:h}]=1 fi done local d local -i found=0 for d in ${(k)mediadirs} ; do local tail=$d:t test -d ~/.fonts/aplayer/${tail}-1 && continue local f for f in $d/**/(#i)font* ; do if test -d $f ; then (( found++ )) ln -s $f ~/.fonts/aplayer/${tail}-${found} elif [[ $f == (#i)*.rar ]] || [[ $f == (#i)*.zip ]] ; then (( found++ )) mkdir ~/.fonts/aplayer/${tail}-${found} pushd -q ~/.fonts/aplayer/${tail}-${found} 7z x $f popd -q fi done done if (( found )) ; then fc-cache -v ~/.fonts fi local -aT subpaths SUBPATHS local -A SUBPATHS_MAP SUBPATHS=( ${(k)^mediadirs}/(#i)*(sub|)*{,/**/*}(/) ) for sp in $SUBPATHS ; do SUBPATHS_MAP[$sp]=1 done local -a subarr for d in ${(k)mediadirs} ; do for subd in $d/**/ ; do if ! test -z $SUBPATHS_MAP[$d] ; then continue fi subarr=( $subd/*.(ass|ssa|srt) ) if (( $#subarr )) ; then SUBPATHS_MAP[$subd]=1 SUBPATHS+=( $subd ) fi done done if (( ${#SUBPATHS} )) ; then args+=( --sub-paths $subpaths ) fi mpv $args $@ &>/dev/tty }
Having zsh things like associative arrays helps a lot when creating such functions.font
at the beginning of the name), setopt nullglob
allows you not to clue about their absence (by default, the absence would cause an error). Using setopt extendedglob
together with (#i)
allows you not to worry about the register: (#i)
allows fonts to be located both in the FONTS
directory and in Fonts
. After finding and installing fonts in ~/.fonts
indexes are updated using fc-cache
: otherwise, even fonts copied to the correct directory will not be used. ${(k)ASSOCIATIVE_ARRAY}
turns an associative array into a simple array of keys.(/)
at the end are used, limiting glob to directories only (for example, glob qualifier). ${^array}
used to make array=( abc ); echo ${^array}*
array=( abc ); echo ${^array}*
was equivalent to echo {a,b,c}*
.ass
, ssa
or srt
extension.subpaths
variable, but its value is used as the argument --sub-paths
. The fact is that zsh noted a rather frequent pattern when an array of values ​​(usually directories) is a simple string, where different values ​​are separated from each other by a separator (usually a colon): an example of such an “array” is PATH
. However, it would be convenient for programmers to work with such arrays exactly as with arrays, therefore, “linked” variables were created, where one of the array variables (example: path
) and another string with the specified (default colon) separator (example: PATH
) , and the change of one of the variables is automatically reflected in the other. In this way, the SUBPATHS
array was associated with the string subpaths
.alias mycmd='noglob mycmd'
and mycmd *.foo
will become equivalent to mycmd '*.foo'
. But what if you want to create a team, at the input of which you are going to file $VAR
literally and do not want to write '$VAR'
? Here I will give an example of code that records zpy import zsh; print(zsh.getvalue("PATH"))
zpy import zsh; print(zsh.getvalue("PATH"))
equivalent to zpython 'import zsh; print(zsh.getvalue("PATH"))'
zpython 'import zsh; print(zsh.getvalue("PATH"))'
; of course, only in interactive mode: zshaddhistory() { emulate -L zsh if (( ${+_HISTLINE} && ${#_HISTLINE} )) ; then print -sr -- "${_HISTLINE}" unset _HISTLINE elif (( ${#1} )) ; then print -sr -- "${1%%$'\n'}" fi fc -p } accept-line() { emulate -L zsh if [[ ${BUFFER[1,4]} == "zpy " ]] ; then _HISTLINE=$BUFFER BUFFER="zpython ${(qqq)BUFFER[5,-1]}" fi zle .accept-line } zle -N accept-line
The main part of the function: when you call the accept-line widget (it is called when you press enter) it determines whether the line starts with zpy
and, if so, the line is replaced with zpython …
, where …
is the screened part of the line after zpy
and space. The zshaddhistory
function zshaddhistory
used to ensure that the source line is in history, not its replacement.*
). But besides simple text files in the directory there are many binary files like *.o
(object) files that you do not want to open. To do this, instead of just the asterisks, you can write several templates corresponding to the necessary files. Or use an exception pattern ( *~*.o
, requires setopt extendedglob
). But with a relatively simple trick, you can automate it: filterglob () { local -r exclude_pat="$2" shift local -r cmd="$1" shift local -a args args=( "${@[@]}" ) local -a new_args local -i expandedglobs=0 local first_unexpanded_glob= for ((I=1; I<=$#args; I++ )) do if [[ $args[I] != ${${args[I]}/[*?]} ]] then local initial_arg=${args[I]} args[I]+="~$exclude_pat(N)" new_args=( $~args[I] ) if (( $#new_args )) ; then expandedglobs=1 else if [[ $options[cshnullglob] == off && $options[nullglob] == off ]] ; then if [[ $options[nomatch] == on ]] ; then : ${~${args[I]%\(N\)}} # Will error out. else new_args=( "$initial_arg" ) fi fi if [[ -z $first_unexpanded_glob ]] ; then first_unexpanded_glob=${args[I]%\(N\)} readonly first_unexpanded_glob fi fi args[I,I]=( "${new_args[@]}" ) (( I += $#new_args - 1 )) fi done if [[ $options[cshnullglob] == on && $options[nullglob] == off ]] ; then if (( !expandedglob )) ; then : $~first_unexpanded_glob # Will error out. fi fi "$cmd" "${args[@]}" } alias vim='noglob filterglob "*.o" vim'
This defines alias, which prohibits zsh (noglob) itself, but uses the function that opens the templates (filterglob) to launch vim itself. But it doesn’t just reveal them, but also complements the template with the exception so that vim *
will work as vim *~*.o
.${~var}
causes zsh to use a pattern expansion as applied to the value of the var
variable and substitutes the result of the pattern expansion instead of the variable itself. array[idx1,idx2]=( $new_array )
removes part of the array from idx1
to idx2
inclusive, inserting the values ​​of the array new_array
in place of the deleted elements. The size of the array
can change. Constructs of the form : $~var
with the comment “Will error out” are needed in order for zsh to show the expected error. At the same time, the function is completed. There are no particular reasons to use this option instead of echo … >&2
, although my like should support catching an error using always
(which you are unlikely to use in an interactive session).Source: https://habr.com/ru/post/272581/
All Articles