Let's see how bash looks for commands according to the PATH environment variable.
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
yoichinakayama@penguin:~$ strace hostname 2>&1 |head -1
execve("/bin/hostname", ["hostname"], 0x7fe429adf0 /* 31 vars */) = 0
And you can see that / bin / hostname
is running, but you can only see the trace from where it was run, and you can't see it looking for a command.
yoichinakayama@penguin:~$ bash -c "hostname"
penguin
If you start bash and then execute it, and trace the started bash, you can see how it looks for the command.
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
According to the value of the PATH environment variable [^ 1],
[^ 1]: The value was / usr / local / bin: / usr / bin: / bin: / usr / local / games: / usr / games: /home/yoichinakayama/.local/bin
You can see that you are executing what you find by searching in order until you find it.
If you specify a command that does not exist
yoichinakayama@penguin:~$ bash -c "hoge"
bash: hoge: command not found
Will result in an error. in this case
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)
After searching for the PATH environment variable, an error message is output.
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 caches the path of the executed command.
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
Let's see this behavior as well.
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
You can see that the second time it is running without searching.
You can see that you can go find it again by doing 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
If you specify the command with an absolute path, the command is executed without searching along the 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
I checked it on the Chromebook terminal.
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.
Recommended Posts