Skip to content
Advertisement

How to make “tar –exclude” behave consistently across *nix environments?

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.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement