Search code examples
bashescapingbsdtar

How to escape chars in BSD tar path substitution option


This does not work:

$ tar -cf /tmp/z.tar -C /space/myroot -s '/^\.svn\/patches/__patches' src .svn/patches
tar: Invalid regular expression: trailing backslash (\)
Also no good:
$ tar -cf /tmp/z.tar -C /space/myroot -s '/^\\.svn\\/patches/__patches' src .svn/patches
tar: Invalid replacement flag _
Every combination of backslashes (up to 4) and quoting gives me one of those two errors. However I know the first one works in sed:
$ tar -cf /tmp/z.tar -C /space/myroot src .svn/patches
$ tar -tf /tmp/z.tar | sed 's/^\.svn\/patches/__patches/'
src/a.c
src/b.c
__patches/A.patch
__patches/B.patch
Also I know I can cop-out and do this:
$ tar -cf /tmp/z.tar -C /space/myroot -s '/^.svn.patches/__patches' src .svn/patches
-- but I don't really want to.

Solution

  • From man bsdtar:

    -s pattern
    ....
    where old is a basic regular expression
    ...

    Basic regular expression should work with \/. Sadly bsdtar is incorrectly written here, and does not handle escaping correctly.

    From bsdtar/subst.c:

    end_pattern = strchr(rule_text + 1, *rule_text);
    

    rule_text is a string char * which is passed with the string you pass to -s option, so it has the string /^\.svn\/patches/__patches. The end_pattern should point to the last character of the pattern (ie. to the / in from of /__patches). However as simple strchr is used, so the first / found is used (the character in \/), escaped or not, it is used. The error:

    tar: Invalid regular expression: trailing backslash (\)
    

    comes from regcomp trying to parse the regular expression ^\.svn\ (as this is the part stripped between the two /). As there is nothing behind the trailing backslash \ in the expression, it throws an error (and is correct - a trailing backslash is there). One could think of posting a bug report to bsdtar developers, but it's probably not really worth fixing.

    Note that the syntax for -s is /old/new/. There is a trailing / in the pattern, which you are missing. After the last / you can specify flags.

    You can however workaround the issue with:

    bsdtar -s '#\.svn/patches#__patches#'
    

    Any character will work for the /.