1

I use the function getTrueShellExeName() posted by mklement0 at https://stackoverflow.com/a/23011530/7471760 in order to get the shell name of a script.

This works perfectly well in most cases, but I have come to a situation involving executable scripts, where it fails (at least on Ubuntu 18).

Consider the following file testshell.sh:

#!/bin/bash

getTrueShellExeName() {
  local trueExe nextTarget 2>/dev/null # ignore error in shells without `local`
  # Determine the shell executable filename.
  trueExe=$(ps -o comm= $$) || return 1
  # Strip a leading "-", as added e.g. by OSX for login shells.
  [ "${trueExe#-}" = "$trueExe" ] || trueExe=${trueExe#-}
  # Determine full executable path.
  [ "${trueExe#/}" != "$trueExe" ] || trueExe=$([ -n "$ZSH_VERSION" ] && which -p "$trueExe" || which "$trueExe")
  # If the executable is a symlink, resolve it to its *ultimate*
  # target.
  while nextTarget=$(readlink "$trueExe"); do trueExe=$nextTarget; done
  # Output the executable name only.
  printf '%s\n' "$(basename "$trueExe")"
}

getTrueShellExeName

Make it executable via chmod +x testshell.sh

I observe the following behaviour in my terminal:

user@pc /tmp $ ./testshell.sh 

user@pc /tmp $ . testshell.sh 
bash
user@pc /tmp $ source testshell.sh 
bash
user@pc /tmp $ bash testshell.sh 
bash
user@pc /tmp $ dash testshell.sh 
dash
user@pc /tmp $ ksh testshell.sh 
ksh93
user@pc /tmp $ zsh testshell.sh 
zsh

So everything works fine except for the first line, where the script is executed.

Some remarks:

  • If I remove #!/bin/bash from the script, then ./testshell.sh prints bash as expected.
  • If I change #!/bin/bash in the script to #!/bin/bash -ex and run ./testshell.sh, it looks as if the script name is mistaken as the shell executable name:
+ getTrueShellExeName
+ local trueExe nextTarget
++ ps -o comm= 15096
+ trueExe=testshell.sh
+ '[' testshell.sh = testshell.sh ']'
+ '[' testshell.sh '!=' testshell.sh ']'
++ '[' -n '' ']'
++ which testshell.sh
+ trueExe=

Any idea how this can be solved to work in all cases, also when bin/bash is at the first line of the script, and it is executed rather than sourced?

ferdymercury
  • 698
  • 4
  • 15
  • 2
    You can try `ps -o args= -p "$$"` – Fravadona Apr 20 '22 at 18:45
  • 1
    neither the script nor your shell have to be in PATH, so the `which` command is not robust. – stark Apr 20 '22 at 19:02
  • 1
    The problem is indeed that the `ps` implementation from the `procps-ng` package reports the _script's_ name rather than the true underlying executable for `comm=` (or perhaps the behavior isn't tied to the `ps` utility but the host platform) - I hadn't noticed, because I ran on macOS. I've updated the linked answer with a solution that now uses `ls /proc/$$/cmdline` on Linux, which should fix the problem. If it does, I suggest deleting this question (I've also added an explanation to the updated answer). – mklement0 Apr 20 '22 at 21:58
  • 2
    @stark, the only bug in the function is that `ps -o comm= $$` is used for trying to determine the name or path of the executable underlying the current process, which on Linux doesn't work with direct execution of shebang-line shell scripts (where it reports the _script_'s name). With that bug fixed, the function works robustly, because it only invokes `which` for executables that were specified _by name only_ (`[ "${trueExe#/}" != "$trueExe" ]`), which implies their presence in PATH. – mklement0 Apr 20 '22 at 22:38

0 Answers0