21

Due to work I have recently started using OS X and have set it up using homebrew in order to get a similar experience as with Linux.

However, there are quite a few differences in their settings. Some only need to be in place on one system. As my dotfiles live in a git repository, I was wondering what kind of switch I could set in place, so that some configs are only read for Linux system and other for OS X.

As to dotfiles, I am referring, among other, to .bash_profiles or .bash_alias.

k0pernikus
  • 14,853
  • 21
  • 58
  • 79
  • I did this with git branches. I have one for FreeBSD, Gentoo and Ubuntu. But this is not ideal. – Raphael Ahrens Jul 17 '13 at 09:05
  • @RaphaelAhrens I want to avoid such a branch-based solution as it is prone to diverge. – k0pernikus Jul 17 '13 at 09:07
  • Yeah, you can make it a little bit easier when you put the system specific stuff into special files. But as i said not ideal. – Raphael Ahrens Jul 17 '13 at 09:09
  • http://stackoverflow.com/questions/394230/detect-the-os-from-a-bash-script You can check for Darwin on OS X. – Raphael Ahrens Jul 17 '13 at 09:19
  • My approach basically boils down to `if (exists rcfile.local); source rcfile.local; endif`, translated to the appropriate rc file. The main rc file I try to keep system agnostic, while the `.local` version has system specific settings. If you want it all in a single repo you can have system dirs and symlink the rcfile.local to the one in the correct directory. – jw013 Jul 17 '13 at 18:07

2 Answers2

22

Keep the dotfiles as portable as possible and avoid OS dependent settings or switches that require a particular version of a tool, e.g. avoid GNU syntax if you don't use GNU software on all systems.

You'll probably run into situations where it's desirable to use system specific settings. In that case use a switch statement with the individual settings:

case $(uname) in
  'Linux')   LS_OPTIONS='--color=auto --group-directories-first' ;;
  'FreeBSD') LS_OPTIONS='-Gh -D "%F %H:%M"' ;;
  'Darwin')  LS_OPTIONS='-h' ;;
esac

In case the configuration files of arbitrary applications require different options, you can check if the application provides compatibility switches or other mechanisms. For vim, for instance, you can check the version and patchlevel to support features older versions, or versions compiled with a different feature set, don't have. Example snippet from .vimrc:

if v:version >= 703
  if has("patch769")
    set matchpairs+=“:”
  endif
endif
Marco
  • 33,188
  • 10
  • 112
  • 146
  • `uname -s` is like `uname`. uname stands for Unix name. – Stéphane Chazelas Jul 17 '13 at 09:59
  • 1
    @StephaneChazelas It just happens to be or is it guaranteed across systems and I can always drop the `-s`? – Marco Jul 17 '13 at 10:01
  • 1
    yes, `uname` alone has been the canonical way to do it for decades and is specified by POSIX. The original `uname` (in PWB Unix) didn't take any option. – Stéphane Chazelas Jul 17 '13 at 10:09
  • @StephaneChazelas Thanks for the clarification, I removed the `-s` from this answer and I'll keep it in mind for my future scripts. – Marco Jul 17 '13 at 10:11
  • It's a relatively minor detail and doesn't detract from the point illustrated by your example, but are those quotes in the vimrc `set` really supposed to be U+201C and U+201D rather than U+0022? – user Jul 22 '13 at 11:16
  • @MichaelKjörling Yes, that's the whole point of this check. It allows jumping between matching quotation marks with `%`. Unicode support for `matchpairs` was added rather recently. This check allows me to use the same .vimrc for every vim version. U+0022 would not work (in any vim version) since it doesn't have a matching pair. Matching pairs need to be distinct characters. – Marco Jul 22 '13 at 11:44
4

If you are only concerned with files that are actually executed, such as .bash_profiles and friends, you may be able to get away with using e.g. uname to differentiate based on the system the code runs on.

For example, completely untested and with the caveat that I don't have an OS X to try things on, if you currently have on Linux:

alias ll='ls -lFA'

and on Mac OS X:

alias ll='ls -lFAx'

(where -x makes OS X's ls do something which GNU ls does by default), then they can be combined into something like this:

OS="$(uname -s)"
if test "$OS" = "Darwin"; then
    alias ll='ls -lFAx'
    # ...other OS X-specific things go here...
else if test "$OS" = "Linux"; then
    alias ll='ls -lFA'
    # ...other Linux-specific things go here...
fi
# ...generic things go here...

The only requirement then is that uname -s works in mostly the same way (it should, since both systems are reasonably POSIX-y and uname -s is required by POSIX (thanks Marco for pointing this out)), and that the syntax for shell script branching based on a string comparison is the same. You can probably test based on other criteria as well; e.g., you could look for /etc/lsb_release, check whether /proc/sys/kernel/ostype contains "Linux", or whatever other tests you can come up with.

user
  • 28,161
  • 13
  • 75
  • 138
  • @RaphaelAhrens It's `Darwin`, I just checked. – k0pernikus Jul 17 '13 at 09:25
  • @RaphaelAhrens Like I wrote, I don't have an OS X to try things on, so I took a wild guess to show the idea rather than spending a lot of time on one relatively insignificant detail which the OP can trivially find out. – user Jul 17 '13 at 09:28
  • Wikipedia to the rescue http://en.wikipedia.org/wiki/Uname if someone else wants there bash for Windows or other stuff. – Raphael Ahrens Jul 17 '13 at 09:30
  • 1
    The uname `-o` switch is not POSIX and fails on many systems, e.g. Solaris. `-s` is mandatory on POSIX and the most compatible way. [IEEE Std 1003.1](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/uname.html) – Marco Jul 17 '13 at 09:34
  • To safe a call to an external command (`uname`) you could use the variable `$OSTYPE` instead. – Slaven Rezic Jul 17 '13 at 09:49
  • 2
    `OSTYPE` is not available in a POSIX shell. It will fail for example on a default FreeBSD installation and can only be used in `.bashrc` or `.zshrc`. It doesn't work reliably in `.profile`, `.alias`, etc. Since we're talking about compatibiliy here, I'd advice to go the safe way, instead of relying on particular shell's features, which are not guaranteed to be available on every system. – Marco Jul 17 '13 at 09:54