As the title suggests — can I be reasonably sure that mktemp will exist on any unix-y operating system I'm likely to encounter?
POSIX does not seem to specify mktemp(1).
It looks like most modern systems have it, but the available functionality and the semantics of the options vary between implementations (so particular invocations may not be portable):
mktemp(1) from OpenBSD — mktemp(1) originated in OpenBSD 2.1
mktemp(1) from FreeBSD
mktemp(1) from Mac OS X — almost always the same as from FreeBSD
mktemp(1) from Todd C. Miller of sudo fame
mktemp(1) from Solaris
mktemp(1) from GNU coreutils
mktemp(1) from HP/UX — this one seems particularly divergent from most of the others listed here
So if you want a portable solution you may need to stick to functionality and options that mean the same thing on all of your platforms of interest.
A mktemp function (AKA mktemp(3)) first appeared in Unix V7 so it's likely to be everywhere. However, a mktemp command (aka mktemp(1)) first appeared, I believe, on OpenBSD 2.1, so if you have to deal with truly antediluvian Unix systems you might have to worry -- unless you can distribute the very portable mktemp.org version (to fix the potential lack of this utility on some customer's antediluvian system). How likely you are to encounter antediluvian system is nigh impossible for us to guess, of course -- e.g., in HP-UX, mktemp(1) has been around for at least 8 years (even most enterprises probably have updated their Unix OS's within that time frame), in Xenix I believe it appeared in 3.0 (in 1992), etc, etc.
FYI, mktemp appears to NOT be included with Solaris 9 (released 2002/2003) - just ran across this today:
$ uname -a
SunOS dcmnapp02 5.9 Generic_122300-47 sun4u sparc SUNW,Sun-Fire-V440
$ mktemp
bash: mktemp: command not found
$ man mktemp
bash-2.05$ man mktemp
Reformatting page. Please Wait... done
Standard C Library Functions mktemp(3C)
NAME
mktemp - make a unique file name
SYNOPSIS
#include
char *mktemp(char *template);
On Solaris 9 it's in package SMCmktemp, see http://sunfreeware.com/indexsparc9.html:
uname -s
SunOS
uname -r
5.9
/usr/sbin/pkgchk -l -p /usr/local/bin/mktemp
Pathname: /usr/local/bin/mktemp
Type: regular file
Expected mode: 0555
Expected owner: bin
Expected group: bin
Expected file size (bytes): 8884
Expected sum(1) of contents: 6493
Expected last modification: Nov 05 08:48:17 2002
Referenced by the following packages:
SMCmktemp
Current status: installed
Related
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.)
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.
I mean I can't use it in bash, is it not available on OS X, or is it just missing on my Mac?
It's not a PATH variable issue, because I searched with find command, and there's no file named setsid on my Mac at all.
If it's missing on OS X, is there any alternative to it?
Or if it's the case that I somehow deleted it accidentally, where can I find a copy of it?
use Brew:
brew install util-linux
Yes. /usr/bin/setsid is missing on Mac OS/X.
The OS interface is available, so based on the chapter 2 man page there may be some hope for porting the Linux source to Darwin.
While macOS does not come with a setsid command, it does come with scripting languages which support calling the setsid C function, such as Perl and Python. So, if you don't want to (or for some reason can't) install a setsid command via Homebrew (or MacPorts or whatever), another option is to write your own in a scripting language. As an example, try this Perl script (which I based off this with some minor changes):
#!/usr/bin/perl -w
use strict;
use POSIX qw(setsid);
fork() && exit(0);
setsid() or die "setsid failed: $!";
exec #ARGV;
If you don't like Perl, Python's os module has a setsid function too.
A simple demo, which relies on the fact that /dev/tty is an alias of your controlling terminal if you have one, but reads/writes to it fail with an IO error if you don't:
$ bash -c 'echo I have a controlling terminal. > /dev/tty'
I have a controlling terminal.
$ ./setsid.pl bash -c 'echo I have a controlling terminal. > /dev/tty'
bash: /dev/tty: Device not configured
$
(Warning: With the release of macOS Catalina (10.15) in 2019, Apple deprecated the Perl, Python, Ruby and Tcl language runtimes shipped with macOS – they say new software should not use them, and they may be removed in a future macOS version – and Apple is not going to update their versions, which are becoming increasingly outdated. However, they are still there in Monterey, and while I haven't upgraded to Ventura yet, I haven't heard anything about their removal in that version either. One obviously shouldn't rely on them for any supported applications – if such software needs one of these runtimes, it should install its own copy of them. However, if it is just for a quick hacky script to easily test how some program behaves without a controlling terminal, using these OS-bundled runtimes is still fine.)
Say, I want to remve a tmp/ dir in shell.
In linux (like ubuntu), I can rm tmp/ -fr or rm -fr tmp/. But in mac, I can only rm -fr tmp/.
Is there any way to config the mac shell (bash/zsh/...), so I can use the linux-like command style: rm tmp/ -fr?
Having options after operands as in rm tmp/ -fr is non-standard. IEEE Std 1003.1-2001 has in the section 12.2 Utility Syntax Guidelines:
Guideline 9:
All options should precede operands on the command line.
The implementors of the GNU utilities (as used by most Linux distributions) have chosen to add many non-standard extensions. While sometimes convenient, using these extionsions is inherently unportable. MacOS X has a userland derived from BSD, which does not have most of the non-standard GNU extensions. If you expect to be working with non-GNU systems such as BSD, Solaris or any other commercial UNIX in the future, it really pays to stick to standard syntax of utilities and not get used to any GNU extensions. It saves a lot of hassle when working with all the different UNIX operating systems out there. This is especially true and important when writing scripts. Relying on GNU syntax in scripts will make them unportable.
So instead of installing GNU coreutils on MacOS X, my advice would be to use it as an opportunity to get used to standard syntax (IEEE Std 1003.1, POSIX.2 etc.).
It depends on the commands that you use. OS X comes from the BSD family (via NeXTSTEP), and so their standard utilities mostly descend from BSD. There is a large degree of compatibility between BSD and Linux style commands, but every once in a while you run into implementation-specific things like this. If you really want to use the Linux versions of the commands, you can install the GNU coreutils package. It's available from Homebrew (recommended) and MacPorts. In general, though, it's a pain to install the GNU coreutils over the built in BSD toolchain, because of naming clashes and such.
It general it would depend on the implementation of the command you execute. However, as for rm (given you mean rm), since you used that as an example, you cannot. At least it will not do what you think.
rm -fr tmp/: will delete directory tmp/ with the options -r and -f, so recursive (here mandatory) and forced.
rm tmp/ -fr: will (attempt) to delete directory tmp/ and/or a file/directory
named -fr, but would produce an error: rm: tmp/ directory.
Note: the only other way to remove a file named -rf would be to use rm -- -rf.
I would like to compare all GNU Unix manuals and and Mac's Unix manuals by sdiff.
I do not know how you go through, for instance, all Mac's Unix manuals and then save them to a file.
The comparison can be done by the following code when the manuals are in two files
sdiff <(file1) <(file2)
Perhaps, there is some index of Unix command names such that we can do the following
sdiff <(man *[in the index]) <(man *[in the index])
How can you compare all GNU Unix manuals with all Unix manuals in Mac?
[edit]
Mac's manuals are at /usr/share/man/man[1-9]/*.
I have an encoding problem with them when I try to cat them.
Another problem is to find the location of Coreutils' manuals.
Your goal, to identify the differing parameters for the different BSD vs GNU/Linux versions of the various programs, is going to be somewhat tedious. It's useful to note that there are other variants of all commands as well. There are system V versions and BSD versions and GNU versions, and the Mac uses a mish-mash of all 3. In any event, as a starting point, the files themselves are filled with formatting macros that you have no interest in. Pipe the output of man through 'col -b' to get data you can diff. In terms of generating the list of commands, you could just ls -1 /bin /usr/bin' Then something like this would get you most of the way:
while read command ; do
man $command | col -b > output1
man ./path/to/GNU/$command | col -b > output2
diff output1 output2 | grep '^[ ]*-' > $command.diffs
done<<EOF
diff
grep
sort
...
...
EOF
GNU means (G)NU is (N)ot (U)nix. GNU is not based, in any way on UNIX, it could not be due to copyright and licensing issues.
Most GNU documentation was written in texinfo format (which Debian later converted to roff (man) format as users wanted man pages). The documentation is in no way based upon the BSD documentation, everything in GNU was written from scratch.
Trying to diff between the two is like diffing a dictionary against a thesaurus. You will find that they both contain many of the same words, but are entirely different books written by entirely different people.
The documentation in no way adequately explains the differences between GNU and BSD (and by extension MacOS).
All man pages exist within the /usr/share/man/*; I'm not sure what you are attempting to accomplish here. Mac runs on BSD, so most applications are going to be the same as the ones you would find on the BSD machine. If you still wanted to do it, you would need to grab the manual pages of the same applications in *nix as in Mac as well as the same version (since the man page can change). And yea, I would say that doing a diff /usr/share/man/man[0-9]/* and the expanded tar of all the man pages from the linux box.