[sf-lug] Fwd: [UMALUG] bash scripting question

Michael Paoli Michael.Paoli at cal.berkeley.edu
Tue Mar 4 22:27:40 PST 2008


> Tyler Trafford wrote:
> > here is the output of running the orginal script with sudo:
> > $ sudo ./mytest.sh
> > ./mytest: line 2: syntax error near unexpected token `('
> > ./mytest: line 2: `vimdiff ~/.bashrc <(whoami)'
> My original script has:
> #/bin/bash
> at the top.

Ah, that would certainly make a difference - at least in many cases.
With the first line having a first character of #, but the second
character not being !, it's no longer #! ("shebang"), and thus no
longer specifies the shell or program used to interpret the script -
but instead the line has been changed to a comment (a first character
of : would also make that first line a comment).  With execute 
permission set, but the file not being an executable binary, it then
gets interpreted by a shell.  Question then remains which shell, and
invoked how or in what manner.  We can see a bit of the various
behavior in the fairly simple tests I included below.  For the Linux
distribution I ran these tests on, /bin/bash is the bash shell, /bin/sh
is a reasonably POSIX/SUS[2][3] compliant shell (which may or may not
be bash - happens to default to and at least presently be configured
using bash on my distribution, but could also be configured to be ash).
The bash shell, at least by default (e.g. when not overridden by other
factors such as specific environment settings, command line options, or
shell options set when or after invoked), when invoked with a basename
portion of sh or -sh will behave in a significantly more POSIX/SUS   
compliant manner.  When invoked with a basename portion of bash or
-bash, it will default to its much more bash-like behavior.

With the /proc file system on Linux, it's also fairly easy for us to
drop a few things into the script, to see a bit more of what's
happening.  For the applicable shells, $$ is the current Process ID
(PID), /proc/$$/exe will show us the pathname of the executable the PID
is running.  /proc/$$/cmdline will show us the command line arguments -
starting with arg0 (usually the name by which the program was invoked),
with the arguments separated and terminated by the ASCII null  
character.  I used cat -vet to be able to visually show that (displays
an ASCII null as ^@) ... and added an echo '' after that to supply a
terminating newline to make the output a bit more clean looking.

I set up 3 test scripts/files.  The foo and bar files respectively use 
#!/bin/bash and #!/bin/sh.  When they are run - with or without sudo,
they run under their specified shells (or in this case, the specified
mode of what happens to be the same shell).  The foobar file starts
with #/bin/bash - so that's just a comment line.  When that is invoked
directly, the shell that's given the command picks a shell to invoke it
under (in this case, bash running in bash-like mode forks bash in a   
bash-like mode to execute it).  In the case of running it under sudo,
sudo, finding the file executable but not a binary executable, runs it
under a shell - likely system default or a default shell that sudo is
compiled or configured to use ... I haven't checked exactly how sudo
decides which, but in any case, for this example, sudo runs it under
/bin/sh.

That pretty much explains what happens, and with that background,
hopefully the test runs, and their output, are then relatively
self-explanatory.

I mentioned before[1], but I'll also mention again that the <(list)   
construct isn't POSIX/SUS[2][3] standards compliant.  One may be well
advised to generally code to the broadest applicable standards as
feasible, to avoid issues with portability and other potentially
unpleasant surprises and "gotchas".

And, ... when in doubt, test. :-)

I sprinkle in some comments below on lines starting with //

$ pwd -P; ls -ond * .
/tmp/Sh
drwxr-xr-x  2 1003 100 Mar  4 20:37 .
-rwxr-xr-x  1 1003  79 Mar  4 21:20 bar
-rwxr-xr-x  1 1003  81 Mar  4 21:20 foo
-rwxr-xr-x  1 1003  80 Mar  4 21:20 foobar
//The above just to emphasize any user should generally be able to
//execute these.
//Our scripts, differing only in their first line:
$ cat foo
#!/bin/bash
cat -vet /proc/$$/cmdline; echo ''
ls -on /proc/$$/exe
cat <(whoami)
$ diff foo bar
1c1
< #!/bin/bash
---
> #!/bin/sh
$ diff foo foobar
1c1
< #!/bin/bash
---
> #/bin/bash
//Our test runs - foo and bar get their specified shells:
$ ./foo
/bin/bash^@./foo^@
lrwxrwxrwx  1 1003 0 Mar  4 21:22 /proc/675/exe -> /bin/bash
michael
//We quickly see <(list) works under bash but not sh:
$ ./bar
/bin/sh^@./bar^@
lrwxrwxrwx  1 1003 0 Mar  4 21:22 /proc/681/exe -> /bin/bash
./bar: line 4: syntax error near unexpected token `('
./bar: line 4: `cat <(whoami)'
$ ./foobar   
-bash^@
lrwxrwxrwx  1 1003 0 Mar  4 21:22 /proc/684/exe -> /bin/bash
michael
$ sudo -u test ./foo
/bin/bash^@./foo^@
lrwxrwxrwx  1 1009 0 Mar  4 21:23 /proc/690/exe -> /bin/bash
test
$ sudo -u test ./bar
/bin/sh^@./bar^@
lrwxrwxrwx  1 1009 0 Mar  4 21:23 /proc/696/exe -> /bin/bash
./bar: line 4: syntax error near unexpected token `('
./bar: line 4: `cat <(whoami)'
//in the case with sudo and foobar, we find different results than we
//did above with foobar, but we see it's consistent with the shell
//that executes the script - it works with bash, but fails with sh
$ sudo -u test ./foobar
/bin/sh^@./foobar^@
lrwxrwxrwx  1 1009 0 Mar  4 21:23 /proc/699/exe -> /bin/bash
./foobar: line 4: syntax error near unexpected token `('
./foobar: line 4: `cat <(whoami)'
$

footnotes/references:
   http://linuxmafia.com/pipermail/sf-lug/2008q1/003876.html
1. http://linuxmafia.com/pipermail/sf-lug/2008q1/003889.html
   http://linuxmafia.com/pipermail/sf-lug/2008q1/003890.html
   http://linuxmafia.com/pipermail/sf-lug/2008q1/003894.html
2. http://www.unix.org/what_is_unix/single_unix_specification.html
3. http://www.opengroup.org/onlinepubs/000095399/utilities/xcu_chap02.html
http://www.rawbw.com/~mp/unix/sh/
http://lists.balug.org/pipermail/balug-announce-balug.org/2005-October/000048.html




More information about the sf-lug mailing list