0

I am reading Sobell's A Practical Guide to Linux Commands, Editors, and Shell Programming, 4e, and I am currently starting Chapter 5 on the basics of shells.

On page 135 Sobell explains that

From the command line, there are three ways you can specify the name of a file you want the shell to execute: as an absolute pathname (starts with a slash [/]; page 90), as a relative pathname (includes a slash but does not start with a slash; page 91), or as a simple filename (no slash).

Further, he writes that

When you specify a simple filename (no slash), the shell searches through a list of directories for a filename that matches the specified name and for which you have execute permission. The shell does not look through all directories but only the ones specified by the variable named PATH.

Importantly, he notes that the working directory is generally not part of PATH.

My question is therefore about understanding how (why?) the shell seems to behave differently based on whether a simple filename is passed as a command versus an argument to a command.

Up until this point in the book, I have understood simple filenames to simply be a special case of relative paths wherein we don't need a slash because the given file is directly a child of the working directory. Thus, for example, if I have a file named foo in my home directory and I am in my home directory, then

ls -l foo

will give me detailed information about foo (I have passed foo, a simple filename, as an argument to ls).

Now it seems that the behavior is different with respect to commands which are themselves programs/files/executables (you'll forgive my naivete and lack of precision here I hope). When I simply give the following to the command line, Sobell seems to be saying that the shell looks through directories in the PATH and not through the working directory necessarily (unless that is part of PATH):

ls

That is, in the specific case that a simple filename is given as a command rather than as an argument, the behavior of the shell in terms of where it looks is different. Is this understanding correct?

Kusalananda
  • 333,661
EE18
  • 233
  • Note that Sobell is exclusively talking about a file that you want to execute. In your two examples, this is ls. In the first example, foo is an argument and completely uninvolved in the finding of the ls file which is what the command is wanting to execute. – Kusalananda Jan 25 '24 at 19:59
  • Related: (POSIX) Command Search and Execution (e. Otherwise, the command shall be searched for using the PATH environment variable as described in XBD ... i. If the search is successful: ... b. ... the shell executes the utility in a separate utility environment (see Shell Execution Environment) with actions equivalent to calling the execl() function Also see: 8.3 Other Environment Variables (PATH). – Vilinkameni Jan 25 '24 at 20:12
  • Most shells require to use relative or absolute paths when executing executables located in the directory not in path. Example: if $HOME/src/hello-0.1 is not in PATH, an executable hello inside of that directory needs to be started with the command ./hello if the current directory is $HOME/src/hello-0.1, or hello-0.1/hello if $HOME/src is current directory, or $HOME/src/hello-0.1/hello in general. Shells with tilde expansion allow tilde (~) to be entered instead of $HOME. – Vilinkameni Jan 25 '24 at 20:20
  • Including also this (https://unix.stackexchange.com/questions/768345/what-happens-when-i-give-the-shell-a-command-versus-a-script/768354#768354) question and answer of mine as a useful link for the future. – EE18 Feb 04 '24 at 22:52

1 Answers1

2

Yes. Your understanding is correct.

You can see this if you move to the /tmp directory and create a text file containing the word 'text':

cd /tmp
echo 'text' > file.txt

Now these commands are all interchangeable:

cat /tmp/file.txt
cat ./file.txt
cat file.txt

Each of these commands will display the text contents of the file.

But! If you now create an executable file:

cd /tmp
printf '#!/usr/bin/env bash\n\necho "hi"\n' > executable
chmod a+x executable

You can still access the file as before, since it is still a file:

cat /tmp/executable
cat ./executable
cat executable

But, in the Bash shell, you can only RUN it like this:

/tmp/executable
./executable

You cannot run the file like this:

executable

That would cause the shell to search the various $PATH locations for a matching executable. Since there are none, the shell will give up and return an error 127 ("Command 'executable' not found").

Why should it work this way?

This is a conscious security decision! If a hacker were to place an executable file in your /tmp directory with a name matching one of the everyday shell commands (e.g. the ls command), then you could wind up running a malicious command without even knowing it.

The solution is to have the shell execute only those trusted commands that you have installed in a $PATH location.

If there's ever a time where you do want to run a one-off script from your current working directory, you can still do that by specifying the path to the executable (e.g. ./executable or /tmp/executable). By providing that extra path information, you're communicating that you do indeed wish to run the executable at that location, and were not tricked into doing it!

ilkkachu
  • 138,973
Andy
  • 86
  • 1
    It's trivial to add . to the end of the set of directories referenced by $PATH. The issue is that it opens a rather unpleasant security hole, so it's never recommended – Chris Davies Jan 25 '24 at 21:15
  • I see. Thank you, Chris. – Andy Jan 25 '24 at 21:17
  • You can make your shell work that way: just set PATH=".:$PATH" and commands will be searched in your current directory too. You should ask yourself though whether that is wise. Imagine you are in a directory where someone else has dropped an executable script called ls, and it contains rm -rf $HOME. This is one of the good reasons for not having the current directory on the path. MS DOS chose to have the current directory irremovably on the path and also had no file permissions. WCGW? – zwets Jan 25 '24 at 21:21
  • 1
    @zwets Put . at the end of $PATH if you must include it at all – Chris Davies Jan 25 '24 at 21:24
  • @ChrisDavies I surely wouldn't, I've been on MS DOS :) – zwets Jan 25 '24 at 21:32
  • @zwets just because DOS gets it badly wrong there's no reason to continue that poor advice here – Chris Davies Jan 25 '24 at 23:05
  • @ChrisDavies my comment doesn't give the advice to add . to the path, rather the perfect opposite. But if you think it could be misread as advice, then I'm happy to remove it. Ah, now I see: with "I surely wouldn't" I meant I'd never add it anywhere in my path, for the reason stated in my comment. – zwets Jan 25 '24 at 23:16
  • All right, guys! I've updated the answer to reflect your discussion. Thank you for pointing out the dangers. – Andy Jan 26 '24 at 00:22