grasys blog
grasysブログ

macOS path_helperと格闘する

こんにちは長谷川です。

みなさんはmacOSでどのterminalを使っていますか?
私は最近warpというterminalを使っています。
まだ個人的な使い方としてbashだからかあちこちかゆい感じなのですが、未来な感じのterminalで気に入っています。zshならめっちゃいいのかも
といいつつも、補完機能が邪魔でtmuxごしに触っています。(意味ないけど🤣)
ただiTerm2は安定してるし、alacrittyはシンプルで良い。

iTerm2は言わずもがなですが、alacrittyはconfigで設定するのでunixっぽくて好き。
warpは新しいし、ちょっとおもしろいので使ってます。飽きたら変えちゃうかもしれないけど。
Hyperはどうしても画面をキラキラバシバシさせちゃうので、Installもして設定もしてあるけど触らないようにしています😆
もうちょっとwarp使ってみて飽きたらalacrittyiTerm2に戻ると思います・・・🤣
ちなみにどうでもいいんだけど全部dracula theme🧛‍♂️です。

path_helper

macOSでbashなりzshなりでtmuxを使うとpath_helper問題に直面します。
他にもいっぱい局面があると思う。

Google Search – path_helper
みんな似たような問題で悩んでるなぁ・・・

さて本題のmacosのpath_helperですが/etc/profileを確認するとわかります。

bat -p /etc/profile
# System-wide .profile for sh(1)

if [ -x /usr/libexec/path_helper ]; then
    eval `/usr/libexec/path_helper -s`
fi

if [ "${BASH-no}" != "no" ]; then
    [ -r /etc/bashrc ] && . /etc/bashrc
fi

この3~5行目のこいつです。
man(叩いてみたらあった😆)を見てみると・・・

man path_helper
path_helper(8)                                                     System Manager's Manual                                                    path_helper(8)

NAME
     path_helper – helper for constructing PATH environment variable

SYNOPSIS
     path_helper [-c | -s]

DESCRIPTION
     The path_helper utility reads the contents of the files in the directories /etc/paths.d and /etc/manpaths.d and appends their contents to the PATH and
     MANPATH environment variables respectively.  (The MANPATH environment variable will not be modified unless it is already set in the environment.)

     Files in these directories should contain one path element per line.

     Prior to reading these directories, default PATH and MANPATH values are obtained from the files /etc/paths and /etc/manpaths respectively.

     Options:

     -c      Generate C-shell commands on stdout.  This is the default if SHELL ends with "csh".

     -s      Generate Bourne shell commands on stdout.  This is the default if SHELL does not end with "csh".

NOTE
     The path_helper utility should not be invoked directly.  It is intended only for use by the shell profile.

Mac OS X                                                               March 15, 2007                                                               Mac OS X

/etc配下のファイルを読み込んでPATHMANPATHを上書きしてきます。
/etc配下の読み込むファイルは以下です。

https://github.com/sharkdp/bat
cat(1) clone with syntax highlighting and Git integration.

batはcatコマンドの進化版みたいなやつです。自分のmacosの中では自分でどうにかできるんでfunctionでcatを上書き(乱暴🤣)して使ってます。(お仕事の環境ではしてはいけません)

https://github.com/sharkdp/bat
yusukeh@yusuke-mbp ~ bash ❯ fd paths /etc/
/etc/manpaths
/etc/manpaths.d/
/etc/paths
/etc/paths.d/

https://github.com/sharkdp/fd
fd is a program to find entries in your filesystem. It is a simple, fast and user-friendly alternative to find. While it does not aim to support all of find‘s powerful functionality, it provides sensible (opinionated) defaults for a majority of use cases.

fdもbatと同じでfindの進化版。
めっちゃ高速。ちょっと正規表現の書き方に癖があるかもだけど悪くないしシンプルで好き。
ちょっとignoreされてるものとの関連でひっかかりが悪いことがあるので注意した方が良い。
findの使い方忘れそう🤣

https://github.com/sharkdp/fd

例えば/etc/pathsと/etc/paths.d/*は以下です。
結論としては以下がPATHの先頭に強制的に追加されます・・・🤬

bat -p /etc/paths
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

bat -p /etc/paths.d/*
/Library/Apple/usr/bin

例えば私のPATHは以下です。(ちなみに私は対策済みです。)

yusukeh@yusuke-mbp ~ bash ❯ printenv PATH | sd ":" "\n"
/Users/yusukeh/rerun
/Users/yusukeh/mo
/Users/yusukeh/google-cloud-sdk/bin
/Users/yusukeh/rust/bin
/Users/yusukeh/anyenv/bin
/Users/yusukeh/anyenv/envs/tfenv/bin
/Users/yusukeh/anyenv/envs/rbenv/shims
/Users/yusukeh/anyenv/envs/rbenv/bin
/Users/yusukeh/anyenv/envs/pyenv/shims
/Users/yusukeh/anyenv/envs/pyenv/bin
/Users/yusukeh/anyenv/envs/plenv/shims
/Users/yusukeh/anyenv/envs/plenv/bin
/Users/yusukeh/anyenv/envs/nodenv/shims
/Users/yusukeh/anyenv/envs/nodenv/bin
/opt/homebrew/opt/gnu-getopt/bin
/opt/homebrew/opt/coreutils/libexec/gnubin
/opt/homebrew/bin
/opt/homebrew/sbin
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
/Library/Apple/usr/bin

rerunmutachegoogle-cloud-sdkrustupanyenvhomebrewが入ってるのがわかってなんか恥ずかしい・・・

https://github.com/chmln/sd
sd – s[earch] & d[isplace]
sd is an intuitive find & replace CLI.

sedの進化版です。sedの使い方忘れそう🤣

https://github.com/chmln/sd

path_helperを実行すると・・・

/usr/libexec/path_helper -s
PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/Users/yusukeh/rerun:/Users/yusukeh/mo:/Users/yusukeh/google-cloud-sdk/bin:/Users/yusukeh/rust/bin:/Users/yusukeh/anyenv/bin:/Users/yusukeh/anyenv/envs/tfenv/bin:/Users/yusukeh/anyenv/envs/rbenv/shims:/Users/yusukeh/anyenv/envs/rbenv/bin:/Users/yusukeh/anyenv/envs/pyenv/shims:/Users/yusukeh/anyenv/envs/pyenv/bin:/Users/yusukeh/anyenv/envs/plenv/shims:/Users/yusukeh/anyenv/envs/plenv/bin:/Users/yusukeh/anyenv/envs/nodenv/shims:/Users/yusukeh/anyenv/envs/nodenv/bin:/opt/homebrew/opt/gnu-getopt/bin:/opt/homebrew/opt/coreutils/libexec/gnubin:/opt/homebrew/bin:/opt/homebrew/sbin"; export PATH;
MANPATH="/usr/share/man:/usr/local/share/man:/opt/homebrew/opt/coreutils/libexec/gnuman:/opt/homebrew/share/man"; export MANPATH;

もうなんとなくわかって頂けると思うんだけど・・・
path_helperが動いた後のPATHはこうなる。

/usr/libexec/path_helper -s | rg ^PATH | sd "PATH=|; export PATH;|\"" "" | sd ":" "\n"
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
/Library/Apple/usr/bin
/Users/yusukeh/rerun
/Users/yusukeh/mo
/Users/yusukeh/google-cloud-sdk/bin
/Users/yusukeh/rust/bin
/Users/yusukeh/anyenv/bin
/Users/yusukeh/anyenv/envs/tfenv/bin
/Users/yusukeh/anyenv/envs/rbenv/shims
/Users/yusukeh/anyenv/envs/rbenv/bin
/Users/yusukeh/anyenv/envs/pyenv/shims
/Users/yusukeh/anyenv/envs/pyenv/bin
/Users/yusukeh/anyenv/envs/plenv/shims
/Users/yusukeh/anyenv/envs/plenv/bin
/Users/yusukeh/anyenv/envs/nodenv/shims
/Users/yusukeh/anyenv/envs/nodenv/bin
/opt/homebrew/opt/gnu-getopt/bin
/opt/homebrew/opt/coreutils/libexec/gnubin
/opt/homebrew/bin
/opt/homebrew/sbin

おい、待てよ!!ってなる・・・
まあわかる。正常に動作するためにってのはよく分かる。
だけどちょっとこれはちょっと・・・

先頭に追加が良くないと思うんだよな
状況次第では環境変数が最初と最後にあったりとかするし・・・
system系のやつがあちこちにあるんだとしたら、staticなpathで実行するようにして貰って、Userの自由度を上げてほしいなぁ
保守性がってのはわかるけど・・・

Twitterとかみてると、/etcに追記しろって書いてる人がいる。
macosは/etcが/private/etcにsynlinkされてるから良いっちゃ良い。
あと、まあ自分で責任取れって話だろうからそれでいいんだろうけど乱暴だなと・・・
個人的には/etcにごりっと個人的な設定を入れるのは抵抗が・・・linuxとかで使いにくくなるし・・・
あと/etc/paths.dに配置しても現状のPATH踏襲された上で新たにPATHが追加されるだけなんで、解決にはならない。

どうでもいいんだけど
昔、shellscriptでコマンドはフルパスで書けってよく怒られたし怒った思い出が・・・🤣
そんでもって環境に手を付けられないポリシーの前提条件でSolaris, FreeBSD, Linuxでそれぞれバージョンも違ってて異なる環境でも動くshellscriptを書くって大変なことしてたなぁ・・・

解決策

https://github.com/yb66/path_helper

探してたらこんなの作ってる人がいて見つけた。
ちょっと使ってみよう。

cd
test ! -d local/src && mkdir -p local/src
cd local/src
git clone https://github.com/yb66/path_helper.git
cd path_helper

exe/path_helper --help
Usage: path_helper.rb [options]
    -p, --path [PATH]                To get a completely fresh PATH pass an empty string or nothing at all (the default).
                                       To append something to the generated path pass the current path, or a path you wish appended.
    -m, --man [MANPATH]              Run for man pages but perhaps read `man manpages` first
                                       See `path' instructions for argument options.
    -f, --dyld-fram [DYLD]           DYLD_FALLBACK_FRAMEWORK_PATH env var
                                       See `path' instructions for argument options.
    -l, --dyld-lib [DYLD]            DYLD_FALLBACK_LIBRARY_PATH env var
                                       See `path' instructions for argument options.
    -c, --c-include [C_INCLUDE]      C_INCLUDE_PATH env var
                                       See `path' instructions for argument options.
        --pc [PKG_CONFIG_PATH]       PKG_CONFIG_PATH env var
                                       See `path' instructions for argument options.
    -q, --quiet                      Quiet, no output
    -d, --debug                      Debug mode, even more output
        --setup                      Set up directory structure, add specific switches
                                       (see `--etc` `--lib` `--config`)
                                       if specific setups required.
                                       Else --setup own its own will do all of them.
        --dry-run                    Use in conjuction with --setup to see what would happen if you ran it.
        --[no-]etc                   This has two applications:
                                       1. In combination with any of:
                                         `--path`, `--man`, `--dyld-fram`, `--dyld-lib`, `--c-include`, `--pc`
                                         will add/remove it from consideration for building the path.
                                       2. When combined with --setup will set up /etc/paths..., erm, etcetera.
                                       The default is for it to be included.
        --[no-]lib                   Adds ~/Library/Paths to the search/build path
                                       Included by default on a Mac.
                                       See `--etc` for more.
        --[no-]config                Adds ~/.config/paths
                                       Included by default on Linux.
                                       See `--etc` for more.
        --version                    Print version
    -h, --help                       Show this message

–dry-run –setup –no-etc –no-lib –config

exe/path_helper --dry-run --setup --no-etc --no-lib --config
Created /Users/yusukeh/.config/paths/c_include_paths.d
Created /Users/yusukeh/.config/paths/c_include_paths
Created /Users/yusukeh/.config/paths/dyld_fallback_framework_paths.d
Created /Users/yusukeh/.config/paths/dyld_fallback_framework_paths
Created /Users/yusukeh/.config/paths/dyld_fallback_library_paths.d
Created /Users/yusukeh/.config/paths/dyld_fallback_library_paths
Created /Users/yusukeh/.config/paths/manpaths.d
Created /Users/yusukeh/.config/paths/manpaths
Created /Users/yusukeh/.config/paths/pkg_config_paths.d
Created /Users/yusukeh/.config/paths/pkg_config_paths
Created /Users/yusukeh/.config/paths/paths.d
Created /Users/yusukeh/.config/paths/paths


# Put this in your ~/.bashrc or your ~/.zshenv
if [ -x /Users/yusukeh/local/src/path_helper/exe/path_helper ]; then
  export C_INCLUDE_PATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper -c)
  export DYLD_FALLBACK_FRAMEWORK_PATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper --dyld-fram)
  export DYLD_FALLBACK_LIBRARY_PATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper --dyld-lib)
  export MANPATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper -m)
  export PKG_CONFIG_PATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper -pc)
  export PATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper -p)
fi

こうすれば~/.config配下つまりXDG_CONFIG_HOMEに配置される。
–etcは/etc配下だし、–libは~/Library配下だから嫌(めっちゃ個人的な感想だけど😅)
/etcと~/Libraryと~/.config全部有効化するのは意味ないのでやめたほうが良いかな
そもそもsystem-wideな設定は/etc/paths, /etc/paths.d, /etc/manpaths, /etc/manpaths.dにあるし、User Fieldでは~/Libraryか~/.configのどちらかのひとつで良いね。
実際にsetupするなら–dry-runを取って実行すればconfigのディレクトリツリーを構成してくれる。

config map

Environmentconfig & config directory
PATH~/.config/paths/paths
~/.config/paths/paths.d
MANPATH~/.config/paths/manpaths
~/.config/paths/manpaths.d
C_INCLUDE~/.config/paths/c_include_paths
~/.config/paths/c_include_paths.d
PKG_CONFIG_PATH~/.config/paths/pkg_config_paths
~/.config/paths/pkg_config_paths.d
DYLD_FALLBACK_LIBRARY_PATH~/.config/paths/dyld_fallback_library_paths
~/.config/paths/dyld_fallback_library_paths.d
DYLD_FALLBACK_FRAMEWORK_PATH~/.config/paths/dyld_fallback_framework_paths
~/.config/paths/dyld_fallback_framework_paths.d

configに直接書くよりかは、config directoryに対象の環境変数のを書いて対応した方が良さげかな?

README.mdみてると/usr/local/libexec配下に入れるとか~/bin配下にいれるみたいな感じだけどマニュアルで叩くものじゃないのでもうちょいどかしといた方が良い気はする。
まあ好みの問題だし好きにすれば良いかな

.bashrc

.bashrcに–setupで出力されたものを貼り付ければ
関係する環境変数に関しては設定でmacos標準のpath_helperに影響を受けず再構成できるようになる。
下記のは自分のmacosの環境とUserのものなのでこのまま貼り付けちゃダメです。

# Put this in your ~/.bashrc or your ~/.zshenv
if [ -x /Users/yusukeh/local/src/path_helper/exe/path_helper ]; then
  export C_INCLUDE_PATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper -c)
  export DYLD_FALLBACK_FRAMEWORK_PATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper --dyld-fram)
  export DYLD_FALLBACK_LIBRARY_PATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper --dyld-lib)
  export MANPATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper -m)
  export PKG_CONFIG_PATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper -pc)
  export PATH=$(ruby /Users/yusukeh/local/src/path_helper/exe/path_helper -p)
fi

ex) PKG_CONFIG_PATH & HOMEBREW

PKG_CONFIG_PATHがあるからpkg-configファイルがあることが前提だけど
/opt/homebrew/lib/pkgconfigにパス通したりはこんな感じでできる。

echo /opt/homebrew/lib/pkgconfig >> ~/.config/pkg_config_paths.d/10-homebrew

先頭の数字は読み込ませたい順番

感想

CFLAGS, LDFLAGSとかも対応してるといいなとは思う。
自分は環境をぐりっと調査して.bashrcを自動生成している。
自前で似たような処理を書いて、/etc/profile読み込んだ後でも.bashrcで改めて初期化する形で対応してる。
さっき紹介したのはrubyだけどオレのはbash。
bashなだけあってそれなりに苦労😂

/etc/paths, /etc/paths.d, /etc/manpaths, /etc/manpaths.d読み込んだ後で
以下を読み込んで順番通りに環境変数を整理してexportする。
事前の環境変数は破棄して再構成してる。

cd .config/path_helper && exa --tree
.
├── c_include_paths
├── c_include_paths.d
├── dyld_fallback_framework_paths
├── dyld_fallback_framework_paths.d
├── dyld_fallback_library_paths
├── dyld_fallback_library_paths.d
├── dyld_library_paths
├── dyld_library_paths.d
├── include_paths
├── include_paths.d
│  ├── 09-usr_local
│  └── 10-homebrew
├── ld_library_paths
├── ld_library_paths.d
├── lib_paths
├── lib_paths.d
│  ├── 09-usr_local
│  └── 10-homebrew
├── manpaths
├── manpaths.d
│  ├── 09-usr_local
│  └── 10-homebrew
├── paths
├── paths.d
├── pkg_config_paths
└── pkg_config_paths.d
   └── 10-homebrew

https://github.com/ogham/exa
exa is a modern replacement for ls.

lsの進化版でHack Nert Fontでiconとかつけられる。おしゃれにしたいならこれ😆

https://github.com/ogham/exa

みなさんはどう対応してますか?