Execute a bash script without typing ./ [duplicate] - linux

I feel like I'm missing something very basic so apologies if this question is obtuse. I've been struggling with this problem for as long as I've been using the bash shell.
Say I have a structure like this:
├──bin
├──command (executable)
This will execute:
$ bin/command
then I symlink bin/command to the project root
$ ln -s bin/command c
like so
├──c (symlink to bin/command)
├──bin
├──command (executable)
I can't do the following (errors with -bash: c: command not found)
$ c
I must do?
$ ./c
What's going on here? — is it possible to execute a command from the current directory without preceding it with ./ and also without using a system wide alias? It would be very convenient for distributed executables and utility scripts to give them one letter folder specific shortcuts on a per project basis.

It's not a matter of bash not allowing execution from the current directory, but rather, you haven't added the current directory to your list of directories to execute from.
export PATH=".:$PATH"
$ c
$
This can be a security risk, however, because if the directory contains files which you don't trust or know where they came from, a file existing in the currently directory could be confused with a system command.
For example, say the current directory is called "foo" and your colleague asks you to go into "foo" and set the permissions of "bar" to 755. As root, you run "chmod foo 755"
You assume chmod really is chmod, but if there is a file named chmod in the current directory and your colleague put it there, chmod is really a program he wrote and you are running it as root. Perhaps "chmod" resets the root password on the box or something else dangerous.
Therefore, the standard is to limit command executions which don't specify a directory to a set of explicitly trusted directories.

Beware that the accepted answer introduces a serious vulnerability!
You might add the current directory to your PATH but not at the beginning of it. That would be a very risky setting.
There are still possible vulnerabilities when the current directory is at the end but far less so this is what I would suggest:
PATH="$PATH":.
Here, the current directory is only searched after every directory already present in the PATH is explored so the risk to have an existing command overloaded by an hostile one is no more present. There is still a risk for an uninstalled command or a typo to be exploited, but it is much lower. Just make sure the dot is always at the end of the PATH when you add new directories in it.

You could add . to your PATH. (See kamituel's answer for details)
Also there is ~/.local/bin for user specific binaries on many distros.

What you can do is add the current dir (.) to the $PATH:
export PATH=.:$PATH
But this can pose a security issue, so be aware of that. See this ServerFault answer on why it's not so good idea, especially for the root account.

Related

Odd behaviour of pwd with symlinks in terminal tabs

If I create a symlink through
ln -s /path/to/linked/dir current/path/link_name
and change the directory to current/path/link_name via
cd link_name
then I can check where I am using pwd-command. The output will be
current/path/link_name
But if I use some terminal emulator, such as terminator, konsole or others, I can split the tab or create a new tab in the same directory. The output of the pwd-command in the newly created tab will be
/path/to/linked/dir
In many cases, this is not convenient. Does anybody know how to change this behaviour in some terminal emulator(s)?
P.S. I also noticed that the output of ls typed from /current/path/link_name is the same as the output of ls typed from /path/to/linked/dir.
You can't. The reason is that you lose the information on how did you get there after the system call has executed. Some terminal emulators and mainly the bash(1) shell try to remember this, and implement pwd as an internal command, to cope with this scenarios. But in general if you try
/bin/pwd
You'll discover that all the information about how did you get to that final directory was lost in the course of time.
Ask yourself how can the /bin/pwd work and how can it determine the directory you are on, and you'll answer yourself the question:
The system maintains a current directory (the pwd command inherits this from its parent shell) in the system data for each process, but to save resources, it only stores the inode number of the directory that is actually your current directory (not actually, it maintains a reference to the in-core inode structure). It doesn't store the path you used to locate it, and it stores that info only to be able to get a starting point when you ask for a relative path when opening a file. The problem is the same as determining which directory a multiple linked file belongs to... no parent directory is stored for a file, as it can be in multiple directories linking to it all at the same time... this is also true for directories, but they have an .. entry inside themselves that links to their parents (their true parents, as one directory is now allowed to belong to different directories by means on normal links, this is forbidden by system, and ensured by the mkdir(2) system call) The pwd(1) commands uses precisely these links to find the parent directory (and then find the current dir in the parent directory, by searching for the inode number of the current directory on it), until this algorithm leads to the same inode (the root directory has this special characteristic, its .. entry points again to itself) so it stops going up. pwd can only work because it is following directories, and never files.
Terminator 1.90 does what you want. In an example session:
$ cd -- "$(mktemp --directory)"
$ mkdir a
$ ln -s a b
$ cd b
Press Ctrl-Shift-e (or o, or t). At this point I'm still in b.

symlinking and running an installation command

In the installation guide of some soft, user is told to run this
sudo ln -s /opt/local/somesoft/somsoft* /opt/local/bin
Is this command dangerous ? Should /opt/local/bin be prevented from calling something else tha n itself ?
This command does few things
sudo gives root permissions to the 'ln' binary
ln is instructed to go through all files matching pattern /opt/local/somesoft/somsoft*
for every such file it tries to create symbolic link in /opt/local/bin directory
this created symbolic link will have the same name as the file just being processed
Your first question is, is it dangerous? Most probably not, there are few things which might go wrong
your $PATH environment contains some strange directory, so instead of calling /usr/bin/ln (as was the original intention I believe) you wold be tricked into calling some different executable. For example if your PATH=.:/tmp:/usr/bin, 'ln' is first searched in your current directory, then in /tmp and then in /usr/bin. And it's called with superuser permissions ...
there are no such files as /opt/local/somesoft/somsoft* . In such case ln will create symbolic link '/opt/local/bin/somsoft*' (including the star in it's name). That's probably not what you wanted
/opt/local/bin already contains the files with the same names as /opt/local/somesoft/somsoft* . In such case ln will not create new files there (is it good or bad? that is the question)
You don't have /opt/local/bin . In such cases there are several ways of how the command fails (depending whether you have /opt/local directory and how many files match the pattern /opt/local/somesoft/somsoft*)
Your second question does not make much sense. /opt/local/bin is a directory, and directory can't "call" anything. So it can't be prevented to do so. If you ask whether the symbolic links should be created there, I would say why not. The whole idea behind the command is to
install somesoft into special directory so that you won't pollute your /usr/bin or any other common directory
to be able to run the commands without the need of specifying every time full path /opt/local/somesoft/somsoft... you may want to create symbolic links in /opt/local/bin. And make sure that your /opt/local/bin is in your directory.

When running a sh file in linux, why do I have to run ./name.sh?

I have a file called x.sh that I want to execute. If I run:
x.sh
then I get:
x.sh: command not found
If I run:
./x.sh
then it runs correctly. Why do I have to type in ./ first?
Because the current directory is not into the PATH environment variable by default, and executables without a path qualification are searched only inside the directory specified by PATH. You can change this behavior by adding . to the end of PATH, but it's not common practice, you'll just get used to this UNIXism.
The idea behind this is that, if executables were searched first inside the current directory, a malicious user could put inside his home directory an executable named e.g. ls or grep or some other commonly used command, tricking the administrator to use it, maybe with superuser powers. On the other hand, this problem is not much felt if you put . at the end of PATH, since in that case the system directories are searched first.
But: our malicious user could still create his dangerous scripts named as common typos of often used commands, e.g. sl for ls (protip: bind it to Steam Locomotive and you won't be tricked anyway :D).
So you see that it's still better to be safe that, if you type an executable name without a path qualification, you are sure you're running something from system directories (and thus supposedly safe).
Because the current directory is normally not included in the default PATH, for security reasons: by NOT looking in the current directory all kinds of nastiness that could be caused by planting a malicious program with the name of a legitimate utility can be avoided. As an example, imagine someone manages to plant a script called ls in your directory, and that script executes rm *.
If you wish to include the current directory in your path, and you're using bash as your default shell, you can add the path via your ~/.bashrc file.
export PATH=$PATH:.
Based on the explanation above, the risk posed by rogue programs is reduced by looking in . last, so all well known legitimate programs will be found before . is checked.
You could also modify the systemwide settings via /etc/profile but that's probably not a good idea.
Because current directory is not in PATH (unlike cmd in Windows). It is a security feature so that malicious scripts in your current directory are not accidentally run.
Though it is not advisable, to satisfy curiosity, you can add . to the PATH and then you will see that x.sh will work.
If you don't explicitly specify a directory then the shell searches through the directories listed in your $PATH for the named executable. If your $PATH does not include . then the current directory is not searched.
$ echo $PATH
/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin
This is on purpose. If the current directory were searched then the command you type could potentially change based on what directory you're in. This would allow a malicious user to place a binary named ls or cp into directories you frequent and trick you into running a different program.
$ cat /tmp/ls
rm -rf ~/*
$ cd /tmp
$ ls
*kaboom*
I strongly recommend you not add . to your $PATH. You will quickly get used to typing ./, it's no big deal.
You can't execute your file by typing simply
x.sh
because the present working directory isn't in your $PATH. To see your present working directory, type
$ pwd
To see your $PATH, type
$ echo $PATH
To add the current directory to your $PATH for this session, type
$ PATH=$PATH:.
To add it permanently, edit the file .profile in your home directory.

creating a reference for script

I want to make the following kind of reference:
"ls" command, for example, is universally available in most *nix environments. User can type in from anywhere to execute the scripts.
So, I write script "x". I want to make sure that from wherever the user type in x, the actual script "x" is referenced.
Thus, if I have script "x" stored in home/user/Desktop directory, I should not have to reference the script as follow:
home/user/Desktop/x
I should be able to do:
x
Thanks!
You want to add the directory to your PATH. E.g.
PATH="$PATH:/home/user/someDirectory"
You can add this line to .bash_profile to do it on startup. However, you probably shouldn't add Desktop to the path because some browsers download to there by default (though it shouldn't be executable by default).
You can also put your script in an existing directory that's already in your path such as /usr/local/bin or create a symlink there to your script's location.
cp /home/user/Desktop/x /usr/local/bin
or
mv /home/user/Desktop/x /usr/local/bin
or
ln -s /home/user/Desktop/x /usr/local/bin
Don't mean to be obnoxiously repetitive, but this is my first time answering a question, I can't reply to someone's already-good answer, and I think they are missing some important bits.
First, if you want to make sure everyone can access the script, you'll need to be sure everyone has execute permissions:
chmod a+x /path/to/script.sh
You'll also want to make sure it's in somewhere $PATH references (as the other answers mentioned):
echo $PATH # place the script in one of these directories
I would personally prefer /usr/local/bin, since that's considered the place for custom global scripts. Something the other answers didn't mention is that, if you do want to use a directory besides one in $PATH (say, /opt/myscriptfolder/) you'll want to add another PATH entry at the end of /etc/profile:
PATH="$PATH:/opt/myscriptfolder/"
By putting this in the end of /etc/profile, all users will receive this modified PATH variable on their next login.

implementing cd command using chdir() in linux

I am writing my own shell program. I am currently implementing the cd command using chdir.
I want to implement the chdir with the below options :
-P Do not follow symbolic links
-L Follow symbolic links (default)
I posted a question here previously asking to know if a path is a symbolic link or actual path. But with that info I am unable to get any ideas on how to proceed with the above problem.
Thanks
Maybe I'm misunderstanding, but you just want (pseudocode):
is_symlink = method_from_other_question();
if(is_symlink and arg(-P))
fail("Can't switch directory -- is a symlink");
If you've already tried something like this and it doesn't work, include the code in your question and we can help debug it
Shells generally do a lot of trickery to find the real dir name. And they also fake a lot of stuff for our user's convenience. E.g.:
$ pwd
/home/auser
$ ls -l
adir -> some/place/
some/
$ cd adir
$ pwd
/home/auser/adir
$ cd ..
$ pwd
/home/auser
Looks logical? Then look again: the .. in /home/auser/some/place points to /home/auser/some, not /home/auser yet cd .. took you to the later.
IOW, there is no other way but to always keep in memory the absolute path of the current directory and parse it fully (and check every element of it) when doing cd.
And yes, it is not reliable. In past on one occasion I have managed to fool bash and it was showing totally wrong absolute path for my current directory.

Resources