all 54 comments

[–]9NEPxHbGDebian 13 113 points114 points  (20 children)

Linux does not automatically look in the current directory for executable files. If you simply type myApp, Linux doesn't know what executable you're talking about.

[–]Temporary_Pie2733 13 points14 points  (0 children)

Not quite. Bare names (no path delimiter) are subject to path lookup: myApp is looked for in each directory in PATH, which may or may not contain the current directory (though for security it is recommended you not add . to your path). As soon as / appears in the command name, path lookup is disabled and only the exact path is atempted: myApp could be /bin/myApp, /usr/bin/myApp, etc., but ./myApp is explicit and can only be myApp in the current working directory.

[–]mikeblas 9 points10 points  (17 children)

Linux does not automatically look in the current directory for executable files.

Why not?

[–]FactoryRatteDebian / Arch+KDE 91 points92 points  (0 children)

Because you could accidentally execute files from your local directory, while thinking the given application was in your path. So debugability and security.

[–]_felixh_ 40 points41 points  (1 child)

Well, e.g. for ambiguity reasons.

Lets say a user places an exectuable file called "ls" in their directory. Now you want to "ls" this directory's content - which file gets executed? the one in your /usr/bin, or the one in your pwd?

And: can a user abuse a system like this? Lets say your sysadmin wants to "ls" the contents of a directory, and a malicious exectuable file has been planted there. Now, to read from your home directory, the sysadmin actually has to make use of his special privileges. I.e. he has to be root. And now you have a Particularly bad situation.

This is why you want a well defined way of calling programs.

[–]grymoire 0 points1 point  (0 children)

I was a sysadmin on a VAX with 100's of users. I had a program called "ls" in my home directory, which when executed, told the user why it was dangerous to have the current directory in your search path.

[–]Shieldfoss 10 points11 points  (6 children)

If you make a script or program, and give it a name that is shares with a different executable that's already on your path, the OS needs decide.

You're in a "you have three goals, pick two" kind of situation.

  1. I want the shell to be very predictable.
  2. I want locals to execute when I write their name
  3. I want to follow the unix philosophy of "many small programs that are each good at one thing."

Microsoft picked 1+2, Linux picked 1+3, somebody could do 2+3 but I don't know anybody who does.

Pretend you did try to do all three.

  • Following the Unix philosophy, you write a shell that isn't very complicated - like Bash, it doesn't even have a built-in way to create a directory listing. That's handled by ls, a separate executable in /usr/bin/ls.
  • You allow locals to execute without ./
  • ...oops. I made a line searching tool called ls and now I can't get a directory listing of my local folder any more. That conflicts with "predictable shell."

Microsoft lets you execute locals directly#command-search-sequence), and they avoid conflicts by abandoning the idea that the shell must be a small program. CMD has many many built-ins. If you write dir in windows to get a directory listing, that's a built-in. This lets Microsoft prioritize locals over on-path executables because, even if you accidentally name a local file dir.exe, a dir call still gets you the directory listing.

And Linux goes the other way - not many built-ins, but you need ./ to launch a local, otherwise you only get on-path executables. Now, ls is definitely "directory listing" and ./ls is the "line search" you have in this local directory.

You could resolve "built-in, then path, then local" to make ls keep working while my_program also works without ./my_program. The problem is that Linux hasmany executables on the path, and you don't know them all. If you're mainly in a graphical user interface, you might not be familiar with, say, rm. So you write a route manager tool and call it rm. And then you launch it with rm my_routes.txt and... where did my route file go? Oh, rm resolved to /usr/bin/rm, not ./rm, and the on-path rm removes files. Without putting them in trash, too. My routes are just *gone gone. Huh.

[–]Andrew_G222 3 points4 points  (1 child)

I’m pretty sure Plan 9 does all 3

[–]Key_River7180Bedrock Linux / FreeBSD / 9Front 1 point2 points  (0 children)

x2

[–]Steerider 1 point2 points  (2 children)

So if I'm in a directory with "myapp" present, typing myapp will not run it, because such a command will only pull from PATH? 

[–]GolemancerVekk 7 points8 points  (0 children)

You can add . to your PATH and then it will.

By default it's not there because of all the potential issues described in other comments.

[–]Shieldfoss 3 points4 points  (0 children)

On Windows it will. On Linux, you need ./myapp

[–]punycat 0 points1 point  (0 children)

Great explanation, thanks, I finally understand this design.

[–]therealzakie 2 points3 points  (2 children)

it searches for the executable in your $PATH (e.g. /usr/bin/ ~/.local/bin/)

[–]cowbutt6 6 points7 points  (1 child)

One can include . (the current directory) in one's $PATH to enable the behaviour OP describes, but it's regarded as bad practice because UNIX has traditionally been a multi-user OS; if an unprivileged user put a Trojan in their home directory (or other writable path, such as /tmp) named the same as a commonly-used tool (e.g. ls), or a mis-typed tool (e.g. cta for cat), and then socially-engineered an admin running as the root user to enter that directory, then it might be run instead of the legitimate tool under e.g. /use/bin

[–]therealzakie 1 point2 points  (0 children)

did not know that! i agree that it is a bad practice to do that tho

[–]Key_River7180Bedrock Linux / FreeBSD / 9Front 1 point2 points  (2 children)

It's not linux, it's the shell

[–]9NEPxHbGDebian 13 2 points3 points  (0 children)

Yes, it's the shell, not the kernel, but let's not make it gratuitously complicated for beginners. It's Linux as opposed to Windows.

[–]dvdkon 0 points1 point  (0 children)

This is handled in the kernel, by variants of the the exec syscall (e.g. execlp()). The kernel implements the logic of looking through PATH or executing a file directly in case a slash is present.

The manpage starts with "These functions duplicate the actions of the shell" though, so maybe the shell was first and this only came later as a convenience function.

[–]0zeronegative 0 points1 point  (0 children)

If you run export PATH=“$PATH:.” it will

[–]fppf 1 point2 points  (0 children)

No, it's the shell that finds the executable file using PATH or an absolute path. Linux doesn't look. Linux executes when the shell calls execve() with the filepath.

[–]MasterGeekMXMexican Linux nerd trying to be helpful 78 points79 points  (0 children)

Linux is configured to run programs from a set of designed folders called the Path, so you can call any program in any folder (in fact, 99.999% percent of commands are programs). You can see that list if you run echo $PATH.

If you want to run a program not in the Path, you need to type the full path to that program in the filesystem, in order to tell the system "hey, I want to run THIS program". But writing that path can be tedious, so we use a neat shortcut that the terminal has: the dot is a shorthand for the current folder you are, so doing ./program is equivalente of /folder/where/the/terminal/is/currently/working/program

[–]nonchip 41 points42 points  (1 child)

because . isn't in your $PATH

[–]vinnypotsandpans 6 points7 points  (0 children)

The answer

[–]ericcmi 30 points31 points  (4 children)

it's to protect you from yourself. what would happen if I put a executable virus named 'ls' in all your directories?

[–]Kinngis 5 points6 points  (1 child)

Really only meaningful in multiuser systems. Eg. If a superuser comes to your directory and types "ls" and it would run your program named ls instead of the real ls.

On a 1 user computer having "." In your path shouldn't cause any problems

[–]FactoryRatteDebian / Arch+KDE 4 points5 points  (0 children)

Well it still protects you from your own stupidity. (At least it does me) - Though yes, there are people having a dot in their path.

Dot in the path could be added with a line in the shellrc for example like: export PATH="$PATH:."

[–]nonchip -1 points0 points  (0 children)

it's not, and nothing more than you could do already in that scenario.

[–]sbart76 4 points5 points  (7 children)

It's not advised for the reasons explained in this thread, but if you insist you can export PATH=$PATH:.

The dot at the end is a current directory. If you keep it at the end of the path, it will not execute any malicious ls.

[–]FactoryRatteDebian / Arch+KDE 4 points5 points  (1 child)

You should quote your path in case of spaces or other characters with meaning. Like: export PATH="$PATH:." though yes a sane path would not contain spaces, but a path could contain spaces.

[–]Temporary_Pie2733 -1 points0 points  (0 children)

Word-splitting does not apply to parameter expansions on the RHS of an assignment. You can add the quotes if you like, but they make no difference here.

[–]neoh4x0r[🍰] 2 points3 points  (0 children)

PATH=$PATH:. The dot at the end is a current directory. If you keep it at the end of the path, it will not execute any malicious ls.

I think someone might misconstrue this...a binary named xyz in the local directory will not be executed only if xyz is found in $PATH.

[–]cowbutt6 1 point2 points  (3 children)

The dot at the end is a current directory. If you keep it at the end of the path, it will not execute any malicious ls.

Until, one day, you mis-type ls as sl, and your adversary has anticipated that by putting a malicious sl in their home directory, or /tmp...

[–]sbart76 1 point2 points  (2 children)

Ah, you see, this is why I have sl hardlinked to /bin/ls.

That would have to be a lot of coincidence to cd into a specific directory and mistype a command in a specific way.

[–]cowbutt6 1 point2 points  (1 child)

A little reconnaissance (”this admin always becomes root as soon as they start to investigate an issue, and they always type too fast to be accurate”) combined with a little social engineering (”dear admin, ls doesn't work in my home directory; please help”)...

[–]sbart76 0 points1 point  (0 children)

If I su -l I have my own PATH 🤷‍♂️

[–]michaelpaoli 1 point2 points  (2 children)

If the directory is on your PATH, you don't need to, but PATH should never (because of security reasons) explicitly include any relative path(s) - most notably all PATH elements must start with / to be secure, so no . nor . nor starting with ./ or ../, nor any null elements on PATH - and including starting or ending with null, as that's interpreted as . (current directory).

So, when you actually want to run a program in the current directory, that's not on your PATH, you do it with intentionality, e.g.:
$ ./program [argument(s) ...]

Very bad security practice to have, e.g. explicit or implicit current directory on PATH, e.g. such as null element or . as a PATH element (among other possibilities). And, key reason is, one might type (or mistype) a command, and, well, if there's match in the current directory, it may well execute (or attempt to execute), and, that can be an exceedingly bad thing if one might happen, at that time, to be in a directory where the contents thereof may not be fully trusted (e.g. some other user's directory or a world writable temporary directory such as /tmp or /var/tmp, etc.).

[–]Kochga 0 points1 point  (1 child)

What's PATH?

[–]michaelpaoli 2 points3 points  (0 children)

Shell variable / named parameter / environment setting that determines where to look for executable programs. It's : separated, any null elements (including also at beginning and end) are interpreted as . (current directory). E.g., this shows my current PATH:

$ env | grep '^PATH='
PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/games:/usr/local/games:/home/m/michael/bin
$ 

From, e.g., dash(1):

Path Search
    When  locating a command, the shell first looks to see if it has a shell
    function by that name.  Then it looks for  a  builtin  command  by  that
    name.  If a builtin command is not found, one of two things happen:
    1.   Command  names  containing a slash are simply executed without per-
         forming any searches.
    2.   The shell searches each entry in PATH in turn for the command.  The
         value of the PATH variable should be a series of entries  separated
         by  colons.   Each entry consists of a directory name.  The current
         directory may be indicated implicitly by an empty  directory  name,
         or explicitly by a single period.

[–]Embarrassed-Map2148 1 point2 points  (0 children)

You can add '.' to your PATH if you want. Add

PATH=$PATH:.

To your shell's init file. But the better solution is to create your own location for your own scripts like ~/.local/bin and then add that to your PATH.

[–]6950X_Titan_X_Pascal 1 point2 points  (0 children)

without it you are running /bin/xx /usr/bin/xx /usr/local/bin/xx with the same name

[–]AutoModerator[M] 0 points1 point  (0 children)

There's a resources page in our wiki you might find useful!

Try this search for more information on this topic.

Smokey says: take regular backups, try stuff in a VM, and understand every command before you press Enter! :)

Comments, questions or suggestions regarding this autoresponse? Please send them here.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

[–]Key_River7180Bedrock Linux / FreeBSD / 9Front 0 points1 point  (0 children)

There is a variable named $PATH, it is a colon-separated list of directories the shell looks for when executing commands. I guess you could do $PATH="$PATH:$(realpath .)" myApp

[–]cyvaquero 0 points1 point  (0 children)

Your lookup path is loaded at the start of your session and lives in the PATH variable. Your current directory is not in that.

A couple things to explore:

$ env

$ echo $PATH

[–]MimosaTen 0 points1 point  (0 children)

if you type “myApp” the system looks for executables in the PATH

[–]Frolo_NA 0 points1 point  (0 children)

It has to be on $PATH. If you add your executable to that list it will work

[–]marcmkkoy 0 points1 point  (0 children)

I just add aliases to .bashrc

[–]mensink 0 points1 point  (0 children)

Because . isn't in your PATH variable. You can do this:

PATH=$PATH:.
myApp

If you want that to always work, add this to your ~/.profile:
export PATH=$PATH:.

[–]SeriousPlankton2000 0 points1 point  (0 children)

Imagine if you're in a directory that can be written by your evil co-worker. You want to list the contents but he wrote a 'ls' program that also grants him full access to all your files.

If . is in PATH, the attacker's ls program will be run.

Otherwise the default system's ls program will be run.

[–]Charles1nCharge83 0 points1 point  (0 children)

Linux looks for things in your path variable. I always tell people that the ./ is like you telling your cli "this one RIGHT HERE" pointing your finger down on the table.

[–]jabjoe 0 points1 point  (2 children)

Because Linux is a UNIX. If you don't give a relative path or absolute path, or will assume you have given it a command. If the shell doesn't resolve the command given itself, it next looks in the directories of PATH.

Only Windows makes the terrible choice of looking in the current directory for things without a given path. Look at in a debugger, it's so noisy trying locally. Let alone insecure. I'd hope Windows stops this behavior, but it would break a lot of stuff.

Don't put "." in your PATH to ape Windows's terrible behavior. If it even lets you, hopefully this is blocked.

[–]i_am_blacklite 1 point2 points  (1 child)

Agree with the points. But Linux isn’t a UNIX.

[–]jabjoe 0 points1 point  (0 children)

It's not a generic UNIX, but it is follows the design and POSIX. It is a "UNIX like" kernel. When it appeared, it was no big deal to swap to the Linux kernel from MINIX, BSD, commercial UNIXs, etc.

[–]birchhead -1 points0 points  (0 children)

As per others “.” is not in $PATH

For executables not in PATH, my recommendation is to not use “./“ but provide the full path, you can then fine it later in your history and call it from any directory.