I am writing a bash script to make a deployment tarball and the script must work across OSX and Linux.
I have a directory structure as shown below and in a bash script I need to tar the contents excluding ./foo
but including ./baz/foo
$ find . . ./bar ./baz ./baz/foo ./foo
Simple! Normally I would do this by passing --exclude='./foo'
to the tar command but I am finding the behaviour of --exclude=pattern
is different across OSX and CentOS.
My question is, how best to overcome this? How can I make both environments behave the same or at least detect the changes needed?
Or, can anyone confirm this behaviour for them (OSX 10.11) so that I know I haven’t got some weird environment?
CentOS works as expected:
$ tar zcfv ../test.tar.gz --exclude='./foo' . ./ ./bar/ ./baz/ ./baz/foo/
OSX behaves differently:
$ tar zcfv ../test.tar.gz --exclude='./foo' . a . a ./bar a ./baz
After hours of reading documentation I have found nothing to explain this different behaviour, but through trial and error I have found that using a caret to denote the start of the pattern works on OSX.
A fix for OSX (using caret):
$ tar zcfv ../test.tar.gz --exclude='^./foo' . a . a ./bar a ./baz a ./baz/foo
But doesn’t work on CentOS:
$ tar zcfv ../test.tar.gz --exclude='^./foo' . ./ ./bar/ ./foo/ ./baz/ ./baz/foo/
And finally, I notice that OSX ships with bsdtar instead of GNU tar, so is this the root of the problem?
OSX:
$ tar --version bsdtar 2.8.3 - libarchive 2.8.3
CentOS:
$ tar --version tar (GNU tar) 1.23
Advertisement
Answer
Thanks for the comments. It appears this is a fundamental difference between bsdtar and GNU tar in the implentation of --exclude=pattern
and there is no way to make them behave the same.
Since I have control of the OS environment my acceptable workaround is to install GNU tar on OSX using brew (thanks @Seer). I then use the following within my script to detect whether the installed tar is GNU, and fallback to gtar if not. If gtar is not installed it will abort.
# OSX uses bsdtar which causes problems with --exclude=pattern # so force use of gtar if non-GNU tar detected tar --version | grep -q 'gnu' if [ $? -eq 0 ] then echo "Detected GNU tar - using tar" TAR_BIN=`command -v tar 2>&1` || { echo >&2 "I require tar but it is not installed. Aborting."; exit 1; } else echo "Detected non-GNU tar - using gtar" TAR_BIN=`command -v gtar 2>&1` || { echo >&2 "I require gtar but it is not installed. Aborting."; exit 1; } fi # this will now work consistently as GNU tar is always used $TAR_BIN zcfv ../test.tar.gz --exclude='./foo' .
Note: CentOS at least actually aliases gtar to tar, so it may be safe to just assume the use of gtar command instead, but I prefer the above method.