Running scripts without a hashbang - ksh anomaly?

I noticed some strange looking parameters on some processes on one of our servers, and after a little playing around we deduced that ksh seemed to be adding a (somewhat random) extra parameter when calling a script without a hashbang in it.

It looks like it's the partial name of the parent script (and not always the same number of characters). I'm thinking it's not just a display artifact as it's in /proc/xxx/cmdline as well. However, the script arguments don't see it.

It doesn't happen if the child script has a hashbang or is called explicitly with ksh, or if the parent script is run with bash.

It doesn't appear to be causing any actual problem for the real script, but it seems a tad odd.

Can anyone shed any light on this?

An example:

$ cat SomeScriptOrOther.sh
#!/bin/ksh

ksh ./sleep.sh PARAM1 PARAM2
./sleep.sh PARAM1 PARAM2

echo "Wibble"

$ cat sleep.sh
echo $# "$@"
ps -Hfu $USER
sleep 1
$ ./SomeScriptOrOther.sh
2 PARAM1 PARAM2
UID        PID  PPID  C STIME TTY          TIME CMD
14347     8996  8989  0 15:53 ?        00:00:00 sshd: carlo@pts/0
14347     8997  8996  0 15:53 pts/0    00:00:00   -bash
14347    11273  8997  0 16:00 pts/0    00:00:00     /bin/ksh ./SomeScriptOrOther.sh
14347    11274 11273  0 16:00 pts/0    00:00:00       ksh ./sleep.sh PARAM1 PARAM2
14347    11275 11274  0 16:00 pts/0    00:00:00         ps -Hfu carlo
2 PARAM1 PARAM2
UID        PID  PPID  C STIME TTY          TIME CMD
14347     8996  8989  0 15:53 ?        00:00:00 sshd: carlo@pts/0
14347     8997  8996  0 15:53 pts/0    00:00:00   -bash
14347    11273  8997  0 16:00 pts/0    00:00:00     /bin/ksh ./SomeScriptOrOther.sh
14347    11278 11273  0 16:00 pts/0    00:00:00       ./sleep.sh PARAM1 PARAM2 her.sh
14347    11279 11278  0 16:00 pts/0    00:00:00         ps -Hfu carlo
Wibble
$ ksh --version
  version         sh (AT&T Research) 93t+ 2010-06-21

The man page for the execvp() call tells what happend to scripts without a #!. They go to sh stdin usually. Permissions bits have no effect on the process except it must not be no read.

Note that what /bin/sh is can vary from system to system. On Linux you might get BASH or DASH. Some other UNIX you might get KSH. In Solaris you might get old-fashioned, 1970's-era sh as written by Bourne himself...

I'm aware that sh (which is bash on this system) is used to execute scripts without a specified executable, but how does that explain where the extra 'ghost' parameter is coming from (and only when the parent script is run with ksh, not with bash)?

With the shebang, the OS loads the interpreter. without the shebang, the shell implements this. Must be how ksh93 copies the environment, overwriting the parent's without zeroing it first? Just looking briefly, it may start with sh_fork()/ sh_ntfork() in xec.c

The hash-bnag supports one parameter for commands like awk and sed that need a parameter to introduce a script. So, script parameters shift right in the real world, "sed -f scriptname $@" or "ksh scriptname $@". Dunno about ghosts. Try running it under truss/tusc/strace -fae (follow forks, report args and environment) and learn what is really happening.

ksh does indeed have a special way to deal with child scripts that have no #! leading line. This introduces a number of special features that expands the traditional unix environment. These expanded features are very non-standard. Basically ksh then also you to export arrays, functions, and aliases to the environment. My feeling is that this is too non-standard. It's well documented, but as this thread attests, few people read the ksh documention. This makes exported arrays, functions, and aliases a support issue even when they are used correctly. Even worse are folks who unintentionally export arrays, functions, and aliases and then are bewildered by the result.

My advice is to always use #! in every script every time.

ksh cannot rely on the standard Unix environment for this stuff. So it implements non-#! child scripts by simply forking. The forked child ksh process knows every variable, array, function, and aliases that the parent knew including stuff that was not exported to the child script. So the child ksh process takes a moment and deletes everything not explicitly exported to the child script. Then the child script can run apparently pulling exported arrays, functions, and aliases from the "environment".

In "ps" this child ksh process will look like a duplicate of the parent. It is. It was forked from the parent. Some Unix systems allow a process to change the arguments that appear in "ps". ksh tries to do this. There is no guarantee that it will succeed. In this case, the final 6 characters of the original arguments were not over written. I might call that a "bug". I might even report it if I actually cared about non-#! ksh scripts.

5 Likes

I recall that on some UNIX systems, HP-UX as I recall, the ksh would run ksh scripts in the parent process, effectively sourcing it, so it changed the parent environment. I had to add () to a bunch of old scripts to protect my shell process! However, I may have had bad #! lines, which still does not justify using the parent shell but does imply it might feed it to stdin. So, beware!

See this thread

1 Like

Does the #! line allow space between #! and the interpreter path? I saw that near the end of the above referenced thread.

Well, see this thread especially "Part 2 The Format of the #! Line"

1 Like