節約テクノロジ >

bashとzshの違い。bashからの乗り換えで気をつけるべき16の事柄


bashからzshに乗り換えるユーザーを対象に16の違いをまとめました。MacOSもbashからzshに変更になりましたので、zshを使い始めるにあたってのポイントを解説していきます。

目次

  1. 1. zshの起動ファイル
    1. ログインシェルモード起動時
    2. ログインシェルモード終了時
    3. 対話的モード(非ログインシェルかつ端末接続時)起動時
    4. 非ログインシェル終了時(含む非対話的モード)
    5. 非対話的モード(標準入出力が端末でない場合)起動時
  2. 2. bashとzshの制御構文系の違い
    1. if/for/while/until/case/select/(..)/{..}
    2. repeat
    3. always
  3. 3. bashとzshの関数定義の違い
    1. 一括定義
    2. 無名関数
    3. Autoload関数
    4. 関数フック
    5. Trap関数
  4. 4. bashとzshのエイリアスの違い
    1. クォート
  5. 5. zshのマルチリダイレクト(Multios)
    1. マルチリダイレクト出力
    2. マルチリダイレクト入力
    3. コマンドなしリダイレクト入力
  6. 6. Job関係
  7. 7. 算術式と演算子
    1. 基数
    2. 演算子と小数点
    3. 条件演算子
  8. 8. bashとzshのプロンプトの違い
  9. 9. bashとzshのヒストリ履歴の違い
  10. 10. bashとzshのプロセス置換の違い
  11. 11. bashとzshのパラメータ展開
  12. 12. bashとzshの配列とハッシュ
    1. bashとzshの配列の添字の違い
    2. zshのハッシュ配列の使い方
  13. 13. bashとzshのシェル変数の違い
    1. シェルがセットする変数(引数やステータス系)
    2. その他シェル変数
  14. 14. cdコマンド
    1. ディレクトリ名だけでcd
    2. 自動的にpushd
    3. 似た名前のディレクトリの水平移動
    4. 深いディレクトリへの移動
  15. 15. bashとzshのファイル名補完の違い
    1. 特殊文字
    2. 深いディレクトリ
  16. 16. bashとzshのライン入力の違い
  17. まとめ bashからzshへの乗り換えに向けて
  18. 付記1 bashとzshの共存と共有設定ファイルについて
  19. 付記2 フレームワークについて
    1. Oh My Zsh
    2. Prezto
  20. 付記3 プラグインマネージャについて
    1. Antigen
    2. zplug
    3. zplugin
    4. その他のプラグインマネージャ
    5. 結局zshにはどのプラグインマネージャやフレームワークがいいのか
    6. 補足の補足 MacOSで使う場合はは最初からzplugを使ったほうがいい
  21. コメント

1. zshの起動ファイル

起動時の読み込みファイルはbashとzshで全く異なります。

以下の3種の起動方法によってそれぞれ読み込まれるファイルが変わります。

また表ではzshのドット付きファイルはチルダ($HOME)と書きましたが実際は$ZDOTDIRという環境変数で変更することが出来ます。

ログインシェルモード起動時

bash zsh
/etc/profile /etc/zshenv
~/.bash_profile ~/.zshenv
~/bash_login /etc/zprofile
~/.profile /~/.zprofile
/etc/zshrc
~/.zshrc
/etc/zlogin
~/.zlogin

bashの方はドット付きファイルに関しては一つ見つけたら以降の探索は行いません。zshは例えば.zshrcと.zlogin両方あれば両方実行します。

定番フレームワークであるOh My Zshは.zshrcを使うので、例えば自分のログイン環境だけで有効にしたい設定は.zloginに分けて書く様な使い方もできます。(ただし非ログインシェルでは実行されないので注意)

ログインシェルモード終了時

bash zsh
~/.bash_logout ~/.zlogout
/etc/zlogout

対話的モード(非ログインシェルかつ端末接続時)起動時

bash zsh
~/.bashrc /etc/zshenv
~/.zshenv
/etc/zshrc
~/.zshrc

非ログインシェル終了時(含む非対話的モード)

bash zsh
なし なし

非対話的モード(標準入出力が端末でない場合)起動時

bash zsh
$BASH_ENV /etc/zshenv

みてわかる通り、zshの/etc/zshenvはいかなる場合も読み込まれるので、なるべく記述は少なめにすることが推奨されています。

2. bashとzshの制御構文系の違い

if/for/while/until/case/select/(..)/{..}

同じです。zshでは加えてthenやdo省略しC言語風に記述できます。

$ if [[ true ]] { echo hoge; }
hoge

$ if [[ true ]] echo hoge
hoge

repeat

zshではrepeat制御構文が追加されています。

$ repeat 3;do echo hoge;done hoge hoge hoge

always

zshではエラー処理などで{..}内でbreak/returnを記述し処理を止めることが出来ます。またその後。必ず実行されるalwaysブロックを記述できます。

$ { return; echo hoge; } always { echo fuga; }
fuga

3. bashとzshの関数定義の違い

一括定義

zshでは複数の名前の関数を一度に定義できます。trapの定義に有効です。

$ foo bar () { echo hoge; }
$ foo
hoge
$ bar
hoge

無名関数

無名関数を使えます。これは非同期呼び出し用ではなくローカルスコープ形成用です。無名関数は定義時に即実行されます。

$val=hoge; function { local val=fuga; echo $val; }; echo $val;
fuga
hoge

Autoload関数

あらかじめ起動ファイルで定義しておかなくても、fpath環境変数とautoload指定により実行時にロードされる関数を作成できます。

この仕組みにより関数のモジュール化が可能となります。

fpath環境変数が示す先に関数名と同名のスクリプト(例えばhoge()の場合はhoge)を設置しておき、その中に同名の(例で言うところのhoge())を定義しておきます。

そして起動ファイルに「autoload 関数名」と記述することで関数呼び出し時に上記スクリプトが自動的に実行され定義されると言うわけです。

$ cat $HOME/hoge/fuga
fuga(){echo Hello from fuga}

$ fpath=($HOME/hoge $fpath)
$ autoload fuga
$ fuga
Hello from fuga

関数フック

関数名_functionsという配列を定義しそこに関数名を列挙することでその関数をフックできます。呼び出しは元の関数が先に行われ同じ引数が渡されます。フックできる関数は以下の通り。

関数名 役割
phpwd cd時
periodic PERIODIC秒毎に定期呼び出し
precmd プロンプトが出る毎(コマンドの後)
preexec コマンド実行直前
zshaddhistory history追加時
zshexit メインシェル終了時(Subshellでない)

Trap関数

bashではtrapコマンドでフックしていましたがzshではTRAP関数を定義することでフックします。トラップの種類は上記リンク参照。

$ TRAPINT() { echo hoge }
$ cat  #ctrl+c
hoge

4. bashとzshのエイリアスの違い

基本的にbashとzshのaliasは同じですが、zshでは-gオプションをつけることでパイプやリダイレクション文字もエイリアスに含むことが出来る。

$ alias -g H='|head'
$ cat foo.txt H
# 上記は$ cat foo.txt |head
# になる

また-sオプションをつけることで対象の拡張子によってコマンドを起動できる(サフィックスエイリアス)

$ alias txt=vim
$ hoge.txt
# 上記は vim hoge.txtになる

クォート

基本的に同じ。シングルクォートは変数展開しない、ダブルクォートは変数展開し、バックスラッシュでクォート出来ます。シングルクォート文字列の中にシングルクォートを入れたい場合は$'...'としてバックスラッシュを使います。

5. zshのマルチリダイレクト(Multios)

マルチリダイレクト出力

zshでは以下の書き方をすることで同時に複数のファイルにリダイレクトで書き出すことが出来ます。

$ cat foo.txt >hoge.txt >fuga.txt

上記の例ではhoge.txtとfuga.txtは同じ内容になります。

bashでもteeコマンドを使えば同等のことが出来ますが、zshの書き方はより自然ですね。追記も同様に行えます。

$ cat foo.txt >>hoge.txt >>fuga.txt

teeコマンドの様に標準出力に出しつつファイルに保存したい場合は以下の様に書きます。

$ cat foo.txt >hoge.txt | cat

注意点としてはMultiosを使った場合はサブプロセスとして実行され即時コマンドが終了することです。次のコマンドで出力されたファイルを受け取るためサブプロセスではなくカレントプロセスで実行したい場合は以下の様にする必要があります。

$ {cat foo.txt } >hoge.txt >fuga.txt

マルチリダイレクト入力

同じくzshでは入力リダイレクトを複数使えます。これは単にcatでつなげてパイプで入力するのと同等です。

$ sort <hoge.txt <fuga.txt
# 以下と同じ
$ cat hoge.txt fuga.txt | soft 

コマンドなしリダイレクト入力

$ <hoge.txt

上記の様にコマンドなしの場合は

$ cat hoge.txt | more

として扱われます。これはNULLCMD(デフォルト:cat)とREADNULLCMD(デフォルト:more)で設定出来ます。

6. Job関係

bashとほぼ同じですが、zshの場合は以下の様に?をつけることで先頭だけでなく部分一致でjobを指定できます。

 $ sleep 100 & 
 $ %?lee

またzshはコマンド実行時に&!とエクスクラメーションをつけることで、jobsに出ない様にバックグラウンドプロセスを起動できます。

7. 算術式と演算子

基数

zshでは8進数や16進数を扱えます。

$ echo $(([#16] 10 ))
16#A

16は基数で16進数表記しろという指定です。setopt cbasesしておくことでC言語風の出力もできます。

$ setopt cbases
$ echo $(([#16] 10 ))
0xA

プレフィックスを消すこともできます。

$ echo $(([##16] 10 ))
A

演算子と小数点

zshではbashの演算子に加えてexponentや比較演算子’が加えられています。また小数点演算も可能です。

$ echo $((2.0 + 2.1))
$ echo $((2 ** -1))  #-> 0.5
$ echo $((2 == 2))  #-> 1

変数は最初の代入で整数かfloatかが決定されるので注意してください。

条件演算子

条件演算子はbashとzshはぼぼ同じですが、zsh文字列比較は完全一致ではなくパターン一致となります。また==や=の代わりに=~を使うことで正規表現も使用可能です。

$ [[ HOGE = HO* ]];echo $?
0

$ [[ HOGE =~ HO.. ]];echo $?
0

8. bashとzshのプロンプトの違い

bashとzshで全く異なります。

どちらもPS1、PS2・・で変更する点は同じですが置換文字が異なりzshではさらに複雑なことが出来ます。通常はTHEMEで変更します。zshのプロンプト用の置換はprintビルトイン関数でも利用可能です。

9. bashとzshのヒストリ履歴の違い

ほぼbashとzshで同じですが、modifiers(修飾子)が拡張されています。

修飾子 機能
a ファイル名を絶対パスに
A aと同じだがシンボリックリンクを解決
c コマンド名を絶対パスに
l 小文字に変換
p aと同じだが.や..は残す
Q クォートを1段外す
u 大文字に変換

10. bashとzshのプロセス置換の違い

以下の様なプロセス置換はbashとzshで共通です。

$ cat <(cat hoge.txt) |tee >(sort)

上記はFIFOを使って行われますが、zshでは以下の様に書くことでテンポラリファイルを介すことが出来ます。これはFIFOではなくファイルでシークしたい場合に利用されます。

$ cat =(cat hoge.txt) | tee >(sort)

11. bashとzshのパラメータ展開

ぼぼbashとzshで同じです。zshには以下の拡張がされています。

$ echo ${+a}
#aがセットされていたら1、そうでなければ0

$ a=(hoge fuga)
$ echo ${a:#fuga}
hoge

$ a=(hoge fuga moga)
$ b=(hoge fuga)
$ echo ${b:|a}
moga
$ echo ${b:*a}
hoge fuga

$ a=(1 2 3)
$ b=(4 5 6)
$ echo ${a:^b}
1 4 2 5 3 6

$ a=hogehogehoge
$ echo ${a/hoge/FUGA}
FUGAhogehoge
$ echo ${a//hoge/FUGA}
FUGAFUGAFUGA
$ echo ${a:/hoge/FUGA}
hogehogehoge   #完全一致でないと置換されない

$ a=(1 2 3)
$ echo hoge${^a}
hoge1 hoge2 hoge3

$ IFS=','
$ a="1,2,3"
$ echo ${=a}
1 2 3    #IFSでスプリット

$ a="*.txt"
$ echo ${~a}
hoge.txt fuga.txt moga.txt   #GLOBさせる

12. bashとzshの配列とハッシュ

bashとzshの配列の添字の違い

bashの配列は0から、zshはなぜか1から始まります。

 $ a=(A B C)
 $ echo $a[1]
 A #-> zshの場合
 B #-> bashの場合

zshのハッシュ配列の使い方

zshではハッシュ構造が使えます。typesetで宣言が必要です。

$ typeset -A a
$ a[hoge]=1
$ a[fuga]=2
$ echo $a[hoge]
1
$ echo $a[*]
hoge fuga    #キーを列挙
$ echo $a[@]
1 2                #値を列挙

13. bashとzshのシェル変数の違い

シェルがセットする変数(引数やステータス系)

以下はbash/zshで共通です。

$! / $# / $$ / $@ / $? / $0 / $_  / $-

以下がzsh独自です。

変数 機能
argv $*と同じだがグローバル変数
status $?と同じ
ARGC $#と同じ
pipestatus 最後のパイプラインの全コマンドのステータス

その他シェル変数

PWDやIFSなど大部分は共通ですが、bashとzshで異なる部分も多くあります。

14. cdコマンド

cd -で前のディレクトリに戻る点、cdした時に見つからなかったらcdpath(zshは小文字)を検索するところまではbashとzshは同じです。

ディレクトリ名だけでcd

zshではオプションで様々なcdについての設定が出来ます。

$ setopt AUTO_CD
$ some_directory
~/some_directory

setopt AUTO_CDでcdコマンドなしでディレクトリ名だけでcdできる様になります。

自動的にpushd

 $ setopt AUTO_PUSHD
 $ cd a
 $ cd b
 $ cd c
 $ dirs
 ~/c ~/b ~/a
 $ cd +2
 ~/a

setopt AUTO_PUSHDで自動的にpushdする様になります。cdに+と数値をつけることでdirsの左から好きな番号のディレクトリにcdすることが出来ます。setopt PUSHD_SILENT しておけば、移動の度にディレクトリスタックを表示するのを止めることが出来ます。

似た名前のディレクトリの水平移動

$ mkdir testA testB
$ cd testA
$ cd A B     #-> 現在のディレクトリ名のAをBに置換して移動、つまりtestBにcd

cdに2つ引数を並べることで、現在のディレクトリの左の文字列を右の文字列に置換してcdしてます。例えばhoge1,hoge2,hoge3...の様に水平に連番に並んでいるディレクトリ間を移動する時便利です。

深いディレクトリへの移動

$ mkdir -p foo/bar/baz
$ cd **/baz

これは次のファイル名補完の機能ですが、アスタリスク2つ並べることで深いディレクトリも検索して一気にcdできます

15. bashとzshのファイル名補完の違い

特殊文字

zshはbashの* / ? / [...] と[:alpha:]などの文字クラスに加えて以下のファイル名補完用の文字が用意されています。

[!...]
[^...]
#...を含まない文字にマッチ
$ touch hogeA hogeB
$ echo hoge[^B]
hogeA

$ touch hoge1 hoge2 hoge3
$ echo hoge<1-2>
hoge1 hoge2
#数値範囲にマッチ

$ touch hogeA fugaA
$ echo (hoge|fuga)A
 hogeA fugaA
#グルーピング

 $ echo *(/)   #ディレクトリのみにマッチ
 $ echo *(.)   #ファイルのみにマッチ
 $ echo *(@)   #シンボリックリンクのみにマッチ
 $ echo *(m+30)   #30日前より過去に変更されたファイルにマッチ(m=変更/a=アクセス/c=作成)
 $ echo *(m-30)   #30日前より未来に変更されたファイルにマッチ
 $ echo *(cm-30) #上記の単位指定。30分前より後に作成されたファイルにマッチ(h=時/m=分/s=秒/w=週/M=月)
 $ echo *(L+1000)   #1000バイトを超えるファイルにマッチ
 $ echo *(LM+10)   #10Mバイトを超えるファイルにマッチ(M=メガ/K/Gも使用可能)

その他setopt EXTENDED_GROBでさらに使える特殊文字やフラグが増えます。

深いディレクトリ

cdの時と同じく深いディレクトリをGlobすることが出来ます。

$ vim **/*.c

さらにsetopt EXTENDED_GROBを有効にすることでfindコマンドの様にディレクトリだけを選んだりファイルだけを選んだりすることができる様になります。

16. bashとzshのライン入力の違い

bashではemacs風とvi風が選べましたがzshでも同様にそれぞれを選べますのでそれほど違和感はありません。

 $ bindkey -e 
 #emacs風のキーバインディングにする

まとめ bashからzshへの乗り換えに向けて

ということで駆け足でbashとzshの違い16を紹介してきました。

ここはほんのさわりで、setopt EXTENDED_GROBを有効にしたりまたフレームワークやプラグインシステムの利用によりzsh膨大な機能の世界への扉が開かれます。

取り急ぎはbashからの移行ポイントとしては起動ファイルの違いとシェル変数の違いを意識しながら設定ファイルを組み、ビルドインの機能から徐々に使ってみれば良いかと思います。

スクリプトについては基本的にbashの制御構造が使えるので互換性も検討してしばらくはbash構文にするのが無難かもしれません。

インタラクティブシェルとしては深い階層や水平移動、自動pushdのcdが非常に使いやすいのでその辺りから積極的に利用すると良いでしょう。

付記1 bashとzshの共存と共有設定ファイルについて

zshへ移行するにしても暫くはbashも使用しながら試用的に移行したい場合もあるでしょう。そんな時起動ファイルの環境変数を変更するたびにbash/zsh両方のファイルを変更するのは不効率です。

おすすめはファイルを3つに分けることです。1つはbash専用の.bash_profile、もう一つはzsh専用の.zshrc、そして両方の共通ファイルとして適当な名前でファイル、例えば.profile_commonを作ります。

そして.bash_profile、.zshrcそれぞれに以下の様に.profile_commonがあればそれをロードするコードとそれぞれのシェル固有の設定を入れておくわけです。

#!/bin/bash

COMMON_PROFILE=$HOME/.profile_common

if [ -e $COMMON_PROFILE ]
then source $COMMON_PROFILE
fi

shopt -s extglob
export PS1="\n^[[36m(\!)[\t^H^H^H]{\$?}^[[33m\h:^[[31m\w^[[0m\n$ "

#bash固有の設定・・・
#!/bin/zsh

COMMON_PROFILE=$HOME/.profile_common

if [ -e $COMMON_PROFILE ]
then source $COMMON_PROFILE
fi

#zsh固有の設定・・・

付記2 フレームワークについて

「フレームワーク」はざっくり言うと「出来合いの設定ファイル」です。よく使うオプションが有効になったり便利な関数や補完が含まれており、プラグインシステムにより取捨選択できます。

zshのフレームワークは有名なもので2種類あります。

Oh My Zsh

古くからあるzsh用のフレームワークです。インストールにより.zshrcが専用のものに書き換わり、そこでプラグインの読み込みなどの設定を行います。

上の例で使っていた.zshrcは.zshrcがOh My Zshのフレームワークで上書きされることに注意してください。zshの場合は複数起動ファイルがあっても読み込まれますので.zloginに退避させるのも良いでしょう(その場合はログインシェルでないインタラクティブモードでは読み込まれないので注意)。

Oh My Zsh一つのgitリポジトリで複数の作者がプラグインをメンテしているのが特徴です。

Prezto

Oh My Zshの後継のフレームワークです。Oh My Zshの欠点として起動が遅いこと、プラグインが全て一つのリポジトリで管理されている事が挙げられます。

そこでPreztoはプラグインをgitのサブモジュールとして分けて管理し、起動についても必要最低限のみ読み込む事で改善を図っています。

Preztoを使う場合は全ての起動ファイルが使われてしまうため、上記の様にbashと共通設定を持ちたい場合は専用のカスタムファイルである.zpreztorcと言う専用ファイルから読み込む様にします。

またプロンプトを書き換えるテーマについても独自仕様であったOh My Zshに比べ、zsh本来のpromptコマンドで切り替えられる仕様なのでより自然に変更可能で、プレビューもできます。

付記3 プラグインマネージャについて

上記どちらにせよフレームワークを使う場合は起動ファイルは書き換えられてしまいます。それが気持ち悪い場合はプラグインマネージャを使います。プラグインマネージャのインストールは.zshrcにsourceでロードする事で行いますので起動ファイル全体を書き換えられることはありません。

また上記の2つのフレームワークに対応しており、どちらのプラグインも利用可能です。一発でオススメ設定にするのではなく、zshを自分で学習しながら少しずつプラグインを利用したい向きには最初からフレームワークではなくプラグインマネージャ経由でOh My ZshやPreztoのプラグインを使うことをおすすめします。

Antigen

zshで最も有名なプラグインマネージャです。インストールは.zshrc全体を書き換えるのではく、1行だけ加えて、続けて使いたいOh My ZshやPreztoのプラグインを書き最後にapplyするだけです。フレームワークは自動的に読み込まれるのでインストールを意識する必要はありません。また独立したgitのリポジトリをプラグインとして指定して利用することも出来ます。

後発のプラグインマネージャほど柔軟性や高速性はありませんが使い方がシンプルなので学習コストがかからずOh My ZshやPreztoプラグインを簡潔に使いたいだけの場合はAntigenが未だおすすめです。

zplug

並列処理による高速性と柔軟性を兼ね揃えた和製プラグインマネージャで、世界中で使われています。antigenよりはやや学習コストがややかかりますがバージョンロックやbash用のプラグインもロード出来たりと柔軟性が非常に高いです。

zplugin

後発のプラグインマネージャです。zplugよりさらに高速と言われています。プラグインの修飾が1行で書けないのが難点ですが非常に多機能で細かい設定が可能です。学習コストは一番かかると思います。

その他のプラグインマネージャ

その他、高速化されたantigenなど多数。

結局zshにはどのプラグインマネージャやフレームワークがいいのか

個人的な意見としては設定ファイルを一気に書き換えてしまうフレームワーク(Oh My ZshやPrezto)をいきなりそのまま使うのはおすすめしません。それはzsh自体の理解の妨げになるからです。

まずは自分で素の状態でzshの設定ファイルを触ってみて、上記フレームワークのプラグインのソース(非常に簡単な構造です)を参考にしながら出来ることを理解するべきです。特にoh-my-zsh/lib辺りが見本として最適です。Prezto/moduleにもhistoryやdirectoryなど設定関係のセットがあり参考になります。

そしてフレームワークではなくプラグインマネージャを利用して「あくまで自分のやりたい事を簡潔に」するのが、正しい流れです。

プラグインを設定ファイルに書くときのアドバイスとしてはソースのURLをコメントで入れておくと中で何をしているか後から確認できるのでオススメです。

ではプラグインマネージャはどれがいいのか?と言う話になってきますが、最初は学習コストのかからないantigenで簡単にOh My ZshやPreztoのプラグインを触ってみるのが良いかと思います。2つのフレームワークのプラグインだけを使う範囲では一番簡潔に書けます。

その上でスピードや柔軟性、保守性が欲しくなった場合はzplugに移行するのが遠回りですが着実な手順でzplugのありがたみも理解できるという訳です。

補足の補足 MacOSで使う場合はは最初からzplugを使ったほうがいい

上ではまずはantigenをと書きましたが、MacOSの場合は不安定な箇所がありましたのでMacの場合はzplugから使い始めることをオススメします。

homebrewのインストールも対応していますが、インストールフォルダが標準的な$HOME/.zplugにならないので以下のインストールスクリプトを使ったほうがいいでしょう。

$ curl -sL --proto-redir -all,https https://raw.githubusercontent.com/zplug/installer/master/installer.zsh | zsh

preztoのプラグインだけを読み込む最も簡単な.zshrcのサンプルも掲載します。

source ~/.zplug/init.zsh

zplug "modules/history", from:prezto # https://github.com/sorin-ionescu/prezto/blob/master/modules/history/init.zsh
zplug "modules/directory", from:prezto # https://github.com/sorin-ionescu/prezto/blob/master/modules/directory/init.zsh
zplug "modules/osx", from:prezto, if:"[[ $OSTYPE == *darwin* ]]"

if ! zplug check --verbose; then zplug install;fi
zplug load #--verbose

historyとdirectoryはzshの基本的な履歴機能と自動pushd機能を有効にしてdでディレクトリスタック表示、数値だけでティレクトリスタックにジャンプ出来るようにするシンプルなプラグインです。非常に簡単なソースなので上のリンクから確認してみてください。

osxはMacOSのファインダーとの連携で、ファインダーで表示しているディレクトリやファイルをpdf/pds/cdsコマンドで取り込むことができます。同じく上のPreztoのリンクからドキュメントを参照してください。

この記事を見た人がよく読んでいる記事

節約テクノロジ >

コメント

トップページ

節約テクノロジ > bashとzshの違い。bashからの乗り換えで気をつけるべき16の事柄