Tab complete second command - ksh

I am trying to get tab completion on the second atom of a command.
I.e. man do<tab complete>
In bash this can be done with complete -cf <command> but I am not sure if this is possible in ksh. Any Ideas?

It depends on which ksh flavour you are using.
In AT&T ksh88 as well as pdksh and derivatives (mksh, NetBSD ksh, OpenBSD ksh / oksh), this is not possible (asides from really weird hacks. They (I can say that for guaranteed for mksh as I am its developer) complete the second (and follow-up, except after a closing parenthesis )) word of a command consistently as cwd-relative pathname (“file” simplified).
AT&T ksh93 in the last version from AT&T 93u+20120801 does not have programmable tab completion; however, the new development under situ from Red Hat introduced it, as indicated in the manpage. You will not likely find this in any stable distro yet, however, they have recently issued their first stable post-fork release.

Related

Using a non-system-dependent bash shebang (instead of #!/bin/bash) that works everywhere?

I typically put a shebang for bash at the top of my shell scripts, e.g.:
#!/usr/bin/bash
However I see many other variants of this, like #!/bin/bash or #!/usr/local/bin/bash etc.
It seems to me these different conventions would result in compatibility or portability issues. If my bash is at another location than someone else's, my script won't work on their machine and vice versa.
If a shell interpreter like bash is apparently not always at the same location, isn't it plain WRONG to explicitly use a hardcoded path in a script?
I understood you can use a somewhat more flexible or less system-dependent approach like this:
#!/usr/bin/env bash
Which results in the (or a?) local version of bash, wherever that may be installed.
Does the latter variant always work? Or is there a better approach that has the highest chance of referring to any system's bash regardless of where it's installed?
I would recommend either "#!/bin/bash" or "#!/usr/bin/bash". On a modern Linux distro, bash should be installed in both places.
Apparently, that isn't true for OpenBSD ... which uses ksh as the default shell. But on an OpenBSD system, you are liable to find that bash isn't installed at all. It is apparently an optional package, and the admin may have not installed it.
So, if you want to maximize portability, use "/bin/sh" and restrict yourself to standard POSIX shell syntax and commands. "/bin/sh" is typically a link to bash or ksh, and runs in POSIX compliant mode.
Other variations:
"#!/usr/local/bin/bash" typically won't work on Linux. If it does, it may give you a locally built / modified version of bash.
"#!/usr/bin/env bash" should work, with a couple of caveats:
This will give you whatever version of bash is first on the user's command search path (i.e. $PATH).
It is conceivable that the path to env may be different, or that it may not exist. (The env command wasn't in the first version of the POSIX specs.)

Why & How fish does not support POSIX?

I have heard about fish that it's a friendly and out-of-box shell but also it doesn't support POSIX standard.
On the other hand I read about POSIX standard (and also I tested it on my Fedora, It's amazing and out-of-box shell now I want to change my default shell to fish).
But the matter that I opened this question for is: I misunderstood about relation between fish and POSIX standard, what do you mean about fish does NOT support POSIX exactly? & How? (Should I change my bash to fish?).
Please explain it simple 'cause I'm a little newbie, thanks.
fish isn't and never tried to be compatible with POSIX sh.
This really just means that it's a separate language (like Java, Python or Ruby) rather than an implementation or extension of sh (like Bash, Dash and Ksh).
Obviously, just like you can't copy-paste Java snippets into a Python program, you can't copy-paste sh code into fish.
In practice, this means that when you search for things like "how do I show the current git branch in my prompt", you need to make sure you find fish answers because the sh ones won't work. Similarly, when books or instructions give commands to run, you may occasionally need to rewrite some of them manually (or open a bash shell and paste them there).
Whether this matters is entirely up to you, so definitely give it a go.
Actually, fish is not compliant with the POSIX sh definition. But neither is csh (and probably zsh). You still can use fish as your interactive shell.
For example echo $$ shows the pid of the shell in POSIX sh. But with fish it does not.
(and that is why I did not switch to fish and keep using zsh as my daily interactive login shell)
You could change your interactive login shell (using chsh) to fish.
But if you write shell scripts, writing them for the POSIX sh specification make these scripts more portable. (You'll use the shebang #!/bin/sh to start them, it is understood by Linux execve(2)). In some cases, you don't care about portability of your shell script and you could make them start with #!/usr/bin/fish to be fish scripts. Then they won't work on systems without fish.
Also, the system(3) C standard library function uses /bin/sh -c.
I enjoyed very much Yann Regis-Gianas' talk on POSIX [s]hell at FOSDEM2018.

How to activate sh DTrace Provider?

I've found out about patches (and updates) for an x86 sh binary in 2007 to support sh DTrace provider.
However I can't find any probes on OS X. I'm checking by:
$ sudo dtrace -l | grep command-entry
See: /bin/sh DTrace Provider at Oracle site (same article on blog)
Maybe because my sh is the same as GNU bash?
$ /bin/sh --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)
Where these probes were implemented, how I can find them or how I can activate them?
Were there only implemented on Solaris?
I never added it to the bourne shell in a public Solaris (although I got the PSARC approval for it) as while testing I found some major performance hits in the way that the bourne shell forks. In order to address this I'd have to migrate it to using posix_spawn(). Given that we were moving away from using this shell in Solaris 11, this was an amount of work that would have been pretty fruitless. I haven't given up on going to other shells, but it's not one of my immediate priorities.
Moving the probes to another shell should be pretty painless, except for deciding what to return as the prior value in "variable-assign", when we have compound variables.
The patches you refer to were specific to the bourne shell on Solaris, and as far as I know nothing like it has ever been ported to other shells.
In the illumos (née OpenSolaris) commit history, we can see that /bin/sh was changed to be a symlink to ksh93 in commit ead1f93 on March 2, 2010. Considering that Alan's blog post is dated 2007, this wasn't included in Solaris 10, at least not initially since Solaris 10 was released in 2005. It may be in one of the updates post 2007 but I sort of doubt it. From the existing code, I can't even find what provided /bin/sh before the switch to ksh, which might mean it was one of the closed bits.
Either way, it was never part of any illumos release and I don't see any references to dtrace probes for command-entry in the illumos commit history. Unless Alan or somebody else close to the matter can speak up, it's probably lost to time.
In summary, as near as I can tell these patches were never included in any released product from Sun or otherwise. It will almost certainly have to be reimplemented from scratch on any existing open source shell.

How do I specify the Bourne shell (not sh)

I am writing a script and I want it to start with the Bourne shell. As I understand it, starting the script with:
#! /bin/sh
will not always specify the Bourne shell, but whatever the OS links to /bin/sh. Is there a way to explicitly specify Bourne?
Thanks!
The original Bourne Shell is not open source, so if you don't already have it, you're SOL.
If you do already have it, just put the location in the shebang.
Simple as that.
<Reminisce_mode_on>
Place a colon (:) on the first line. I used it on older systems
(A/UX, SCO Unix, Interactive Unix System V Release 3.2 SVR3, circa
1989, comes to mind) as the first character in a file to denote a
Bourne Shell script. It was also recognised by Thompson Toolkit's Korn
Shell (DOS > v3.2 ). Allowed Unix programmers to shell program in the
PC environment (DOS/WindowsNT).
</Reminisce_mode_off> Yeah, I'm old. Unix programmers never die, they just become zombie processes.
Depends on what you mean by "Bourne Shell".
Original Bourne Shell, which I believe is not what you are after:
proprietary, very limited, and only available on some true UNIX
systems, such as Solaris, derived from the original AT&T UNIX code.
#!/bin/sh is the way to get this non-portable shell.
Its ability to run true legacy scripts, and its smallness,
are about the only assets it has in my mind.
Today's memory gives the size asset less appeal.
Modern shells based on Bourne shell syntax. As opposed to, say csh.
bash, ksh, zsh are some. Bash is common in Linux environments, and yes, /bin/sh tends to link to one of these.
To use a specific shell, use something like which $SHELL to
get an absolute path.
#!/bin/env bash is less dependent on the shuffling of paths
found in different operating systems.
But this portable trick has the risk of choosing
the wrong shell if PATH is not set well enough and
does not allow arguments, such as -x, on the shell.
It has the advantage
of PATH being able to control which of several shells you use if
different versions of the same shell are available, which
ksh is well noted for.
So, if you want more help you will really need to detail the application you are after, and your definition of "Bourne Shell".

How can I determine my csh version?

I have some code which works for me, but when I gave it to some colleagues, it broke. They're using tcsh whereas, as far as I can figure, I'm using csh.
I've tried:
csh -v
csh --version
csh -V
csh --help
csh -h
with no success (they all just take me straight to the interpreter prompt). I've also grepped the man page for the string "version", but I didn't come up with anything useful there either. Is there a way to determine the version of csh that I have installed?
-- Edit --
Following the symbolic links from /bin/csh, they seem to terminate at /bin/bsd-csh which seems to imply that I'm using some BSD flavor csh shell if that helps anyone. Also, I'm using ubuntu linux.
In comments, you've indicated that you're on Ubuntu, and that /bin/csh is a symlink to /etc/alternatives/csh, which is a symlink to /bin/csh.
The csh shell originated on BSD Unix, so it's not surprising that csh is an indirect symlink to /bin/bsd-csh. It goes back to 1978 or so, before it became common for Unix program to report their own version numbers.
Since you're on Ubuntu, this:
dpkg -l csh
should tell you what version you have -- though the version number of the Debian/Ubuntu package isn't likely to be more useful than the information you already have. And the relationship between the package version number and the version of the shell isn't entirely clear.
I'm assuming that's the right package name. If not, try dpkg -S /bin/bsd-csh.
You can tell whether you're running tcsh or not, like this:
if ($?tcsh) then
echo This is tcsh
else
echo This is csh, not tcsh
endif
tcsh is supposed to be backward compatible with csh, with some extra features, mostly for interactive use. A script written for tcsh could easily fail under csh if it uses tcsh-specific features, but I'd expect tcsh to be able to handle a csh script. As the tcsh(1) man page says:
tcsh is an enhanced but completely compatible version of the Berkeley UNIX C shell, csh(1).
I understand you probably can't post the entire failing script, but can you post a small representative example that works for you and fails for your colleagues?
One solution might be to ask you colleagues to install vanilla csh on their systems; they can still use /bin/tcsh as their interactive shell if they like, but #!/bin/csh would then cause the script to be executed by the old csh, not tcsh.
Finally, I can't answer a question about [t]csh scripting without adding a link to this.
Addendum: I have access to a Solaris system with a /bin/csh that isn't tcsh; I can run some simple tests there if you like. One data point: both /bin/tcsh and /bin/csh accept : as a null command, but with /bin/csh it doesn't accept arguments:
% :
% arg
:: Too many arguments
csh --version would work only if csh is actually a symlink to... /bin/tcsh(!)
Otherwise, a csh session doesn't seem to set any version, except in this MKSToolkit, where that csh is supposed to set a variable $csh_version.
Pre-defined Variables
The following variables have special meaning to the C Shell.
Of these, argv, csh_version, cwd, home, path, prompt, ROOTDIR, shell, status, COMSPEC, and TMPDIR are always set by the shell.
Except for cwd and status, this setting occurs only at initialization; these variables are then not modified unless done explicitly by the user.
See this dotfile for instance:
shell_is_csh () { return [ -n "$csh_version" ]; }
/bin/csh links to /etc/alternatives/csh which links to /bin/bsd-csh.
Apparently it's bsd-csh
... then bsd-csh doesn't seem to support any kind of version feature.
If you're using Ubuntu, then your csh is the OpenBSD version, with a few patches. It has absolutely no support for getting its version, being that it doesn't encode its version anywhere. Can't help you beyond that, though.
If you've not installed csh on ubuntu, but have installed tcsh, then it will use tcsh as an alias to csh. They are, as you've discovered, mostly compatible.
For yourself, you can probably debug the issue by using update-alternatives to redirect csh to tcsh (as long as you've installed tcsh as well).

Resources