Running console applications on Lin… er, OS X

Here’s a few small small OS X (or more generally, Unix and Unix-y) console application tips. All of them are pretty trivial, but I easily forget such things, so they’re for my own future benefit really…

  • If a application isn’t on the system PATH, then you always need to provide a path when running the program on the command line. Happily, relative paths are acceptable, and the symbol for ‘the current directory’ (i.e., a single full stop) is the same on Unix and Windows. For example, say you have a console program called MyUtil that has been put in ~/Applications. If in a Terminal window you go into that folder, the command for running the program will involve prefixing its name with a full stop and a forward slash:
  cd ~/Applications
  • There are numerous ‘standard’ locations to put console applications where they will be globally available — on my iMac, the system path (with no changes to it on my part) is composed of /opt/local/bin, opt/local/sbin, /usr/bin, /usr/sbin, /sbin, /usr/local/bin and /usr/X11/bin! Of the main Linux-y stuff I’ve installed, Wine has dumped a load of things in /opt/local/bin and Mono has liberally fly-tipped /usr/bin. Since out of the root directories, only /opt isn’t hidden by default in Finder (well, at least for me), I’ve put things of my own in /opt/local/bin. This still requires getting past the equivalent of a UAC prompt, though that isn’t a bad thing IMO.
  • When distributing a Delphi-produced console application, remember there is one dependency you can’t avoid – libcgunwind.1.0.dylib. This must be placed in the same directory as the executable. However, it is only 25KB. The much bigger, and program-specific, .rsm file the compiler produces should not be required – this is just for debugging.
  • When the user passes a parameter to a console application that contains a wildcard, the command-line interpreter will expand it before the application gets a chance to do anything. For example, say the command myutil *.jpg is entered, where MyUtil is a console application written in Delphi; inside of the application, ParamCount will return the number of *.jpg files in the current directory, ParamStr(1) the first *.jpg file, ParamStr(2) the second and so on.
  • If you want to prevent wildcard auto-expansion, wrap the parameter in quotes, just as you would wrap a parameter that contains one or more spaces in quotes:
  myutil "*.jpg"
  • ParamStr(0) on OS X will return whatever was typed by the user to start the program. If that was ./myutil , then ./myutil will be returned (you won’t notice this in a FireMonkey application, since a graphical OS X application is ‘bundled’, and so never run directly by the user). If you want a path, you can use GetModuleName(0) instead (as you can on Windows in fact). This however is likely to return the executable’s path concatenated with whatever ParamStr(0) returns, meaning both a superfluous ./ could be embedded and the ‘wrong’ casing used (on Windows, ParamStr(0) will return the file name with the case of the actual EXE file – MyUtil.exe not myutil.exe). To fix both, pass whatever GetModuleName returns to ExpandFileNameCase:
  function GetExecutableName: string;
    MatchedCase: TFilenameCaseMatch;
    Result := ExpandFileNameCase(GetModuleName(0), MatchedCase);

While unnecessary, this extra handling is harmless when targeting Windows, so you don’t need to IFDEF it when targeting both platforms.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s