Help us understand the problem. What is going on with this article?

テキスト処理のための標準的なコマンド群の macOS への導入手順

前置き

想定読者

想定読者:テキスト処理をしようと思って手元の 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 系コマンドを中心とした標準的なテキスト処理用のコマンド群をインストールする手順 を示します.

# 「ここの記述が不正確だぞ」「もっと効率の良い・問題の起きづらい導入法あるよ」「これも入れるべき」など突っ込みどころがありましたら是非コメントをお寄せください.

想定環境

  • OS: El Capitan (10.11) 以後
  • Shell: Bash, Zsh

※ 使っているシェルが 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
  • エディタ
    • nanoemacs および vim は扱いません)
  • ファイルシステム
    • ls, mv, etc.
  • ほか
    • echo, etc.

取り扱わないコマンド

  • バイナリファイル処理・低レベル言語の処理
  • ネットワーク・通信
  • プロセス・ジョブ管理
  • バージョン管理
  • シェル(そのもの)
  • ターミナルマルチプレクサ
  • ほか

主な参考サイト

導入手順

1. シェルの設定ファイルの調整

  1. ~/.bash_profile~/.bashrc(Bash の場合),または ~/.zshenv~/.zshrc(Zsh の場合)に以下を追記します.ファイルが存在しない場合は作成してください.

    ~/.bash_profile
    export 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
    
    ~/.zshenv
    export 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
    
  2. $ 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 を通す

  1. ~/.bash_profile(Bash の場合)または ~/.zshenv (Zsh の場合)に以下を追記します.

    ~/.bash_profile
    # homebrew
    PATH=/usr/local/bin:/usr/local/sbin:${PATH}
    MANPATH=/usr/local/share/man:${MANPATH}
    
    ~/.zshenv
    path=(
        /usr/local/bin(N-/) # homebrew
        /usr/local/sbin(N-/) # homebrew
        ${path}
    )
    manpath=(
        /usr/local/share/man(N-/) # homebrew
        ${manpath}
    )
    
  2. $ 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 coreutilsGNU 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.zshrcalias nawk=/usr/bin/awk としておいても良いでしょう.

path を通す

  1. ~/.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}
    
    ~/.zshenv
    path=(
        /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}
    )
    
  2. $ 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 は起動時に次の順番で設定ファイルを読み込みます
    1. /etc/profile (ログインシェルの場合)
    2. /etc/bashrc.macOS の場合に /etc/profile から呼び出されます.)
    3. ~/.bash_profile, ~/.bash_login, ~/.profile (ログインシェルの場合)(最初に見つかったものがひとつだけ実行されます.)
    4. ~/.bashrc (インタラクティブシェルの場合)
  • path などの設定は ~/.bash_profile で,インタラクティブシェルの場合に限った設定(alias やプロンプトなど)は ~/.bashrc でをおこなえば良いように見えますが,いくつかの問題があります.
  • まず,ログインシェルは通常インタラクティブに利用されるにも関わらず,Bash でログインした際にインタラクティブシェルのための設定ファイル ~/.bashrc は自動では読み込まれません.そこで,Bash の公式リファレンスにもある通り~/.bash_profile の末尾で ~/.bashrc を呼び出すようにします.
  • また,インタラクティブシェルの場合に(~/.bash_profile から呼び出された)~/.bashrc の設定が読み込まれないよう,~/.bashrc の冒頭で分岐を入れます.これは,/etc/bashrc の冒頭に元から書かれている分岐と全く同じ目的・書きかたです.

Zsh の設定ファイルの調整が必要な理由

  • Zsh は起動時に次の順番で設定ファイルを読み込みます
    1. /etc/zshenv ※ macOS には存在しません.
    2. ~/.zshenv
    3. /etc/zprofile (ログインシェルの場合)
    4. ~/.zprofile (ログインシェルの場合)
    5. /etc/zshrc (インタラクティブシェルの場合)
    6. ~/.zshrc (インタラクティブシェルの場合)
    7. /etc/zlogin (ログインシェルの場合) ※ macOS には存在しません.
    8. ~/.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 が付きます.たとえば lsgls としてインストールされます.
  • /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 周りのコメントの更新.
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした