Can a script be used as an interpreter by the #! hashbang line? - bash

I'm trying to write a bash script which will behave as a basic interpreter, but it doesn't seem to work: The custom interpreter doesn't appear to be invoked. What am I doing wrong?
Here's a simple setup illustrating the problem:
/bin/interpreter: [owned by root; executable]
#!/bin/bash
echo "I am an interpreter running " $1
/Users/zeph/script is owned by me, and is executable:
#!/bin/interpreter
Here are some commands for the custom interpreter.
From what I understand about the mechanics of hashbangs, the script should be executable as follows:
$ ./script
I am an interpreter running ./script
But this doesn't work. Instead the following happens:
$ ./script
./script: line 3: Here: command not found
...It appears that /bin/bash is trying to interpret the contents of ./script. What am I doing wrong?
Note: Although it appears that /bin/interpreter never invoked, I do get an error if it doesn't exist:
$ ./script
-bash: ./script: /bin/interpreter: bad interpreter: No such file or directory
(Second note: If it makes any difference, I'm doing this on MacOS X).

To make this work you could add the interpreter's interpreter (i.e. bash) to the shebang:
#!/bin/bash /bin/interpreter
Here are some commands for the custom interpreter.
bash will then run your interpreter with the script path in $1 as expected.

You can't use a script directly as a #! interpreter, but you can run the script indirectly via the env command using:
#!/usr/bin/env /bin/interpreter
/usr/bin/env is itself a binary, so is a valid interpreter for #!; and /bin/interpreter can be anything you like (a script of any variety, or binary) without having to put knowledge of its own interpreter into the calling script.

Read the execve man page for your system. It dictates how scripts are launched, and it should specify that the interpreter in a hash-bang line is a binary executable.

I asked a similar question in comp.unix.shell that raised some pertinent information.
There was a second branch of the same thread that carried the idea further.
The most general unix solution is to have the shebang point to a binary executable. But that executable program could be as simple as a single call to execl(). Both threads lead to example C source for a program called gscmd, which is little more than a wrapper to execv("gs",...).

Related

Should I use a Shebang with Bash scripts?

I am using Bash
$ echo $SHELL
/bin/bash
and starting about a year ago I stopped using Shebangs with my Bash scripts. Can
I benefit from using #!/bin/sh or #!/bin/bash?
Update: In certain situations a file is only treated as a script with the
Shebang, example
$ cat foo.sh
ls
$ cat bar.sh
#!/bin/sh
ls
$ file foo.sh bar.sh
foo.sh: ASCII text
bar.sh: POSIX shell script, ASCII text executable
On UNIX-like systems, you should always start scripts with a shebang line. The system call execve (which is responsible for starting programs) relies on an executable having either an executable header or a shebang line.
From FreeBSD's execve manual page:
The execve() system call transforms the calling process into a new
process. The new process is constructed from an ordinary file, whose
name is pointed to by path, called the new process file.
[...]
This file is
either an executable object file, or a file of data for an interpreter.
[...]
An interpreter file begins with a line of the form:
#! interpreter [arg]
When an interpreter file is execve'd, the system actually execve's the
specified interpreter. If the optional arg is specified, it becomes the
first argument to the interpreter, and the name of the originally
execve'd file becomes the second argument
Similarly from the Linux manual page:
execve() executes the program pointed to by filename. filename must be
either a binary executable, or a script starting with a line of the
form:
#! interpreter [optional-arg]
In fact, if a file doesn't have the right "magic number" in it's header, (like an ELF header or #!), execve will fail with the ENOEXEC error (again from FreeBSD's execve manpage):
[ENOEXEC] The new process file has the appropriate access
permission, but has an invalid magic number in its
header.
If the file has executable permissions, but no shebang line but does seem to be a text file, the behaviour depends on the shell that you're running in.
Most shells seem to start a new instance of themselves and feed it the file, see below.
Since there is no guarantee that the script was actually written for that shell, this can work or fail spectacularly.
From tcsh(1):
On systems which do not understand the `#!' script interpreter conven‐
tion the shell may be compiled to emulate it; see the version shell
variable. If so, the shell checks the first line of the file to see if
it is of the form `#!interpreter arg ...'. If it is, the shell starts
interpreter with the given args and feeds the file to it on standard
input.
From FreeBSD's sh(1):
If the program is not a normal executable file (i.e., if it
does not begin with the “magic number” whose ASCII representation is
“#!”, resulting in an ENOEXEC return value from execve(2)) but appears to
be a text file, the shell will run a new instance of sh to interpret it.
From bash(1):
If this execution fails because the file is not in executable format,
and the file is not a directory, it is assumed to be a shell script, a
file containing shell commands. A subshell is spawned to execute it.
You cannot always depend on the location of a non-standard program like bash. I've seen bash in /usr/bin, /usr/local/bin, /opt/fsf/bin and /opt/gnu/bin to name a few.
So it is generally a good idea to use env;
#!/usr/bin/env bash
If you want your script to be portable, use sh instead of bash.
#!/bin/sh
While standards like POSIX do not guarantee the absolute paths of standard utilities, most UNIX-like systems seem to have sh in /bin and env in /usr/bin.
Scripts should always begin with a shebang line. If a script doesn't start with this, then it may be executed by the current shell. But that means that if someone who uses your script is running a different shell than you do, the script may behave differently. Also, it means the script can't be run directly from a program (e.g. the C exec() system call, or find -exec), it has to be run from a shell.
You might be interested in an early description by Dennis M Ritchie (dmr) who invented the #! :
From uucp Thu Jan 10 01:37:58 1980
.>From dmr Thu Jan 10 04:25:49 1980 remote from research
The system has been changed so that if a file
being executed begins with the magic characters #! , the rest of the
line is understood to be the name of an interpreter for the executed
file. Previously (and in fact still) the shell did much of this job;
it automatically executed itself on a text file with executable mode
when the text file's name was typed as a command. Putting the facility
into the system gives the following benefits.
1) It makes shell scripts more like real executable files, because
they can be the subject of 'exec.'
2) If you do a 'ps' while such a command is running, its real name
appears instead of 'sh'. Likewise, accounting is done on the basis of
the real name.
3) Shell scripts can be set-user-ID.
4) It is simpler to have alternate shells available; e.g. if you like
the Berkeley csh there is no question about which shell is to
interpret a file.
5) It will allow other interpreters to fit in more smoothly.
To take advantage of this wonderful opportunity, put
#! /bin/sh
at the left margin of the first line of your shell scripts. Blanks
after ! are OK. Use a complete pathname (no search is done). At the
moment the whole line is restricted to 16 characters but this limit
will be raised.
Hope this helps
If you write bash scripts, i.e. non portable scripts containing bashisms, you should keep using the #!/bin/bash shebang just to be sure the correct interpreter is used. You should not replace the shebang by #!/bin/sh as bash will run in POSIX mode so some of your scripts might behave differently.
If you write portable scripts, i.e. scripts only using POSIX utilities and their supported options, you might keep using #!/bin/sh on your system (i.e. one where /bin/sh is a POSIX shell).
It you write stricly conforming POSIX scripts to be distributed in various platforms and you are sure they will only be launched from a POSIX conforming system, you might and probably should remove the shebang as stated in the POSIX standard:
As it stands, a strictly conforming application must not use "#!" as the first two characters of the file.
The rationale is the POSIX standard doesn't mandate /bin/sh to be the POSIX compliant shell so there is no portable way to specify its path in a shebang. In this third case, to be able to use the 'find -exec' syntax on systems unable to run a shebangless still executable script, you can simply specify the interpreter in the find command itself, eg:
find /tmp -name "*.foo" -exec sh -c 'myscript "$#"' sh {} +
Here, as sh is specified without a path, the POSIX shell will be run.
The header is useful since it specifies which shell to use when running the script. For example, #!/bin/zsh would change the shell to zsh instead of bash, where you can use different commands.
For example, this page specifies the following:
Using #!/bin/sh, the default Bourne shell in most commercial variants
of UNIX, makes the script portable to non-Linux machines, though you
sacrifice Bash-specific features ...
TL;DR: always in scripts; please not in source'd scripts
Always in your parent
FYI: POSIX compliant is #!/bin/bash, not #!/bin/sh
You want to clarify this so that nothing else overrides the interpreter your script is made for.
You don't want a user at the terminal using zsh to have trouble if your script was written for POSIX bash scripts.
You don't want to run source in your #!/bin/bash unrecognized by #!/bin/sh, someone in an sh terminal have it break the script because it is expecting the simple/POSIX . for including source'd files
You don't want e.g. zsh features - not available in other interpreters - to make their way into your bash code. So, put #!/bin/bash in all your script headers. Then, any of your zsh habits in your script will break so you know to remove them before your roll-out.
It's probably best, especially so POSIX-compliant scripts don't break in a terminal like zsh.
Not expected for included source scripts
FYI: POSIX compliant for sourcing text in a BASH script is ., not source
You can use either for sourcing, but I'll do POSIX.
Standard "shebanging" for all scripting:
parent.sh:
#!/bin/bash
echo "My script here"
. sourced.sh # child/source script, below
sourced.sh:
echo "I am a sourced child script"
But, you are allowed to do this...
sourced.sh: (optional)
#!/bin/bash
echo "I am a sourced child script"
There, the #!/bin/bash "shebang" will be ignored. The main reason I would use it is for syntax highlighting in my text editor. However, in the "proper" scripting world, it is expected that your rolled-out source'd script will not contain the shebang.
In addition to what the others said, the shebang also enables syntax highlighting in some text editors, for example vim.
$SHELL and #!/bin/bash or #!/bin/sh are different.
To start, #!/bin/sh is a symlink to /bin/bash on most Linux systems (on Ubuntu it is now /bin/dash)
But on whether to start with /bin/sh or /bin/bash:
Bash and sh are two different shells. Basically bash is sh, with more
features and better syntax. Most commands work the same, but they are
different.
Just assume if you're writing a bash script, stick with /bin/bash and not /sh because problems can arise.
$SHELL does not necessarily reflect the currently running shell.
Instead, $SHELL is the user's preferred shell, which is typically the
one set in /etc/passwd. If you start a different shell after logging
in, you can not necessarily expect $SHELL to match the current shell
anymore.
This is mine for example, but it could also be /root:/bin/dash or /root:/bin/sh depending on which shell you have input in passwd. So to avoid any problems, keep the passwd file at /bin/bash and then using $SHELL vs. #!/bin/bash wouldn't matter as much.
root#kali:~/Desktop# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
Sources:
http://shebang.mintern.net/bourne-is-not-bash-or-read-echo-and-backslash/
https://unix.stackexchange.com/questions/43499/difference-between-echo-shell-and-which-bash
http://man.cx/sh
http://man.cx/bash

How to run a TCL script from shell script?

I am new to TCL scripting and shell scripting. I want to invoke a TCL script from the shell script. I have tried as below.
#!/bin/sh
for i in {1..5}
do
my_script
test_script
done
If I run the script, it is throwing error as follows,
./sample.sh: line 5: my_script: command not found
./sample.sh: line 5: test_script: command not found
Can anyone help me out with this ?
Thanks in advance.
If they cannot be found in your $PATH you have to provide a path to your scripts, e.g.:
./my_myscript # current directory
/path/to/test_script # absolute path
If you haven't made your script executable (with chmod +x) then you need to use:
tclsh my_script.tcl
Or maybe tclsh8.5 /path/to/script.tcl or many variations on that.
If you have made the script executable, check that the directory containing the script is on your PATH (if not, use the full filename of the script or adjust your PATH) and that you've got a suitable #! line. The usual recommended one is:
#!/usr/bin/env tclsh8.5
as that will search your path for the tclsh8.5 executable instead of hard-coding it.
From man tclsh. I guess the second block answers your question.
If you create a Tcl script in a file whose first line is
#!/usr/local/bin/tclsh
then you can invoke the script file directly from your shell if you mark the file as executable. [...]
An even better approach is to start your script files with the following three lines:
#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" ${1+"$#"}
This approach has three advantages over the approach in the previous paragraph [...]
You should note that it is also common practice to install tclsh with its version number as part of the name.This has the advantage of allowing multiple
versions of Tcl to exist on the same system at once, but also the disadvantage of making it harder to write scripts that start up uniformly across different versions of Tcl.

Bash: What is the effect of "#!/bin/sh" in a bash script with curl

I make a complex and long line command to successful login in a site. If I execute it in Console it work. But if I copy and paste the same line in a bash script it not work.
I tried a lot of thing, but accidentally discovery that if I NOT use the line
#!/bin/sh
it work! Why this happens in my mac OSX Lion? What this config line do in a bash script?
A bash script that is run via /bin/sh runs in sh compatibility mode, which means that many bash-specific features (herestrings, process substitution, etc.) will not work.
sh-4.2$ cat < <(echo 123)
sh: syntax error near unexpected token `<'
If you want to be able to use full bash syntax, use #!/bin/bash as your shebang line.
"#!/bin/sh" is a common idiom to insure that the correct interpreter is used to run the script. Here, "sh" is the "Bourne Shell". A good, standard "least common denominator" for shell scripts.
In your case, however, "#!/bin/sh" seems to be the wrong interpreter.
Here's a bit more info:
http://www.unix.com/answers-frequently-asked-questions/7077-what-does-usr-bin-ksh-mean.html
Originally, we only had one shell on unix. When you asked to run a
command, the shell would attempt to invoke one of the exec() system
calls on it. It the command was an executable, the exec would succeed
and the command would run. If the exec() failed, the shell would not
give up, instead it would try to interpet the command file as if it
were a shell script.
Then unix got more shells and the situation became confused. Most
folks would write scripts in one shell and type commands in another.
And each shell had differing rules for feeding scripts to an
interpreter.
This is when the "#! /" trick was invented. The idea was to let the
kernel's exec() system calls succeed with shell scripts. When the
kernel tries to exec() a file, it looks at the first 4 bytes which
represent an integer called a magic number. This tells the kernel if
it should try to run the file or not. So "#! /" was added to magic
numbers that the kernel knows and it was extended to actually be able
to run shell scripts by itself. But some people could not type "#! /",
they kept leaving the space out. So the kernel was exended a bit again
to allow "#!/" to work as a special 3 byte magic number.
So #! /usr/bin/ksh and
#!/usr/bin/ksh now mean the same thing. I always use the former since at least some kernels might still exist that don't understand the
latter.
And note that the first line is a signal to the kernel, and not to the
shell. What happens now is that when shells try to run scripts via
exec() they just succeed. And we never stumble on their various
fallback schemes.
The very first line of the script can be used to select which script interpreter to use.
With
#!/bin/bash
You are telling the shell to invoke /bin/bash interpreter to execute your script.
Assure that there are not spaces or empty lines before #!/bin/bash or it will not work.

Why can't I call `history` from within Ruby?

I can run Bash shell commands from with a Ruby program or irb using backticks (and %x(), system, etc). But that does not work with history for some reason.
For example:
jones$ irb --simple-prompt
>> `whoami`
=> "jones\n"
>> `history`
(irb):2: command not found: history
=> ""
From within a Ruby program it produces this error:
/usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31: command not found: history
In bash itself, those commands work fine
It's not that the Ruby call is invoking a new shell - it simply does not find that command...
Anyone know why? I'm stumped...
Most unix commands are implemented as executable files, and the backtick operator gives you the ability to execute these commands from within your script. However, some commands that are interpreted by bash are not executable files; they are features built-in to the bash command itself. history is one such command. The only way to execute this command is to first execute bash, then ask it to run that command.
You can use the command type to tell you the type of a particular command in order to know if you can exec it from a ruby (or python, perl, Tcl, etc script). For example:
$ type history
history is a shell builtin
$ type cat
cat is /bin/cat
You'll also find that you can't exec aliases defined in your .bashrc file either, since those aren't executable files either.
It helps to remember that exec'ing a command doesn't mean "run this shell command" but rather "run this executable file". If it's not an executable file, you can't exec it.
It's a built-in. In general, you can run built-ins by manually calling the shell:
`bash -c 'history'`
However, in this case, that will probably not be useful.
{~} ∴ which history
history: shell built-in command

bash trickery using --init-file

I have a bash-script (let's call it /usr/bin/bosh) using the following she-bang line:
#!/bin/bash --init-file
It defines a couple of functions, and generally puts the interactive shell in an environment where the user can control a bunch of stuff that I want. This works pretty well. Now for the interesting part, I'd like to be able to let users use this in-between-layer for writing new scripts, without explicitly havnig to source this one. Is that at all possible?
I tried writing a script (let's call it /usr/bin/foo) using the she-bang line
#!/usr/bin/bosh
Which I thought, would be rewritten to execute the command
/usr/bin/bosh /usr/bin/foo
which in turn would result in
/bin/bash --init-file /usr/bin/bosh /usr/bin/foo
But it doesn't work, /usr/bin/foo gets executed, but /usr/bin/bosh is not source before that.
How can I make it source the init file even though the script is not interactive? Or would I have to write a wrapper script for that? I thought of having a script like this
#!/bin/bash
. /usr/bin/bosh
. "$1"
But that wouldn't turn into an interactive shell if I don't specify a script to run, which would be kind of a shame.
EDIT
For clarification, what I'm really asking is, how can I make bash source a file (like --init-file) regardless whether it's interactive (before starting the interactive part) or not (before executing the script)? If there's no way, is there any other way to solve my problem perhaps?
The program specified by the #! cannot be another script I'm afraid at least until linux kernel 2.6.27.9, which allows this feature. If you run strace on foo you'll see that you'd get an ENOEXEC or exec format error, because bosh cannot be executed as a standalone program.
What is happening is that instead of /bin/bosh being executed and handed foo as input, your login shell is simply silently falling back to executing foo itself in a sub-shell, which is why it seems to almost work.
A wrapper or C program that launches bash the way you want are probably your only options. Even with an upgrade to your kernel, it will not quite work the way you want I'm afraid.
Everything you ever wanted to know about #! here: http://www.in-ulm.de/~mascheck/various/shebang/
EDIT: If your kernel really does support chained scripts, then a work-around for /usr/bin/bosh might be something like:
#!/bin/bash
if [ ! $PS1 ]; then
exec /bin/bash --init-file "$0" -i "$#"
fi
... rest of bosh init file ...
An exec seems to be unavoidable to get this to work the way you want it to.
A script is not a runtime environment. That may be your problem. The shebang defnies the runtime environment. ie... /bin/java /bin/python /bin/bash /bin/dash. Your script is not an environment. Your "wrapper example" would be appropriate.

Resources