Why doesn't this softlink work as expected?

bash4.3 # pwd
/bin
bash4.3 # ll sh
lrwxrwxrwx. 1   root    root    4   May 17 22:22 sh -> bash
bash4.3 # ll bash
-rwxr-xr-x. 1   root    root    1072056 May 17 22:22 bash
bash4.3 # bash
bash4.3 # sh
sh-4.3#

My OS is fedora 24 (Default GNOME version).

From the example, we can know: under /bin, bash is a binary executable; sh is a soft link to bash.

So, to my knowledge, type bash and press enter should effect the same result exactly as type sh and press enter.

When I type bash and press enter, I get [root@localhost bin]# as expected.

However, if I type sh and press enter, I surprisingly get sh-4.3#.

What's the cause?


That is a documented feature.

If you run bash via a symlink named sh, bash will start in sh compatibility mode.

From man bash:

If bash is invoked with the name sh, it tries to mimic the startup behavior of historical versions of sh as closely as possible, while conforming to the POSIX standard as well. When invoked as an interactive login shell, or a non-interactive shell with the --login option, it first attempts to read and execute commands from /etc/profile and ~/.profile, in that order. The --noprofile option may be used to inhibit this behavior. When invoked as an interactive shell with the name sh, bash looks for the variable ENV, expands its value if it is defined, and uses the expanded value as the name of a file to read and execute. Since a shell invoked as sh does not attempt to read and execute commands from any other startup files, the --rcfile option has no effect. A non-interactive shell invoked with the name sh does not attempt to read any other startup files. When invoked as sh, bash enters posix mode after the startup files are read.

How does a program know what name was used to start it?

If it is a c program, it can inspect argv[0]. If it is a shell or perl script, it can inspect $0.

As an example, let's consider this simple shell script:

$ cat utc
#!/bin/sh
case "${0##*/}" in
        utc) date -u ;;
        et) TZ=US/Eastern date ;;
esac

$0 is the name that the script was called by. ${0##*/} is the name that the script was called with any directory names removed.

Let's create this symlink:

ln -s utc et

So, utc and et both run the same executable but they provide different results. When run as utc, it outputs universal time. When run as et, is outputs US Eastern time. For example:

$ utc
Wed Jul 20 18:14:18 UTC 2016
$ et
Wed Jul 20 14:14:20 EDT 2016