Until the shell finds the command

Overview

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

Anyway strace

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.

Trace bash

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 not found

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

Do you look for it every time?

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.

Clear cache

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

Specify by absolute path

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

Confirmation environment

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

Until the shell finds the command
Linux [shell command]
the shell review
Shell script command replacement
About the service command
Install the pip command
Until the maximum likelihood estimation method finds the true parameter
Clone using the dd command
Speed up the netstat command
Check the HTTP status code response with the curl command (#Linux #Shell)
[Infrastructure] Linux command, shell script collection
Dive into the Django Custom command [1]
Run shell command / python in R
In the python command python points to python3.8
Make the default interactive shell IPython
[linux] kill command to kill the process
Have python read the command output
Using cgo with the go command
Hit the top command with htop
Shell script numerical calculation bc command
Command for the current directory Python
I want to leave an arbitrary command in the command history of Shell