zshの人命救助のコツ

Zshは、優れた機能を備えた最高のコマンドシェルの1つです。 ただし、多数の可能性があるため、それらの一部が注意を無視したり、日常のタスクを解決するためのアプリケーションの可能性が明らかでないことは驚くことではありません。 この記事では、zshの「組み込み」機能のいくつかと、生活を楽にする複雑なコードの例を取り上げます。
内容
1. READNULLCMDを使用する

2.開閉ブラケットを挿入します

3.グローバルエイリアス

4.端末設定を返す

5. zmv関数

6.字幕が自動的に見つかった状態でmpvを起動する

7.引数の自動スクリーニングを使用したコマンドの作成

8.グローブからの自動ファイル除外


1. READNULLCMDを使用する



READNULLCMD変数は、コマンドを入力せずにstdinリダイレクトが使用される場合に呼び出されるコマンドを定義します: <file.txt



。 この方法では、はるかに少ない文字を入力することでless



を呼び出すことができます。単にREADNULLCMD=less



設定してREADNULLCMD=less







2.開閉ブラケットを挿入します



Vimのようなエディターでは、アドオンが入力されたときに自動的に括弧を閉じるためによく使用されます。 つまり [



を入力すると、中央にカーソルを置いて[]



を取得します。 シェルでは、これも可能です(bashでも): binkey -s "[" $'\Cv[]\C-b'



ようなものを使用する必要があります。このコマンドに相当するものは.inputrcに配置することもできます。 zshのより普遍的なソリューションには、ZLEウィジェットの使用が含まれます。
 insert-double-brackets() { LBUFFER="${LBUFFER}[[ " RBUFFER=" ]]${RBUFFER}" } zle -N insert-double-brackets bindkey ',H' insert-double-brackets
      
      



ここで、LBUFFER変数にはカーソルの前のコマンドライン全体が含まれ、RBUFFER変数には後の全体が含まれます。 2番目のコマンドはウィジェットを作成し、3番目はウィジェットを組み合わせ,H



割り当てます。したがって、input ,H



はカーソルが中央にある[[ ]]



変わります。



3.グローバルエイリアス



おそらく、シェルでエイリアスが何であるかを知っているでしょうし、おそらくalias hp='hg push'



ようなものを使用したでしょう。 zshのエイリアスには2つの追加機能があります:いわゆる サフィックスエイリアス。プログラムを入力せずにファイルを自動的に開くことができます(例: alias -s txt=vim



foo.txt



vim foo.txt



変換します)。 私は前者を使用したことがなく、後者は非常に便利だと感じました。



グローバルエイリアスは、独立した単語を独自の意味に置き換えるために使用されます。 接尾辞や通常のエイリアスとは異なり、置換された単語はコマンドの位置にある必要はありません(つまり、コマンドラインの最初の単語、またはコマンドセパレータの後の最初の単語)。 エイリアスはメインパーサーが機能する前に処理されるため、グローバルエイリアスには、リダイレクト、 if



、コマンドセパレーターなど、すべてを含めることができます。



私の観点からは、さまざまなタイプのリダイレクトが最も便利です。
 alias -g NN='&>/dev/null' alias -g L='|less' alias -g G='|grep'
      
      



この例では、3つのエイリアスが定義されています。1つはコマンドについてサイレントで、もう1つはlessを使用してコマンドの出力を表示し、3つ目は入力をフィルターします。 使用例: hg cat -r default file.csv G 42 L



書くのhg cat -r default file.csv G 42 L



hg cat -r default file.csv | grep 42 | less



hg cat -r default file.csv G 42 L



同等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



が、はるかに短い。 G



コマンドを入力に送信するには、エスケープを使用する必要があります: \G



または'G'



\G



'G'



も単語を形成し、それらにalias -g "'G'=|grep"



が存在する可能性があることに注意してください: alias -g "'G'=|grep"



、これを使用しないように気をつけてください事実。



その便利さにもかかわらず、zshのいくつかの機能により、グローバルエイリアスはzshアドオンを台無しにする可能性があるため、非常に危険です。 ある例では、 L)



という形式の条件もあるスクリプトを見ましたが、まったく異なる条件への変換のために機能しませんでした。 したがって、グローバルエイリアスは、すべてのアドオンを既にダウンロードした後、最新のものによって決定される必要があります。 定義後にアドオンをダウンロードするには、 ALIASES



設定を無効にALIASES



ます。次のようなものを使用します
 source() { setopt localoptions setopt noaliases builtin source "${@[@]}" } .() { setopt localoptions setopt noaliases builtin . "${@[@]}" }
      
      



したがって、アドオンをダウンロードするための各オプションについては( source



.



に加えて、少なくともそのような機能の有効性についてはautoload



があります。 ただし、グローバルエイリアスは対話型セッションでのみ危険であり、 #!/bin/zsh



を使用したスクリプトは影響を受けません。



4.端末設定を返す



cat /bin/test



(より正確にはcat any-binary-file



)を作成すると、さまざまな奇妙な効果が得られることは誰にとっても秘密ではありません。たとえば、入力した文字の一部をグラフィックス描画用の文字に置き換えます。 ほとんどの効果は盲目的にecho $'\ec'



書くことで除去されますが、これは自動化したいものです。 フックprecmd



はこれに役立ち、シェルが表示される直前に関数を実行できます。 ターミナルに誤ってバイナリファイルを出力したり、エディター(Vim)がクラッシュしたり、ワインを起動した場合に何らかの問題が発生する(何らかの理由で入力モード(キーボード送信モード)が切り替わり、返されない):グラフィック文字通常、代替画面がメイン画面になり(=スクロールバック(入力履歴)はありません)、矢印が正常に機能しなくなり(キーボード送信がここにマークされました)、カーソルは表示されません。 それらを解決するために、次の関数が作成されました。
 _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
      
      



。 導入後、 echo $'\ec'



と入力する必要はほとんどありません。



ttyctl -f



にも注意してください。この組み込みのzsh機能は、端末設定への変更をブロックします。特別なシーケンス(エスケープシーケンス)を使用して設定できる設定ではなく、 stty



を使用して設定されます。



5. zmv関数



複数のファイルの名前を自動的に変更するために、renameコマンドに遭遇した可能性があります。 perlで記述され、 Cで記述されたバリアントの 2つのコピーにも存在します Zshには似たような機能がありますが、より強力です。まず、この方法でファイルをコピーするか、単にmv



ように動き回る代わりにhg mv



を実行できます。 第二に、 noglob zmv -W *.c *.cpp



ような「直感的な」オプションを使用できます( noglob



を取り除くには、 alias



使用します;さらなる例でnoglob



noglob



暗示されます)。 Zmvは正規表現を使用しませんが、globタスクにより適した表現を使用します。 2番目の引数として事実上任意の式を使用することもできます: zmv -w test_*.c 'test/${1/_foo/_bar}'



test_foo_1.c



test_bar_1.c



ます。 ここで、形式$N



パラメーターは、正規表現から「キャプチャーグループ」の類似物へのアクセスを提供し、 -w



test_*.c



test_(*).c



変換しtest_(*).c







すべての引数:

6.字幕を自動的に見つけてmpvを開始



トレントから外部字幕付きのテレビ番組をダウンロードしたことがある場合、それらをアップロードする各人が字幕の場所について自分の意見を持っていることに間違いなく気付くでしょう。 主なオプションは2つあります。自分のディレクトリとビデオのすぐ隣にありますが、「独自のディレクトリ」の下では、ディレクトリの名前はすべて隠すことができます。また、さまざまな深さの添付ファイルもあります。「subs {sub group}」、「subtitles {sub group}」 、「Subs / {sub group}」、さらには「{sub group}」です。 追加の問題は、字幕とともに非標準のフォントを使用し、字幕とともに配布することです。



字幕を選択して正しいフォントを使用するには、さまざまな方法を使用できます。 ほとんどすべての場合に適切なジョブを自動的に実行する関数を作成することを好みました。
 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 }
      
      



zshに連想配列のようなものがあることは、そのような関数を作成するときに非常に役立ちます。



ここで、関数の最初の部分はすべての引数を調べて、mediadirsの連想配列にある作品のディレクトリを詰まらせます。 重複を避けるためだけに結合されます。



次に、この機能は、作品を含むすべてのカタログを調べて、それらのフォントまたはアーカイブ内またはサブディレクトリ内にあるサブディレクトリ内のフォントを検索します。 フォントは特性名(名前の先頭にあるfont



の存在)によって識別されますsetopt nullglob



使用すると、 font



が存在しないことを心配する必要がありません(デフォルトでは、存在しないとエラーが発生します)。 setopt extendedglob



(#i)



組み合わせて使用​​すると、 setopt extendedglob



setopt extendedglob



せずに済みます。 (#i)



フォントをFONTS



ディレクトリとFonts



両方に配置できます。 ~/.fonts



フォントを見つけてインストールした後~/.fonts



インデックスはfc-cache



を使用して更新されます。それ以外の場合、正しいディレクトリにコピーされたフォントも使用されません。 ${(k)ASSOCIATIVE_ARRAY}



は、連想配列をキーの単純な配列に変換します。



3番目のサイクルでは、字幕付きのディレクトリが検出され、「subs」や「subtitles」などの「単純な」名前を持つ連想配列にたたき込まれます。 繰り返しますが、大文字と小文字を区別せず、最後に個別に(/)



を使用して、globをディレクトリのみに制限します(glob修飾子の例)。 ${^array}



使用して、 array=( abc ); echo ${^array}*



array=( abc ); echo ${^array}*



echo {a,b,c}*



と同等でした。



最後のサイクルでは、非標準の方法と呼ばれる字幕付きのディレクトリを見つけます。 字幕ディレクトリは、拡張子がass



srt



またはsrt



ファイルを少なくとも1つ含む(ビデオのあるディレクトリに関連する)任意のサブディレクトリsrt







かなり奇妙なコードがあることに注意してください:誰もsubpaths



変数に触れていないようですが、その値は引数--sub-paths



として使用され--sub-paths



。 実際、値の配列(通常はディレクトリ)が異なる値が区切り文字(通常はコロン)で区切られた単純な文字列である場合、zshはかなり頻繁にパターンを記録しますPATH



変数はそのような「配列」の例です。 ただし、プログラマは配列と同じようにこのような配列を操作するのが便利なので、変数の1つが配列(例: path



)であり、指定された(デフォルトのコロン)セパレータ(例: PATH



) 、一方の変数の変更が他方に自動的に反映されます。 これは、 SUBPATHS



配列がsubpaths



文字列に関連付けられたsubpaths



です。



7.引数の自動スクリーニングを使用したコマンドの作成



一部のコマンドの引数は決してファイルではありません。 ただし、この事実はzshがテンプレートを展開するのを止めません。 通常の場合、 alias mycmd='noglob mycmd'



を書くだけで、 mycmd *.foo



mycmd '*.foo'



と同等になります。 しかし、入力を文字通り$VAR



に送信し、 '$VAR'



を書きたくないチームを作成する場合はどうでしょうか。 ここで、 zpy import zsh; print(zsh.getvalue("PATH"))



を記録するコードの例をzpy import zsh; print(zsh.getvalue("PATH"))



zpy import zsh; print(zsh.getvalue("PATH"))



zpython 'import zsh; print(zsh.getvalue("PATH"))'



と同等zpython 'import zsh; print(zsh.getvalue("PATH"))'



zpython 'import zsh; print(zsh.getvalue("PATH"))'



; もちろん、対話モードでのみ:
 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
      
      



関数の主要部分:ウィジェットaccept-lineを呼び出すとき(Enterを押すと呼び出されます)、行がzpy



で始まるかどうかが判別され、そうであれば、行はzpython …



に置き換えられzpython …



。ここで



は、 zpy



およびspaceの後の行のエスケープ部分です。 zshaddhistory



関数zshaddhistory



使用して、ソース文字列zshaddhistory



置換ではなく履歴にzshaddhistory



されるようにします。

このようにして、任意のカスタム構文をzshに追加できます。



8.グローブからの自動ファイル除外



Vimエディターがあり、それを使用してディレクトリーからすべてのファイルを開くことを想像してください( *



テンプレートを使用)。 しかし、ディレクトリ内の単純なテキストファイルに加えて、 *.o



(オブジェクト)ファイルのような多くのバイナリを開きます。 これを行うには、単なるアスタリスクの代わりに、必要なファイルに対応するいくつかのテンプレートを作成できます。 または、例外パターンを使用します( *~*.o



setopt extendedglob



が必要setopt extendedglob



)。 しかし、比較的簡単なトリックで、これは自動化できます:
 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'
      
      



これはエイリアスが定義されている場所で、zsh(noglob)がテンプレート自体を展開するのを防ぎますが、関数を使用してテンプレート自体(filterglob)を展開するvimを開きます。 しかし、それは単にそれらを開くだけでなく、 vim *



vim *~*.o



ように機能するようにパターン例外を付けてそれを完成させます。

この関数は、zshの次の機能を使用します。 ${~var}



〜var ${~var}



は、zshにvar



変数の値に関してテンプレート展開を使用させ、変数自体の代わりにテンプレート展開の結果を置き換えます。 array[idx1,idx2]=( $new_array )



は、 idx1



からidx2



までの配列の一部を削除し、削除された要素の代わりにnew_array



配列の値を挿入します。 array



のサイズは変更される場合があります。 次の形式の構造: $~var



とコメント「Will error out」は、zshが期待されるエラーを表示するために必要です。 この場合、関数の実行は完了します。 echo … >&2



代わりにこのオプションを使用する特別な理由はありませんが、私の場合はalways



(インタラクティブセッションではほとんど使用しない)エラーalways



をサポートしているようです。



All Articles