I am creating a self-extracting archive and I have got the basic thing going.

However I want to set the command that extracts the archive contents tail -n+$ARCHIVE_START_LINE $0 into a variable. I can't get this part to work

I have tried many things here - one is shown below for explanation. (Note this is a simplified example and the question is as much about understanding pipes etc as solving the problem.)

export TMPDIR=`mktemp -d /tmp/selfextract.XXXXXX`
ARCHIVE_START_LINE=`awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' $0`

# This works...
tail -n+$ARCHIVE_START_LINE $0 | tar xzv -C $TMPDIR

# .. but this is the kind of thing I want. But it doesn't work...
# I get: gzip: stdin is a multi-part gzip file -- not supported

I am on Ubuntu 12.04


All of your problems are explained in Why does my shell script choke on whitespace or other special characters?, but I'll repeat the relevant parts here.

First, $var in a shell script does not mean “the value of the variable var”, it means “take the value of the variable var, split it into words and interpret each word as a wildcard pattern”. To avoid these extra steps, put double quotes around variable substitutions: "$var" means “the value of the variable var”. Always put double quotes around variable substitutions and command substitutions unless you know and understand why need to leave them out.

Second, echo doesn't exactly print its argument: it interprets some options, and in some shells, it interprets backslash escape sequences. Furthermore echo prints a trailing newline, which you could remove with the -n option. Gzipped data never looks like an option and bash's echo treats backslashes literally by default, but in general, don't use echo on unknown data, use printf %s instead.

printf %s "$ARCHIVE_CONTENTS" | tar xzv -C "$TMPDIR"

This is guaranteed to feed the value of the variable ARCHIVE_CONTENTS to tar exactly.

The remaining problem, and one you can't solve without using a fundamentally different approach, is that most shells (all the shells I've seen apart from zsh) truncate the value of variables at the first null byte (this is because they use C null-terminated strings under the hood). So the variable ARCHIVE_CONTENTS in fact contains only the beginning of the archive file, up to the first null byte. You can't use a shell to manipulate binary data.


why not use a here document like in the old days ?

 export TMPDIR=`mktemp -d /tmp/selfextract.XXXXXX`

 ## other stuff

 base64 -d <<EOF | tar xvf -C $TMPDIR
 ## base64 encoded

you produce base64 encoded this way

 tar cf - my_dir | base64 > /some/place

(including it int self extract archive is left to the reader)


You may be inadvertently shell-expanding the contents of your archive. Try quoting your variable.




echo "$ARCHIVE_CONTENTS" | tar xzv -C $TMPDIR

You also may need to avoid appending a newline with echo -n

  • thanks. I tried both these but I still get the error gzip: stdin is a multi-part gzip file -- not supported – spiderplant0 Apr 12 '15 at 14:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.