zshで、readでn文字ずつ読み込みますです
zshはread
で入力を受け付けることができますですー>ω<
$ read line hello, world $ echo $line hello, world
このread
にはいくつかのオプションがありますでして、その中の一つに-k
というものがありますです。これは、k文字の入力を受け取るということで、このように使えますです。
$ while read -k 5 five; do echo "-> $five"; done xxxx-> xxxx hello-> hello ^C $
ちょうど5文字入力したところで反応してとても面白いのですー>ω<
しかし、このread -k
には重大な問題がありますです。それは、このままではパイプラインやリダイレクトを使って入力を指定したとしても、標準入力へ入力を受け付けに行ってしまうのです(`・ω・′)
$ cat hello.txt hello, world $ cat hello.txt | while read -k 5 five; do echo "-> $five"; done
諸事情(わたしの場合bf.zsh)で、n文字ずつ文字列を受け取らなくてはいけない場合、これでは大変困ったことになりますです。
()
で囲って別プロセスにしたり、別ファイルに記述したりしても上手くいかなかったので半ば諦めていたのですが……
man zshbuiltins
のread
を眺めていたら、-u
というオプションを見付けたのですー>ω<
どのファイルデスクリプタを入力に使用するかを明示的に指定するオプションなのですが、これに標準入力のファイルデスクリプタである0
を指定したところ、上手くいったのです>ω<
$ cat hello.txt | while read -n0 -k5 five; do echo "-> $five"; done hello , wor ld $
ちなみに、このオプションを指定すると-k
オプションのみの場合と違ってキーを押した瞬間に反応したりはしなくなる様子ですので、注意して欲しいのですー>ω<
(ところで、zshの組み込みコマンドでファイルをopenしてファイルディスクリプタを取得するにはどうしたらいいのです?? 現状は困らないのですけれど)
最後に、この話に教訓を残すなら「迷わずにman
を見ろ」ということです。ありがとうございましたのですー>ω<
Vimで、開いているファイルを再読み込み
filetypeを設定ためのコメントを追加したときなど、ファイルを再読み込みしたいときがありますです
そんなときは、
:e %
で、再読み込みできますですー>ω< %
は現在のバッファのファイル名になっているのです
追記
と、思っていたのですが、実は、
:e
だけでも再読み込みできるみたいなのでしたー
もっと良い方法がありましたら教えていただきたいのですー>ω<
Ubuntu 13.10で、zshのmanページが見れないことの対処法なのですー>ω<
Ubuntu13.10のバグで、zshのmanページが見れなくなっていますです。
$ man zsh zsh というマニュアルはありません
Bug #1242108 “all zsh manpages are missing” : Bugs : “zsh” package : Ubuntu
これはパッケージにzshのmanページを含めていなかったことが原因のようなのですが、ごちゃごちゃしていて未だに解決していないのです(もしかしたら、14.04では直っているかもしれませんが、現時点で上に上げたページの議論がcloseしていないのでどうなんだろうな、って感じです)(′・ω・`)
info zsh
という手もあるのですが、やっぱりLinuxのマニュアルといったらman
なので、やはりman
で見たいものです。なので、今回は仕方ないので、手動でzshのmanをインストールすることにしますです。
$ cd ~/Downloads $ mkdir zsh-doc && cd $_ $ wget http://downloads.sourceforge.net/project/zsh/zsh/5.0.2/zsh-5.0.2.tar.bz2 $ tar -xvf zsh-5.0.2.tar.bz2 $ cd zsh-5.0.2/Doc $ ls -p *.1 zsh.1 zshbuiltins.1 zshcompctl.1 zshcompwid.1 zshexpn.1 zshmodules.1 zshparam.1 zshtcpsys.1 zshzle.1 zshall.1 zshcalsys.1 zshcompsys.1 zshcontrib.1 zshmisc.1 zshoptions.1 zshroadmap.1 zshzftpsys.1
manファイルを見つけたので、これを適当なディレクトリにコピーして、そこを環境変数MANPATH
に加えますです
$ mkdir -p ~/man/man1 $ cp *.1 ~/man/man1
として、適当なエディターで.zshenv
を開いて、
export MANPATH=$HOME/man:
を追加しますです。ポイントは、zshのmanファイル(拡張子が1で終わっているもの)を~/man/man1
にコピーしたのに対し、環境変数MANPATH
に設定したパスは$HOME/man
であるという点です。これは、man
のディレクトリのレイアウトに合わせているからなのです。
また。MANPATH
の最後に:
を追加していますですが、これを忘れるとシステムのmanを参照できなくなりますですので注意ですー>ω<
zsh文法帳なのですー>ω<
moonline.zshというものを作っていますですので、zshの文法を理解していなければいけないのですが、わたしはまだzshを使い始めて一週間にもならない未熟者なのですので、学習したことをメモしておきたいと思いますですー>ω<
色々と偏っているかもしれませんですし、(恐らく永遠に)書きかけの記事なので、あまりあてにしない方がいいかもしれないのですー>ω<
また、シェルスクリプトもzshも初心者なので、どこもかしこも間違っていそうな気がしていますです。なので、間違いを指摘していただけると、わたしとしては至極嬉しい限りなのです!
コメント
# コメントは # から始めますですー>ω< # ちなみに、 下記のオプションを設定しないと対話シェル中ではコメントが有効にならないので注意なのです setopt interactive_comments
コマンドの実行
# 普段、端末で行なうのと同じです mkdir test # mkdir はディレクトリを作るコマンドです (MaKe DIRectoryの略だと思われます) cd test # cd はディレクトリを移動するコマンドです (Change Directoryの略だったと思いますです) # ; で区切って複数実行できますです touch test.txt; ls # touch はファイルの編集時刻を更新するコマンドです。ファイルが無い場合は作成する、という機能もありますです # ls はディレクトリのファイルなどの一覧を表示するコマンドです # && で、左のコマンドが正常に行なわれたら、右のコマンドを実行する、といったことができますです mkdir test2 && cd test2
出力
# 出力には echo コマンドを使いますです echo "Hello World" # 改行なしにする場合は、 -n オプションを付けますです echo -n "Hello World"
変数
# 代入(連想配列以外なら宣言は必要ないのです) # 変数名と = の間に、空白を入れないことが大切なのです x=1 # 複数使う場合は、空白区切りで並べられますです(別に改行で区切っても問題ないです) y=2 z=3 # 初期化したい場合は、左辺に何も書かないのです(単に変数名だけだと、その名前のコマンドと解釈されてしまいますです) w= # 参照は$を付けますです echo $x #=> 1 # 空白を含む場合は、 "" で囲うのです x="Hello World" echo $x #=> Hello World # 変数から変数への代入は、参照するのと同様のイメージなのです y=$x echo $y #=> Hello World # "" の中でも変数を展開できるのです echo "$x - こんにちは世界" #=> Hello World - こんにちは世界 # ただし、 '' の中では展開されないのです echo '$x' #=> $x # 存在しない変数は "" (長さゼロの文字列)として扱われるのです(エラーが出ない) echo $xxx #=> (何も表示されない) # 展開するとき、どこまでが変数名か分からなくなるなら、 {} で変数名を囲うのです echo "$xWideWeb" #=> (何も表示されないのです。なぜなら、存在しない $xWideWeb という変数を参照しているからなのです) echo "${x}WideWeb" #=> Hello WorldWideWeb
配列
# () で囲って、要素を空白で区切りますです xs=(1 3 5) # 要素の参照は、1から始まりますです echo $xs[1] #=> 1 echo $xs[2] #=> 3 echo $xs[3] #=> 5 # 要素に代入することもできますです xs[1]=2 echo $xs[1] #=> 2 # 配列の入った変数をそのまま参照すると、空白区切りの文字列となりますです echo $xs #=> 2 3 5 # 要素数は、 $# で参照しますです(冷静に考えると、ここでの # はコメントとして解釈されないのですね。不思議です(`・ω・′)) echo $#xs #=> 3 # 要素の追加には += を使いますです。この場合は末尾に追加されるのです xs+=(7) echo $xs #=> 2 3 5 7 echo $#xs #=> 4 # 配列を別の変数に代入するときは、()で囲むのです # ちなみに、要素に空白を含むものがあっても問題ないのです ys=($xs) echo $ys #=> 2 3 5 7 # さっきのはRubyやPythonなどのようにオブジェクトを参照しているのではなく、コピーを取っているので、要素に代入しても一方に反映される、といったことはないのです ys[1]=1 echo $ys #=> 1 3 5 7 echo $xs #=> 2 3 5 7 # 配列のループには、 for文(これはzshに組み込まれたものなので、コマンドではなく文法なのだと思いますです)を使いますですー>ω< # これは、pythonで言うfor in文、PHPのforeach文のようなものなのではないかと思いますです for x in $xs; do # x に各要素が代入されていますです echo -n "${x}x" done #=> 2x3x5x7x # どうしてもインデクッスでループをしたいのなら、次のように書くこともできますです # (ただし、あまりこの書き方はしないような気がしますです) for ((i = 1; i <= $#xs; i++)); do echo "$i --> $xs[$i]" done #=> 1 --> 2 #=> 2 --> 3 #=> 3 --> 5 #=> 4 --> 7
関数
# zshの関数の定義方法は、2つありますです # 1つは、function文を使う方法です function fn1() { # 引数は $1,$2...$9 で取得することができますです echo "1 --> $1" echo "2 --> $2" } # 呼び出しは、コマンドのようになりますです fn1 a b #=> 1 --> a #=> 2 --> b fn xxx yyy #=> 1 --> xxx #=> 2 --> yyy # もう1つは、functionを省略した形です。こちらの方が(何となくですけど)伝統的なような気がしていますです fn2() { echo fn2 } fn2 #=> fn2 # この2つの違いはよく分からないのです(′・ω・`) # どなたか教えて下さると幸いです # 今回は、省略した形の方が短かく書けるし、そうしないとVimのインデントが上手く働いてくらなかったのでそちらを採用しますです # 関数の中で変数を使うときに、グローバル変数を壊したくないのなら、 local で宣言するのです x="global variable" y="global variable" fn3() { # こんな風に local x x="local variable" echo "x is $x" #=> x is local variable # 宣言と代入を同時に行なうこともできますです local y="local variable" echo "y is $y" #=> y is local variable # 複数の変数を同時に宣言することもできますです # 初期値はあっても無くても問題ないのです local z w=local # ただし local の場合、配列は同時に代入することができないのです # local xs=(1 2 3) # これはエラーになるのです # この場合、宣言と代入を2回に分けて行なうといいのです local -a xs # -a オプションは、配列の変数を宣言していることを表わしますです。今回はすぐに代入してしまうので関係ありませんが、空の配列として初期化されますです xs=(1 2 3) echo $xs #=> 1 2 3 echo $#xs #=> 3 } # localで宣言しているので、変数は上書きされないのです fn3 # fn3の呼び出し echo "x is $x. y is $y too" #=> x is global variable, y is global variable too # 可変長引数は、$* か $@ という特殊な変数を使って参照できますです。また、引数の個数は $# なのです # fn4 は与えられた引数を全て表示する関数 fn4() { local i arg # for文で使うループ変数も local で宣言しておかないと、グローバルな変数として扱われてしまうので注意です for ((i = 1; i <= $#; i++)); do echo "$i --> $*[$i] $@[$i]" done } # fn4を呼び出してみますです。$@と$*が "この場合は" 違わないことが分かりますです fn4 1 a "Hello World" #=> 1 --> 1 1 #=> 2 --> a a #=> 3 --> Hello World Hello World # もう1つ実験してみますです # $* と $@ で引数を展開して、fn4 に渡す関数 fn5() { echo '$*'; fn4 $* echo '$@'; fn4 $@ } fn5 1 a "Hello World" #=> $* #=> 1 --> 1 1 #=> 2 --> a a #=> 3 --> Hello World Hello World #=> $@ #=> 1 --> 1 1 #=> 2 --> a a #=> 3 --> Hello World Hello World # $* と $@ は、配列として参照する場合には違いはないのです # では、どんなときに違いが表れるのかというと、""に囲んで展開したときに変化があるのです # fn6 は与えられた引数を "$*" で fn4 に渡す関数 fn6() { echo '"$*"'; fn4 "$*" } # fn7 は与えられた引数を "$@" で fn4 に渡す関数 fn7() { echo '"$@"'; fn4 "$@" } # この fn6 と fn7 を使うと、"$*" と "$@" の違いが顕著に表れますです fn6 1 a "Hello World" #=> "$*" #=> 1 --> 1 a Hello World fn7 1 a "Hello World" #=> "$@" #=> 1 --> 1 1 #=> 2 --> a a #=> 3 --> Hello World Hello World # "$*" のときは1つのパラメーターとして、 "$@" のときは複数のパラメーターとして展開されていますです!
(書きかけ…。まだまだ書きたいことはあるのです…。文字列関連とか、ともかく色々)