3

I am writing a set of scripts that I want to be portable, but I need to know whether sh on the current platform stands for bash, ksh, or ash. Is there a clear way to do it?

What comes to my mind first is to inspect which shell has which --version:

$ zsh --version
zsh 5.0.2 (x86_64-apple-darwin13.0)

$ bash --version
GNU bash, version 4.3.39(1)-release (x86_64-apple-darwin13.4.0)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

$ ksh --version
  version         sh (AT&T Research) 93u+ 2012-08-01

$ dash --version
dash: 0: Illegal option --

$ pdksh --version
pdksh: pdksh: --: unknown option

Apart from being clumsy, this doesn't even produce results in all cases.

Edit

I need it for my bashrc/zshrc/...-like project, where I assign my current working shell to a variable, and use that variable everywhere.

I can't post my code because I need to solve the problem to enable overall cleanliness of my work. Moreover, it would be too much monkeycode... don't misunderstand it, but POSIX compatibility is too narrow to make my project small enough. I'd need to crutch on system configs otherwise.

However, I can post my UNIX Shell defining function:

PROJ_GET_SHELL () {
    local PROJ_SHELL="`ps -p $$ | tail -1 | tr ' ' '\n' | tail -1`"
    PROJ_local=(pdksh bash dash mksh zsh ksh sh)
    for i in ${PROJ_local[*]}
    do
        if ! [ -z `echo $PROJ_SHELL | grep $i` ]
        then
            echo "$i"
            break
        fi
    done
}

PS. The least bit of research shows that $SHELL doesn't change when running a shell as subprocess.

theoden8
  • 133
  • 1
  • 8
  • 2
    If your scripts are written to be *portable*, then by definition your scripts will run correctly under all these shells, therefore you do not need to query to find out which one is in use! – Celada Jul 19 '15 at 17:50
  • @Celeda, You are right, but they aren't enough small and `ksh` throws segfault when `zsh` and `bash` don't. – theoden8 Jul 19 '15 at 17:51
  • 1
    What do you mean by "aren't enough small"? Do you mean that you have a very big workload and it crashes `ksh`? Sounds like a bug in `ksh` but it also sounds like the way to portability is to be more conservative with your workload or use something other than the shell as a programming language if the task is complex and not really suited for a shell script. – Celada Jul 19 '15 at 17:54
  • @Celeda, actually, the task is to make a convenient environment to make working on unix and linux as close to my mind as It's possible. You can generate shell scripts with `perl`, but writing `zsh` scripts compatible with `dash` is not a good idea. Moreover, they have different environmental variables, and working in general with all shells would be too much headache when you can make if-elses in a separate func. **EDITED** – theoden8 Jul 19 '15 at 17:58
  • 1
    I think I just don't understand your goal or motivation here, sorry. "writing `zsh` scripts compatible with `dash` is not a good idea"? I happen to think that writing portable shell scripts *is* a good idea! (In which case they're not called `zsh` scripts or `dash` scripts but `sh` scripts.) I also don't understand your comment about generating shell scripts using `perl` — you are talking about autogenerated code here? – Celada Jul 19 '15 at 18:05
  • 1
    @theoden It might help to provide a more specific example to illustrate your needs. The POSIX standard was created just for the purpose of enabling people to write compatible scripts without need to test for shell version. If you have some requirement that cannot be met under POSIX, it might help if you explained it. – John1024 Jul 19 '15 at 18:25
  • I may don't understand the question, but if you want to know which shell `sh` is pointing to then what's wrong with `readlink -f "$(command -v sh)"`? – jimmij Jul 19 '15 at 18:56
  • @jimmij not really. `bin/sh` might not be a symlink. – Celada Jul 19 '15 at 19:47
  • 1
    you don't need to know what `sh` is. just use syntax which will work with any of them. – mikeserv Jul 19 '15 at 19:48
  • What is the script that throws segmentation fault? – Ed Heal Jul 19 '15 at 20:27
  • Sorry had to leave the question for important reasons. I need to use many of environmental variables that are different for different bash clones. I have a lot of aliases that I'm used to but that make life harder when trying to work with `ash`. For instance, `alias .="$FILEMANAGER ." overrides `source` in `ksh`, or I can't use lists the same way in `zsh` and `ash`. There's a lot of such things that I don't have time to track. The most important reason is that I use my own method of defining what shell is i'm using to source the script and I crutch on it in all other files. – theoden8 Jul 19 '15 at 20:29
  • @EdHeal, it's a my whole project. But I'm aware of publishing whole source code of it because I still think that it's not any good and doesn't give any compatibility with `yash`/`dash`. That's why I keep improving it. – theoden8 Jul 19 '15 at 20:37
  • Why do you need it to be compatible with yash/dash? – Ed Heal Jul 20 '15 at 01:12
  • @MichaelMrozek, I'm really sorry, I thought duplicate is something else.. Considering it a question with the same answer, http://unix.stackexchange.com/a/71137/111378 is a solution. Thus, it is a duplicate, though it's weird to say so about such different questions. – theoden8 Jul 20 '15 at 12:31

1 Answers1

5

On popular distributions:

$ which sh
/bin/sh
$ readlink -f /bin/sh
/bin/dash

Or more compact:

$ readlink -f $(which sh)
/bin/dash

However, some (especially embedded systems or initrd builds) have their shell directly compiled as /bin/sh or it's a hardlink (busybox)

Another unusual case is Windows. git bash in this case:

$ readlink -f $(which sh)
/usr/bin/sh
Daniel Alder
  • 838
  • 11
  • 26
  • No, because `/bin/sh` is not necessarily a symlink. – Celada Jul 19 '15 at 20:04
  • This is a good and simple solution, but I've tried it. The problem is that I'm currently working actively with **Debian** (sh is linked to `dash`) and **Darwin** (sh is not a link, but a binary and is `bash`). – theoden8 Jul 19 '15 at 20:33
  • From my experience, dash is a base which works which almost every shell: just avoid things like `switch` and `<<<` and take care about different signal names for `trap`'s. Or just use `#!/bin/dash` in your shell scripts which should be available on every POSIX distro. And yes. Don't forget testing. Unfortunately, it's not easier than that... – Daniel Alder Jul 19 '15 at 20:49
  • I can't use `dash` for my project, it's too `dash`.. I would rather use perl or python, but I can't embed it into my working shell. POSIX shell is not what I can actually use without extensions. – theoden8 Jul 19 '15 at 20:54
  • @DanielAlder don't use `#!/bin/dash`, use `#!/bin/sh`. `dash`'s raison d'être is to be a minimal fully compliant POSIX shell aka `/bin/sh` implementation. If you use `#!/bin/sh` and write portable code then it will *just work* everywhere (Debian and Darwin have been mentioned in this thread but there's much more) and obviate the need for the OP's question. – Celada Jul 19 '15 at 20:55
  • Once again, I can't write for `dash`, I don't know what it eats, it has no autocompletion, it takes a lot of time for a project that is to save a lot of time. I don't want `dash` or `yash`. I don't even know how to convert that piece of code that I appended to the question into `dash`(POSIX)-compatible function. And I have tonns of such functions. And I never use `dash` so it's senseless to rewrite it. – theoden8 Jul 19 '15 at 21:02
  • @theoden So then I don't know what you want. You have libraries which are obviously written for bash, and you don't want to rewrite them. The only way to solve your problem is just having bash as a requirement and use `#!/bin/bash` in your scripts – Daniel Alder Jul 19 '15 at 21:12
  • No, I primarily use `zsh` (and `bash` sometimes), but I'm trying to expand it for `ksh` `mksh` and `pdksh`. The reasons for that are because `ksh` is a lot(about 1.5x for tested program) faster than `zsh`, and `zsh` is a lot faster than `bash`(2x). I'm trying to do that my project works for all of them instantly on a new system (after changing a few times I got lazy to reformat my previous `rc` file) with my shell after just performing `git clone` **EDITED** _mistype_. – theoden8 Jul 19 '15 at 21:15
  • @Celada I still prefer `#!/bin/dash` for most of my scripts because it's feature set is very stable so that I can expect that my scripts will still work 10y later on another system. It's just a natural limiter which helps me discovering incompatible code during development. – Daniel Alder Jul 19 '15 at 21:20
  • @DanielAlder I think you don't appreciate how stable, widespread, portable, and well-defined the POSIX standard definition of `/bin/sh` is. With `/bin/sh` your POSIX-standard shell script will run on all GNU/Linux distributions, MacOS, Solaris, FreeBSD, etc... etc... whereas with `/bin/dash`, which *by design* has the same features, you will only run where it is available which is... only Debian and derivatives. – Celada Jul 19 '15 at 21:25
  • @DanielAlder, btw, thanks, a couple of script files already work with `dash`, so it's a good appreciation of using it... now this question is a curiosity rather than neccessity. – theoden8 Jul 19 '15 at 21:35