前置き
想定読者
想定読者:テキスト処理をしようと思って手元の Mac でコマンドを打ちこんでみたけど書籍や Web サイトの見本通りに動作しなくて困っている人
macOS にプリインストールされているコマンド群には BSD(Unix 系 OS のひとつ)由来のものが多く含まれます.
一方,会社や大学などで利用するサーバ(業務用途・学術用途のサーバ)には Linux 系 OS がインストールされていることが多く,また,書籍や Web ページに(注釈なく)書かれているシェルスクリプトやワイライナーも Linux 系環境が想定されていることがしばしばです.
BSD 系コマンドと GNU/Linux 系コマンドの間の互換性は完全ではなく,たとえば同じ ls
でも,BSD の ls
と GNU coreutils の ls
では利用できるオプションは異なります.
この記事では,初心者が,シェルコマンドを用いたテキスト処理を行う際に(ちょっとしたシェル芸で遊んでみたいときに)書籍や Web ページのコマンドを変更せずに手元で再現できるよう,また macOS 上で作成したコードの可搬性を高められるよう,Homebrew を用いて,macOS に,GNU/Linux 系コマンドを中心とした標準的なテキスト処理用のコマンド群をインストールする手順 を示します.
# 「ここの記述が不正確だぞ」「もっと効率の良い・問題の起きづらい導入法あるよ」「これも入れるべき」など突っ込みどころがありましたら是非コメントをお寄せください.
想定環境
※ 使っているシェルが Bash か Zsh か不明な場合は,ターミナルで $ echo $SHELL
と打ってみてください.ログインシェルが(たとえば /bin/bash
のように)表示されます.
取り扱うコマンド
- (主に行単位の)テキスト処理
-
tr
,cut
,ed
,gnu-sed
,gawk
-
grep
,ag
,sift
-
sort
,uniq
,tac
,head
,tail
-
- 複数ファイルの結合・分割・差分抽出
cat
-
paste
,join
-
diff
系コマンド
- 構造化テキストの処理
-
xmlstarlet
,jq
,pandoc
-
- 並列処理
-
xargs
,parallel
-
- ダウンロード
wget
- 圧縮・解凍
-
gnu-tar
,gzip
,unzip
-
- ページャなど
-
less
,lv
-
- エディタ
-
nano
(emacs
およびvim
は扱いません)
-
- ファイルシステム
-
ls
,mv
, etc.
-
- ほか
-
echo
, etc.
-
取り扱わないコマンド
- バイナリファイル処理・低レベル言語の処理
- ネットワーク・通信
- プロセス・ジョブ管理
- バージョン管理
- シェル(そのもの)
- ターミナルマルチプレクサ
- ほか
主な参考サイト
- Install and Use GNU Command Line Tools on Mac OS X | Hong Xu (xuhdev)
- terminal - How to replace Mac OS X utilities with GNU core utilities? - Ask Different
- The First 10 Things I Do on a New Mac
- One Tip Per Day: Install GNU in Mac OS
導入手順
1. シェルの設定ファイルの調整
-
~/.bash_profile
と~/.bashrc
(Bash の場合),または~/.zshenv
と~/.zshrc
(Zsh の場合)に以下を追記します.ファイルが存在しない場合は作成してください.~/.bash_profileexport PATH export MANPATH # (~/.bash_profile 末尾) # for login (and then interactive) shell # ref. https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html if [ -f ~/.bashrc ]; then . ~/.bashrc; fi
~/.bashrc# (~/.bashrc 冒頭) # If not running interactively, don't do anything # ref. https://www.gnu.org/software/bash/manual/html_node/Is-this-Shell-Interactive_003f.html if [ -z "$PS1" ]; then return fi
~/.zshenvexport PATH export MANPATH # -U: keep only the first occurrence of each duplicated value # ref. http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html#index-typeset typeset -U PATH path MANPATH manpath # ignore /etc/zprofile, /etc/zshrc, /etc/zlogin, and /etc/zlogout # ref. http://zsh.sourceforge.net/Doc/Release/Files.html # ref. http://zsh.sourceforge.net/Doc/Release/Options.html#index-GLOBALRCS unsetopt GLOBAL_RCS # copied from /etc/zprofile # system-wide environment settings for zsh(1) if [ -x /usr/libexec/path_helper ]; then eval `/usr/libexec/path_helper -s` fi
~/.zshrc# (~/.zshrc 冒頭) # in ~/.zshenv, executed `unsetopt GLOBAL_RCS` and ignored /etc/zshrc [ -r /etc/zshrc ] && . /etc/zshrc
-
$ source ~/.bash_profile
(Bash の場合)または$ source ~/.zshenv; source ~/.zshrc
(Zsh の場合)をおこない設定を反映します.
これらの設定が必要な理由
あらかじめ PATH
, MANPATH
を環境変数にしておきました.以後 path を通したい場合は PATH=/usr/local/bin:${PATH}
等をおこなうだけで(export を都度書かずとも)サブシェルに引き継がれる path が通ります.また,zsh 向けには path の重複登録を抑止する命令(typeset -U
)を追記しています. ※ 「path を通す」の意味が気になるかた,は例えば拙文ですが UNIX/Linux 環境でのコマンドライン操作に慣れる…前の基礎知識 - Qiita などをご確認ください.
さらに,(1) Bash をログインシェルとして起動時する際に ~/.bashrc
が読み込まれない問題,および (2) El Capitan 以後の macOS で Zsh を利用する際に path_helper
が ~/.zshenv
よりも後から実行される問題に対処してあります.特に Zsh を用いている場合は,Homebrew でインストールした GNU/Linux 系コマンド群を(g
prefix 抜きで)利用するためにこの設定が必須です. ※ 詳細が気になる方は末尾の補遺をご確認ください.
2. Homebrew の導入
$ which brew
として brew
コマンドが見つからない場合は, Homebrew のインストールからはじめましょう.
インストール
インストールは,公式ドキュメントにしたがい次のコマンドを叩けばOKです.
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
$ brew update
path を通す
-
~/.bash_profile
(Bash の場合)または~/.zshenv
(Zsh の場合)に以下を追記します.~/.bash_profile# homebrew PATH=/usr/local/bin:/usr/local/sbin:${PATH} MANPATH=/usr/local/share/man:${MANPATH}
~/.zshenvpath=( /usr/local/bin(N-/) # homebrew /usr/local/sbin(N-/) # homebrew ${path} ) manpath=( /usr/local/share/man(N-/) # homebrew ${manpath} )
-
$ source ~/.bash_profile
(Bash の場合)または$ source ~/.zshenv
(Zsh の場合)をおこない設定を反映します.
確認
$ which brew
として /usr/local/bin/brew
が返ってくれば準備完了です.
3. GNU/Linux 版コマンドへの置き換え
ls
, grep
, find
といった基本的なコマンドを GNU/Linux 版に置き換えます.
インストール
$ brew install coreutils
$ brew install diffutils
$ brew install ed
$ brew install findutils
$ brew install gawk
$ brew install gnu-sed
$ brew install gnu-tar
$ brew install grep
$ brew install gzip
-
coreutils
-
ls
,mv
といった基本的なコマンド(の GNU/Linux 版)が含まれています. - テキスト処理用途には,
cat
,paste
,join
,head
,tail
,tr
,cut
,sort
,uniq
,wc
,echo
などを使うことになるでしょう. - 同梱されているコマンドの一覧は,
$ brew ls coreutils
や GNU Core Utilities - Wikipedia を参照してください.
-
-
diffutils
-
diff
,cmp
,diff3
,sdiff
が同梱されています. - これらのコマンドに関しては,GNU のコマンドがもともと macOS にインストールされていますが,Homebrew からより新しいバージョンをインストールすることができます.
- diff をした結果に色を付けて表示させたい場合は,別途
colordiff
をインストールしても良いでしょう.
-
-
findutils
-
find
,locate
,updatedb
,xargs
が同梱されています.
-
-
gzip
-
gunzip
,gzexe
,gzip
,uncompress
,zcat
,zcmp
,zdiff
,zegrep
,zfgrep
,zforce
,zgrep
,zless
,zmore
,znew
が同梱されています.
-
- grep
-
grep
,egrep
(=grep -E
),fgrep
(=grep -F
) が同梱されています.
-
-
gawk
- macOS にもともと入っている awk(いわゆる nawk)を, GNU 版の awk(いわゆる gawk)に置き換えます.これで,
$ gawk
あるいは単に$ awk
で GNU awk が呼び出されるようになります. - 基本的に gawk は nawk に対して大幅に機能が追加された上位互換版ですが,どうしても nawk を使いたいときのために
.bashrc
や.zshrc
にalias nawk=/usr/bin/awk
としておいても良いでしょう.
- macOS にもともと入っている awk(いわゆる nawk)を, GNU 版の awk(いわゆる gawk)に置き換えます.これで,
path を通す
-
~/.bash_profile
(Bash の場合)または~/.zshenv
(Zsh の場合)に以下を追記します.~/.bash_profile# coreutils PATH=/usr/local/opt/coreutils/libexec/gnubin:${PATH} MANPATH=/usr/local/opt/coreutils/libexec/gnuman:${MANPATH} # ed PATH=/usr/local/opt/ed/libexec/gnubin:${PATH} MANPATH=/usr/local/opt/ed/libexec/gnuman:${MANPATH} # findutils PATH=/usr/local/opt/findutils/libexec/gnubin:${PATH} MANPATH=/usr/local/opt/findutils/libexec/gnuman:${MANPATH} # sed PATH=/usr/local/opt/gnu-sed/libexec/gnubin:${PATH} MANPATH=/usr/local/opt/gnu-sed/libexec/gnuman:${MANPATH} # tar PATH=/usr/local/opt/gnu-tar/libexec/gnubin:${PATH} MANPATH=/usr/local/opt/gnu-tar/libexec/gnuman:${MANPATH} # grep PATH=/usr/local/opt/grep/libexec/gnubin:${PATH} MANPATH=/usr/local/opt/grep/libexec/gnuman:${MANPATH}
~/.zshenvpath=( /usr/local/opt/coreutils/libexec/gnubin(N-/) # coreutils /usr/local/opt/ed/libexec/gnubin(N-/) # ed /usr/local/opt/findutils/libexec/gnubin(N-/) # findutils /usr/local/opt/gnu-sed/libexec/gnubin(N-/) # sed /usr/local/opt/gnu-tar/libexec/gnubin(N-/) # tar /usr/local/opt/grep/libexec/gnubin(N-/) # grep ${path} ) manpath=( /usr/local/opt/coreutils/libexec/gnuman(N-/) # coreutils /usr/local/opt/ed/libexec/gnuman(N-/) # ed /usr/local/opt/findutils/libexec/gnuman(N-/) # findutils /usr/local/opt/gnu-sed/libexec/gnuman(N-/) # sed /usr/local/opt/gnu-tar/libexec/gnuman(N-/) # tar /usr/local/opt/grep/libexec/gnuman(N-/) # grep ${manpath} )
-
$ source ~/.bash_profile
(Bash の場合)または$ source ~/.zshenv
(Zsh の場合)をおこない設定を反映します.
確認
$ which -a ls
として /usr/local/opt/coreutils/libexec/gnubin/ls
(coreutils 版の ls)が /bin/ls
(システムデフォルトの ls)よりも上に返ってくれば準備完了です.以後 $ ls
と打てば coreutils の ls が呼び出されます.たとえば $ ls --color=auto
で(coreutils の ls 流に)出力結果に色付けをすることができます.
4. 便利コマンドの導入
もともと macOS にはインストールされていないものの,テキスト処理に便利なコマンド群は,この機会にインストールしておきましょう.これらのコマンドのために新たに path を通す必要はありません.
$ brew install ag
$ brew install jq
$ brew install lv
$ brew install parallel
$ brew install pandoc
$ brew install sift
$ brew install wget
$ brew install wdiff --with-gettext
$ brew install xmlstarlet
5. 古いコマンド群を新しくする
- macOS にプリインストールされているけれど,Homebrew でより新しいバージョンをインストールできるコマンド群も,この機会に新しいものと入れ替えておきましょう.これらのコマンドのために新たに path を通す必要はありません.
$ brew install nano
$ brew install unzip
まとめ
- Enjoy!
補遺
以下,本文中で触れた各種設定についての細かい補足です.
Bash の設定ファイルの調整が必要な理由
- Bash は起動時に次の順番で設定ファイルを読み込みます.
-
/etc/profile
(ログインシェルの場合) - (
/etc/bashrc
.macOS の場合に/etc/profile
から呼び出されます.) -
~/.bash_profile
,~/.bash_login
,~/.profile
(ログインシェルの場合)(最初に見つかったものがひとつだけ実行されます.) -
~/.bashrc
(インタラクティブシェルの場合)
-
- path などの設定は
~/.bash_profile
で,インタラクティブシェルの場合に限った設定(alias やプロンプトなど)は~/.bashrc
でをおこなえば良いように見えますが,いくつかの問題があります. - まず,ログインシェルは通常インタラクティブに利用されるにも関わらず,Bash でログインした際にインタラクティブシェルのための設定ファイル
~/.bashrc
は自動では読み込まれません.そこで,Bash の公式リファレンスにもある通り,~/.bash_profile
の末尾で~/.bashrc
を呼び出すようにします. - また,非インタラクティブシェルの場合に(
~/.bash_profile
から呼び出された)~/.bashrc
の設定が読み込まれないよう,~/.bashrc
の冒頭で分岐を入れます.これは,/etc/bashrc
の冒頭に元から書かれている分岐と全く同じ目的・書きかたです.
Zsh の設定ファイルの調整が必要な理由
-
Zsh は起動時に次の順番で設定ファイルを読み込みます.
-
/etc/zshenv
※ macOS には存在しません. ~/.zshenv
-
/etc/zprofile
(ログインシェルの場合) -
~/.zprofile
(ログインシェルの場合) -
/etc/zshrc
(インタラクティブシェルの場合) -
~/.zshrc
(インタラクティブシェルの場合) -
/etc/zlogin
(ログインシェルの場合) ※ macOS には存在しません. -
~/.zlogin
(ログインシェルの場合)
-
-
path などの設定は
~/.zshenv
で,インタラクティブシェルの場合に限った設定(alias やプロンプトなど)は~/.zshrc
でをおこなえば良いように見えます. -
しかし,El Capitan 以後の macOS では,
~/.zshenv
よりも後から読み込まれる/etc/zprofile
でシステムデフォルトの path が設定されます(正確にはpath_helper
が実行されます.詳細は/etc/zprofile
の中身および$ man path_helper
を参照).すなわち,ユーザが~/.zshenv
に登録した path よりも,システムデフォルトの path の優先順位が上がってしまいます.たとえば coreutils で導入したls
よりも,元々入っているls
が優先されてしまいます. -
この問題に対処するため,今回は (1)
GLOBAL_RCS
を用いて/etc/zprofile
を一旦無視し,(2)path_helper
は別途~/.zshenv
から呼び出しました.# 再掲 # ~/.zshenv # ignore /etc/zprofile, /etc/zshrc, /etc/zlogin, and /etc/zlogout unsetopt GLOBAL_RCS # copied from /etc/zprofile # system-wide environment settings for zsh(1) if [ -x /usr/libexec/path_helper ]; then eval `/usr/libexec/path_helper -s` fi # ~/.zshrc # in ~/.zshenv, executed `unsetopt GLOBAL_RCS` and ignored /etc/zshrc [ -r /etc/zshrc ] && . /etc/zshrc
代案
path_helper
の問題に対しては上記の方法(本文中に示した方法)がおすすめですが,ほかにも対策は考えられます.
たとえば /etc/zprofile
を無視する方法は unsetopt GLOBAL_RCS
の他にもあり得ます.
- (a)
setopt NO_GLOBAL_RCS
も同様の効果です.書き方はsetopt no_global_rcs
,setopt no_globalrcs
,setopt noglobalrcs
,setopt NO_GLobalrcs
, 等でも構いません. - (b)
/etc/zprofile
をコメントアウトしても同様の効果が得られます.ただし,このファイルは全ユーザに共通の設定ファイルであり,編集はおすすめできません.( @GeneralD さんコメントありがとうございます.) - (c)
$ brew install zsh --without-etcdir
で「/etc
以下のグローバルな設定ファイルを無視する zsh」をインストールすることができました.が,この機能はunsetopt GLOBAL_RCS
で代替できるため,現在は当該オプションは削除されています. - (d)
$ brew install zsh --disable-etcdir
で「/etc
以下のグローバルな設定ファイルを無視する zsh」をインストールすることができました.が,現在は当該オプションは削除されています.
また /etc/zprofile
を無視せずに path_helper
の問題に対処する方法もあり得ます.
- (e)
~/.zshrc
で path を通せば,このファイルは/etc/zprofile
より後から読み込まれるため,問題は解決されます.しかし,非インタラクティブシェルの場合に path が通らなくなる問題が生じ得ます. - (f)
~/.zprofile
で path を通せば,このファイルは/etc/zprofile
より後から読み込まれるため,問題は解決されます.しかし,非ログインシェルの場合に path が通らなくなる問題が生じ得ます.
一部の GNU/Linux コマンド用に特別な path を通す理由
- GNU coreutils など一部の GNU/Linux コマンド用に(Homebrew とは別に)path を通した理由を coreutils を例に挙げて説明します.他のコマンド群も同様です.
-
$ brew install coreutils
でインストールされたコマンドにはg
という prefix が付きます.たとえばls
はgls
としてインストールされます. -
/usr/local/opt/coreutils/libexec/gnubin
には prefix を省いたコマンド名(たとえばls
)から coreutils のコマンド(たとえばgls
)にシンボリックリンクが張られています.したがって,ここに path を通したあとは,ls
とタイプすれば coreutils のgls
が実行されるようになります. - 以前は一部のコマンドに対しては
$ brew install
時に--with-default-names
というオプションを設定し同様の効果を得ることができましたが,これは廃止されました.( @taku-ito5555 さんコメントありがとうございます.)
更新履歴
2019-04-01
-
--with-default-names
の削除への対応.( @taku-ito5555 さんコメントありがとうございます.) -
$ brew tap homebrew/dupes
の削除への対応.( @taku-ito5555 さんコメントありがとうございます.) - Zsh 周りの設定ファイルの更新.( @GeneralD さんコメントありがとうございます.)
-
path_helper
への対策を/etc/zprofile
の手動コメントアウトからsetopt GLOBAL_RCS
に変更; - 配列変数
$path
,$manpath
の利用; -
typeset -U
の利用; -
(N-/)
の利用.
-
- Bash 周りの設定ファイルの更新.
- awk 周りのコメントの更新.