概要
bashがPATH環境変数に応じてコマンドを探す様子を見てみます。
yoichinakayama@penguin:~$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/home/yoichinakayama/.local/bin
yoichinakayama@penguin:~$ type hostname
hostname is /bin/hostname
yoichinakayama@penguin:~$ hostname
penguin
なにはともあれ strace
strace すると
yoichinakayama@penguin:~$ strace hostname 2>&1 |head -1
execve("/bin/hostname", ["hostname"], 0x7fe429adf0 /* 31 vars */) = 0
と /bin/hostname
が実行されていることはわかりますが、実行されたところからのトレースしか見られず、これだとコマンドを探す様子は見られません。
bash をトレース
yoichinakayama@penguin:~$ bash -c "hostname"
penguin
とbashを起動した上で実行するようにして、起動したbashをトレースすれば、コマンドを探す様子を見ることができます。
yoichinakayama@penguin:~$ strace -f bash -c "hostname" 2>&1 |grep "\(execve\|fstatat\)"
...
newfstatat(AT_FDCWD, "/usr/local/bin/hostname", 0x7ff2d34128, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/bin/hostname", 0x7ff2d34128, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/bin/hostname", {st_mode=S_IFREG|0755, st_size=18440, ...}, 0) = 0
...
execve("/bin/hostname", ["hostname"], 0x31c1b008 /* 31 vars */) = 0
PATH環境変数の値 1 に沿って、
- /usr/local/bin/hostname → -1: 無い (ENOENT)
- /usr/bin/hostname → -1: 無い (ENOENT)
- /bin/hostname → 0: 見つかった
と見つかるまで順に探していき、見つかったものを実行していることが見れます。
見つからない場合
存在しないコマンドを指定すると
yoichinakayama@penguin:~$ bash -c "hoge"
bash: hoge: command not found
とエラーになります。この場合は
yoichinakayama@penguin:~$ strace bash -c "hoge" 2>&1 |grep fstatat| grep hoge
newfstatat(AT_FDCWD, "/usr/local/bin/hoge", 0x7ff32941e8, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/bin/hoge", 0x7ff32941e8, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/bin/hoge", 0x7ff32941e8, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/local/games/hoge", 0x7ff32941e8, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/games/hoge", 0x7ff32941e8, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/home/yoichinakayama/.local/bin/hoge", 0x7ff32941e8, 0) = -1 ENOENT (No such file or directory)
とPATH環境変数を探しきったあとに、エラーメッセージを出力しています。
yoichinakayama@penguin:~$ strace bash -c "hoge" 2>&1 |grep "command not found"
write(2, "bash: hoge: command not found\n", 30bash: hoge: command not found
毎度探すの?
bash は実行したコマンドのパスをキャッシュしています。
yoichinakayama@penguin:~$ type hostname
hostname is /bin/hostname
yoichinakayama@penguin:~$ hostname
penguin
yoichinakayama@penguin:~$ type hostname
hostname is hashed (/bin/hostname)
yoichinakayama@penguin:~$ hostname
penguin
この動作も見てみましょう。
yoichinakayama@penguin:~$ strace -f bash -c "hostname; hostname" 2>&1 |grep "\(execve\|fstatat\)"
...
newfstatat(AT_FDCWD, "/usr/local/bin/hostname", 0x7febd16b68, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/bin/hostname", 0x7febd16b68, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/bin/hostname", {st_mode=S_IFREG|0755, st_size=18440, ...}, 0) = 0
newfstatat(AT_FDCWD, "/bin/hostname", {st_mode=S_IFREG|0755, st_size=18440, ...}, 0) = 0
newfstatat(AT_FDCWD, "/bin/hostname", {st_mode=S_IFREG|0755, st_size=18440, ...}, 0) = 0
newfstatat(AT_FDCWD, "/bin/hostname", {st_mode=S_IFREG|0755, st_size=18440, ...}, 0) = 0
newfstatat(AT_FDCWD, "/bin/hostname", {st_mode=S_IFREG|0755, st_size=18440, ...}, 0) = 0
newfstatat(AT_FDCWD, "/bin/hostname", {st_mode=S_IFREG|0755, st_size=18440, ...}, 0) = 0
[pid 2555] execve("/bin/hostname", ["hostname"], 0xc2b9008 /* 31 vars */) = 0
[pid 2556] execve("/bin/hostname", ["hostname"], 0xc2b9008 /* 31 vars */) = 0
二回目は探さずに実行していることがわかります。
キャッシュのクリア
hash -r
すると再度探しに行くことが確認できます。
yoichinakayama@penguin:~$ strace -f bash -c "hostname; hash -r; hostname" 2>&1 |grep "\(execve\|fstatat\)"
...
newfstatat(AT_FDCWD, "/usr/local/bin/hostname", 0x7fc0d8aa08, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/bin/hostname", 0x7fc0d8aa08, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/bin/hostname", {st_mode=S_IFREG|0755, st_size=18440, ...}, 0) = 0
...
[pid 2598] execve("/bin/hostname", ["hostname"], 0x110de008 /* 31 vars */) = 0
...
newfstatat(AT_FDCWD, "/usr/local/bin/hostname", 0x7fc0d8aba8, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/bin/hostname", 0x7fc0d8aba8, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/bin/hostname", {st_mode=S_IFREG|0755, st_size=18440, ...}, 0) = 0
...
[pid 2599] execve("/bin/hostname", ["hostname"], 0x110de008 /* 31 vars */) = 0
絶対パスでの指定
コマンドを絶対パスで指定すると、PATHに沿って探すことはせずにコマンドを実行します。
yoichinakayama@penguin:~$ strace -f bash -c "/bin/hostname" 2>&1 |grep "\(execve\|fstatat\)"
execve("/bin/bash", ["bash", "-c", "/bin/hostname"], 0x7fec75a6c8 /* 31 vars */) = 0
newfstatat(AT_FDCWD, "/home/yoichinakayama", {st_mode=S_IFDIR|0755, st_size=622, ...}, 0) = 0
newfstatat(AT_FDCWD, ".", {st_mode=S_IFDIR|0755, st_size=622, ...}, 0) = 0
newfstatat(AT_FDCWD, "/home", {st_mode=S_IFDIR|0755, st_size=28, ...}, 0) = 0
newfstatat(AT_FDCWD, "/home/yoichinakayama", {st_mode=S_IFDIR|0755, st_size=622, ...}, 0) = 0
newfstatat(AT_FDCWD, ".", {st_mode=S_IFDIR|0755, st_size=622, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/local/bin/bash", 0x7fff5c3fd8, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/bin/bash", 0x7fff5c3fd8, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/bin/bash", {st_mode=S_IFREG|0755, st_size=1016024, ...}, 0) = 0
newfstatat(AT_FDCWD, "/bin/bash", {st_mode=S_IFREG|0755, st_size=1016024, ...}, 0) = 0
newfstatat(AT_FDCWD, "/bin/bash", {st_mode=S_IFREG|0755, st_size=1016024, ...}, 0) = 0
newfstatat(AT_FDCWD, "/bin/bash", {st_mode=S_IFREG|0755, st_size=1016024, ...}, 0) = 0
newfstatat(AT_FDCWD, "/bin/bash", {st_mode=S_IFREG|0755, st_size=1016024, ...}, 0) = 0
newfstatat(AT_FDCWD, "/bin/bash", {st_mode=S_IFREG|0755, st_size=1016024, ...}, 0) = 0
execve("/bin/hostname", ["/bin/hostname"], 0x3d55e008 /* 31 vars */) = 0
確認環境
Chromebook のターミナル上で確認しました。
yoichinakayama@penguin:~$ uname -a
Linux penguin 5.4.39-04075-gfc7cb60d7f13 #1 SMP PREEMPT Sun May 10 10:47:48 PDT 2020 aarch64 GNU/Linux
yoichinakayama@penguin:~$ bash --version
GNU bash, version 4.4.12(1)-release (aarch64-unknown-linux-gnu)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
-
値は
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/home/yoichinakayama/.local/bin
でした ↩