Today, I started writing a Bash script which installed a bunch of software, and needed to create a new script which the user could execute. I wanted this installer to be a single file, so the question arose how to create a new script?
I really didn't want to 'echo' a bunch of lines piped into a test file, so I figured perhaps I could somehow embed the new script inside the one I was running. Needless to say, this proved more difficult than one would imagine, since Bash itself has very poor regular expression parsing, and all the command-line tools provided in Linux are very "line" centric.
After hours of head-bashing, this is basically what I came up with (this is simplified to show the concept):
#!/bin/sh # use grep to locate the byte which contains our marker FIND_MARK=`grep -b "\#MARK-\{2\}" $0` # extract the number part POS=`expr $FIND_MARK : "\([0-9]\+\)"` # get this file's size THIS_FILE=`cat $0` THIS_SIZE=`expr length "$THIS_FILE"` # calculate where to read FROM THE END BYTES=`expr $THIS_SIZE - $POS` # remember to drop the marker BYTES=`expr $BYTES - 7` # extract the part we want tail -c $BYTES $0 > test2.sh chmod +x test2.sh # we must always include this, to prevent accidentally running the rest exit #MARK-- #!/bin/sh echo "BOO!" exit
Is it pretty? Not by a long shot. I personally feel there's far too many variables involved here, but I can't seem to figure out how to get one command to use data from another quite the way I need it do (i.e. not using the venerable "|" piping).
I certainly hopes this saves others a bit of time.
AMENDMENT
After some experimentation, I've been able to reduce the size of the code required thus:
#!/bin/sh # use grep to locate the byte which contains our marker, and expr to extract the numberic data POS=`expr "\`grep -b \"\#MARK-\{2\}\" $0\`" : "\([0-9]\+\)"` # get this file's size by getting the length in bytes of a string dump of the file THIS_SIZE=`expr length "\`cat $0\`"` ## calculate where to read FROM THE END - remember to drop the marker (-6) BYTES=`expr \`expr $THIS_SIZE - $POS\` - 6` # extract the part we want tail -c $BYTES $0 > test2.sh chmod +x test2.sh # we must always include this, to prevent accidentally running the rest exit #MARK-- #!/bin/sh echo "BOO!" exit


