How bash and other smart shells can find executable files? - linux

They handle executable elfs, scripts and symbolic links from PATH, however what the algorithm of this doing? I'm afraid of I cannot find a source code of this part of a shell.
UDP: Oh, I'm stupid. It looks for EACH executable file in PATH, either directory or ordinary file.

Well, the actual search is performed by find_user_command_in_path() in findcmd.c:553.
The algorithm to search for a command ${foo} is basically:
check if ${foo} is absolute: if it is return this path and stop searching
iterate over all elements in PATH: for p in ${PATH}
construct a path ${p}/${foo} and see if it exists
if it exists and is executable return this path and stop searching

I'm no expert in this area, but I'm almost perfectly sure that on Linux the executable bit in file permissions is all that matters. No sophisticated algorithm needed.

Let's say that we have a file called hello in the current directory, and that the file contains just one line: echo "hello"
If you ran chmod 755 on the file and and you subsequently execute the file, then the bash shell will look through every path that you have listed in the PATH variable of say .bashrc, starting with the first path, until it locates the first path that contains your hello executable. Think of PATH as a linked list and think of the bash shell as going through the linked list of paths, path by path. If the bash shell is not running the hello executable that you want it to run, you have one option: put your hello executable in any one of the preceeding paths.
I am lazy. I don't bother to turn hello into an executable i.e. I am not running the chmod command and I just run
bash hello
where the bash shell is going to look for the hello file in the current directory, fork a bash process and the forked bash process is going to run the hello file before the forked bash process dies.
I am using the bash shell as an example but any other shell will behave the same way.

Related

Running a Bash script results in 'Bad interpreter: No such file or directory' error

I did found questions on
Bad interpreter: No such file or directory thing on SO.
My issue is also solved when I changed the script from
#!/usr/bin/bash
echo -e "\t\t\e[92mHello from the Test Script!\e[39m"
to:
#!/bin/bash
echo -e "\t\t\e[92mHello from the Test Script!\e[39m"
after
I did the first line change from looking an answer here.
Shell script: Bad interpreter.No such file or directory
I can not understand why removing the /usr from the first line helps.
P.S.I am learning about linux file permissions and I was unable to execute my file even after changing the permission using '755'.
So, please if anyone can explain me this.Thanks in advance.:)
On your system, the bash shell lives in /bin/bash and not /usr/bin/bash.
The path after the ! should be the path to an executable that will be passed the contents of the script as an argument.
You can read more about this at wikipedia
As for the second part of your question; it would not have mattered what the permissions are; as the file was pointing to a bad interpreter.
For more on unix file permissions, I suggest reading this entry on wikipedia.
That's because there is no bash binary at /usr/bin/bash and the correct path for bash is /bin/bash.
The #! line at the top of scripts, called the shebang, determines what program (sh, bash, ruby, perl, python, etc.) is used for running the script.
This post covers this topic well:
https://unix.stackexchange.com/questions/87560/does-the-shebang-determine-the-shell-which-runs-the-script
In my case adding sh before script name solved the issue.
You can also call your script by adding "./" at the beginning in case you call it from the local directory. The other solution is to call it by specifying its full path.

How to execute a file without .sh extension in shell

I want to execute a file in bash without the .sh extension.
Example: I have file "abc.sh" which I can execute directly (as I have added #!/bin/bash as the first line) but I want the filename to be just "abc"
If the file is already executable as abc.sh, then all you need to do is
mv abc.sh abc
(assuming you are in the directory where the file lives)
In a Linux or Unix shell, file extension doesn't affect whether it will execute or not.
In Linux you use ./filename too run a script. And you need execute permission:
chmod 755 filename
But you still need the "Shebang":
#!/bin/bash
From here I got this:
If you did not put the scripts directory in your PATH, and . (the
current directory) is not in the PATH either, you can activate the
script like this:
./script_name.sh
A script can also explicitly be executed by a given shell, but
generally we only do this if we want to obtain special behavior, such
as checking if the script works with another shell or printing traces
for debugging:
rbash script_name.sh
sh script_name.sh
bash -x script_name.sh
What are the permissions on the file? To make it executable with doing something like ./abc.sh it needs to have EXECUTABLE rights.
You can always do bash abc.sh
Linux permissions overview
Filename in Linux doesn't mean anything in terms of execution capabilities, you can call the file myfile.something.something and it can still be executable. You can name it abc but it has to have EXECUTABLE rights for the user,group,other.
To add that permission you can do chmod +x <filename> but you should look at the link above for a better understanding.

File execution with dot space versus dot slash

I am attempting to work with an existing library of code but have encountered an issue. In short, I execute a shell script (let's call this one A) whose first act is to call another script (B). Script B is in my current directory (a requirement of the program I'm using). The software's manual makes reference to bash, however comments in A suggest it was developed in ksh. I've been operating in bash so far.
Inside A, the line to execute B is simply:
. B
It uses the "dot space" syntax to call the program. It doesn't do anything unusual like sudo.
When I call A without dot space syntax, i.e.:
./A
it always errors saying it cannot find the file B. I added pwd, ls, whoami, echo $SHELL, and echo $PATH lines to A to debug and confirmed that B is in fact right there, the script is running with the same $SHELL as I am at the command prompt, the script is the same user as I am, and the script has the same search path $PATH as I do. I also verified if I do:
. B
at the command line, it works just fine. But, if I change the syntax inside A to:
./B
instead, then A executes successfully.
Similarly, if I execute A with dot space syntax, then both . B and ./B work.
Summarizing:
./A only works if A contains ./B syntax.
. A works for A with either ./B or . B syntax.
I understand that using dot space (i.e. . A) syntax executes without forking to a subshell, but I don't see how this could result in the behavior I'm observing given that the file is clearly right there. Is there something I'm missing about the nuances of syntax or parent/child process workspaces? Magic?
UPDATE1: Added info indicating that the script may have been developed in ksh, while I'm using bash.
UPDATE2: Added checking to verify $PATH is the same.
UPDATE3: The script says it was written for ksh, but it is running in bash. In response to Kenster's answer, I found that running bash -posix then . B fails at the command line. That indicates that the difference in environments between the command line and the script is that the latter is running bash in a POSIX-compliant mode, whereas the command line is not. Looking a little closer, I see this in the bash man page:
When invoked as sh, bash enters posix mode after the startup files are read.
The shebang for A is indeed #!/bin/sh.
In summary, when I run A without dot space syntax, it's forking to its own subshell, which is in POSIX-compliant mode because the shebang is #!/bin/sh (instead of, e.g., #!/bin/bash. This is the critical difference between the command line and script runtime environments that leads to A being unable to find B.
Let's start with how the command path works and when it's used. When you run a command like:
ls /tmp
The ls here doesn't contain a / character, so the shell searches the directories in your command path (the value of the PATH environment variable) for a file named ls. If it finds one, it executes that file. In the case of ls, it's usually in /bin or /usr/bin, and both of those directories are typically in your path.
When you issue a command with a / in the command word:
/bin/ls /tmp
The shell doesn't search the command path. It looks specifically for the file /bin/ls and executes that.
Running ./A is an example of running a command with a / in its name. The shell doesn't search the command path; it looks specifically for the file named ./A and executes that. "." is shorthand for your current working directory, so ./A refers to a file that ought to be in your current working directory. If the file exists, it's run like any other command. For example:
cd /bin
./ls
would work to run /bin/ls.
Running . A is an example of sourcing a file. The file being sourced must be a text file containing shell commands. It is executed by the current shell, without starting a new process. The file to be sourced is found in the same way that commands are found. If the name of the file contains a /, then the shell reads the specific file that you named. If the name of the file doesn't contain a /, then the shell looks for it in the command path.
. A # Looks for A using the command path, so might source /bin/A for example
. ./A # Specifically sources ./A
So, your script tries to execute . B and fails claiming that B doesn't exist, even though there's a file named B right there in your current directory. As discussed above, the shell would have searched your command path for B because B didn't contain any / characters. When searching for a command, the shell doesn't automatically search the current directory. It only searches the current directory if that directory is part of the command path.
In short, . B is probably failing because you don't have "." (current directory) in your command path, and the script which is trying to source B is assuming that "." is part of your path. In my opinion, this is a bug in the script. Lots of people run without "." in their path, and the script shouldn't depend on that.
Edit:
You say the script uses ksh, while you are using bash. Ksh follows the POSIX standard--actually, KSH was the basis for the POSIX standard--and always searches the command path as I described. Bash has a flag called "POSIX mode" which controls how strictly it follows the POSIX standard. When not in POSIX mode--which is how people generally use it--bash will check the current directory for the file to be sourced if it doesn't find the file in the command path.
If you were to run bash -posix and run . B within that bash instance, you should find that it won't work.

Making perl scripts executable... can I do away with the preceding 'perl' statement?

This is a pretty simple one... I just want to make a perl script executable without the preceding perl command, and instead let the environment deduce the interpreter from the shebang line. Here is my sample script called test:
#!/usr/bin/perl
print "Hey there\n";
I then use chmod 775 test to make the script executable. If I use the command perl test, I get the output Hey there.
However, if I just type test, I get no output. What's the deal? Why isn't my shebang line making the environment realize this is perl? Can someone please help me?
Don't name your script test. This is a built-in command in most shells, so they don't go looking for an external program.
Also, to run a program in your current directory, you should type ./programname. It's generally a bad idea to have . in your $PATH, which would be necessary to execute it without the directory prefix.
To run something from the current directory you need to prefix "./" to tell it "this directory" ie ./testprogram.
If you type just test it will look in standard install directories like /bin. This is why when you run cp or rm it knows where the executable is.
As mentioned by others, naming scripts test is not allowed with most shells.

How to change Example.bat to Example.pl?

I have read other threads enter link description herethat discuss .bat to L/unix conversions, but none has been satisfactory. I have also tried a lot of hack type approach in writing my own scripts.
I have the following example.bat script that is representative of the kind of script I want to run on unix.
Code:
echo "Example.bat"
perl script1 param.in newParam.in
perl script2 newParam.in stuff.D2D stuff.D2C
program.exe stuff.D2C
perl script3 stuff.DIS results.out
My problem is I don't know how to handle the perl and program.exe in the unix bash shell. I have tried putting them in a system(), but that did not work. Can someone please help me?
Thank you!
Provided that you have an executable file named program.exe somewhere in your $PATH (which you well might — Unix executables don't have to end in .exe, but nothing says they can't), the code you've pasted is a valid shell script. If you save it in a file named, say, example.bat, you can run it by typing
sh example.bat
into the shell prompt.
Of course, Unix shell scripts are usually given the suffix .sh — or no suffix at all — rather than .bat. Also, if you want your script to be executable directly, by typing just
example.sh
rather than sh example.sh, you need to do three things:
Start the script with a "shebang" line: a line that begins with #! and the full path to the shell interpreter you want to use to run it (e.g. /bin/sh for the basic Bourne shell), like this:
#!/bin/sh
echo "This is a shell script."
# ... more commands here ...
Mark your script as executable using the chmod command, e.g.
chmod a+rx example.sh
Put your script somewhere along your $PATH. On Unix, the default path will not normally contain the current directory ., so you can't execute programs from the current directory just by typing their name. You can, however, run them by specifying an explicit path, e.g.
./example.sh # runs example.sh from the current directory
To find out what your $PATH is, just type echo $PATH into the shell.

Resources