Search code examples
awksedbusybox

Extracting data after a tag and CR with Busybox sed


I have a script that extracts a file from a bash script combined with a binary file. It does so using the following GNU sed syntax sed -n '/__DATA__/{n;:1;n;p;b1}' /tmp/combined.file > /tmp/binary.file

The files are assembled by cat'ing an ISO file to the end of a bash script. Which is then sent over the network to an embedded device and extracted on the device, piping the ISO file to a temporary dir and executing the bash script to install it.

However, on executing this I get a sed: unterminated {

Am I missing something here? Is this task possible with BusyBox sed?


Solution

  • It tried the "Second attempt" below with OSX/BSD awk and it failed, just printing up til the first NUL character. So you can't do this job portably with awk or sed.

    Here's what should work everywhere given that the POSIX standard says

    the input file to tail can be any type

    so the input to tail doesn't have to be a POSIX text file (no NULs) and we're exiting from awk before the first NUL is encountered in the input so they should both be happy:

    $ tail -n +"$(awk '/^__DATA__$/{print NR+2; exit}' binary.bin)" binary.bin | cat -ev
    ER^H^@^@^@M-^PM-^P^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@3��M-^Nռ^@|��f1�f1�fSfQ^FWM-^N�M-^N�R�^@|�^@^F�^@^A��K^F^@^@R�A��U1�0���^Sr^VM-^A�U�u^PM-^C�^At^Kf�^F�^F�B�^U�^B1�ZQ�^H�^S[^O��@PM-^C�?Q��SRP�^@|�^D^@f��^G�D^@^OM-^BM-^@^@f@M-^@�^B��fM-^A>@|��xpu   ��{�D|^@^@�M-^C^@isolinux.bin missing or corrupt.^M$
    f`f1�f^C^F�{f^S^V�{fRfP^FSj^Aj^PM-^I�f�6�{��^FM-^H�M-^H�M-^R�6�{M-^H�^H�A�^A^BM-^J^V�{�^SM-^Md^Pfa��^^^@Operating system load error.^M$
    ^��^NM-^J>b^D�^G�^P<$
    u��^X���^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@L^D^@^@^@^@^@^@�K�6^@^@M-^@^@^A^@^@?�M-^K^@^@^@^@^@`^\^@^@�������<R^@^@^@^_^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@U�EFI PART^@^@^A^@\^@^@^@]3�.^@^@^@^@^A^@^@^@^@^@^@^@�_^\^@^@^@^@^@@^@^@^@^@^@^@^@�_^\^@^@^@^@^@Uc�r^Oqc@M-^Rc^F�$LZ�^L^@^@^@^@^@^@^@�^@^@^@M-^@^@^@^@�t^]F^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@$
    

    Second attempt:

    Now that I have a better idea what you're trying to do (process a file consisting of POSIX text lines up to a point and then can contain NUL characters afterwards), try this:

    $ cat -ev file
    echo "I: Installation finished!"$
    exit 0$
    $
    __DATA__$
    $
    foo^@bar^@etc
    
    $ cat tst.awk
    /^__DATA__$/ { n=NR + 1 }
    n && (NR == n) { RS="\0"; ORS="" }
    n && (NR > n)  { print (c++ ? RS : "") $0 }
    
    $ awk -f tst.awk file | cat -ev
    foo^@bar^@etc
    

    The above doesn't try to store any input lines containing NUL in memory, instead it reads \n-terminated text lines until it reaches the line after the one containing __DATA__ and then switches to reading NUL-terminated records into memory and printing NULs between them on output.

    It's still undefined behavior per POSIX (see my comments below) but in theory it should work since it just relies on being able to set one variable (RS) to NUL rather than trying to store input strings that contain NULs. Also, setting RS to NUL has been a (flawed) workaround for awk scripts for years to be able to read a whole file into memory at once so being able to set RS to NUL should work in any modern awk.


    Using the new sample you provided with the missing blank line after the __DATA__ line added:

    $ cat -ev file
    #!/bin/bash$
    $
    echo "I: Awesome Things happened here"$
    exit 0$
    $
    __DATA__$
    $
    ER^H^@^@^@M-^PM-^P^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@3M-mM-zM-^NM-UM-<^@|M-{M-|f1M-[f1M-IfSfQ^FWM-^NM-]M-^NM-ERM->^@|M-?^@^FM-9^@^AM-sM-%M-jK^F^@^@RM-4AM-;M-*U1M-I0M-vM-yM-M^Sr^VM-^AM-{UM-*u^PM-^CM-a^At^KfM-G^FM-s^FM-4BM-k^UM-k^B1M-IZQM-4^HM-M^S[^OM-6M-F@PM-^CM-a?QM-wM-aSRPM-;^@|M-9^D^@fM-!M-0^GM-hD^@^OM-^BM-^@^@f@M-^@M-G^BM-bM-rfM-^A>@|M-{M-@xpu    M-zM-<M-l{M-jD|^@^@M-hM-^C^@isolinux.bin missing or corrupt.^M$
    f`f1M-Rf^C^FM-x{f^S^VM-|{fRfP^FSj^Aj^PM-^IM-ffM-w6M-h{M-@M-d^FM-^HM-aM-^HM-EM-^RM-v6M-n{M-^HM-F^HM-aAM-8^A^BM-^J^VM-r{M-M^SM-^Md^PfaM-CM-h^^^@Operating system load error.^M$
    ^M-,M-4^NM-^J>b^DM-3^GM-M^P<$
    uM-qM-M^XM-tM-kM-}^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@L^D^@^@^@^@^@^@M-/KM-66^@^@M-^@^@^A^@^@?M-`M-^K^@^@^@^@^@`^\^@^@M-~M-^?M-^?M-oM-~M-^?M-^?<R^@^@^@^_^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@UM-*EFI PART^@^@^A^@\^@^@^@]3M-%.^@^@^@^@^A^@^@^@^@^@^@^@M-^?_^\^@^@^@^@^@@^@^@^@^@^@^@^@M-J_^\^@^@^@^@^@UcM-)r^Oqc@M-^Rc^FM-2$LZM-p^L^@^@^@^@^@^@^@M-P^@^@^@M-^@^@^@^@M-{t^]F^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@$
    

    .

    $ awk -f tst.awk file | cat -ev
    ER^H^@^@^@M-^PM-^P^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@3M-mM-zM-^NM-UM-<^@|M-{M-|f1M-[f1M-IfSfQ^FWM-^NM-]M-^NM-ERM->^@|M-?^@^FM-9^@^AM-sM-%M-jK^F^@^@RM-4AM-;M-*U1M-I0M-vM-yM-M^Sr^VM-^AM-{UM-*u^PM-^CM-a^At^KfM-G^FM-s^FM-4BM-k^UM-k^B1M-IZQM-4^HM-M^S[^OM-6M-F@PM-^CM-a?QM-wM-aSRPM-;^@|M-9^D^@fM-!M-0^GM-hD^@^OM-^BM-^@^@f@M-^@M-G^BM-bM-rfM-^A>@|M-{M-@xpu    M-zM-<M-l{M-jD|^@^@M-hM-^C^@isolinux.bin missing or corrupt.^M$
    f`f1M-Rf^C^FM-x{f^S^VM-|{fRfP^FSj^Aj^PM-^IM-ffM-w6M-h{M-@M-d^FM-^HM-aM-^HM-EM-^RM-v6M-n{M-^HM-F^HM-aAM-8^A^BM-^J^VM-r{M-M^SM-^Md^PfaM-CM-h^^^@Operating system load error.^M$
    ^M-,M-4^NM-^J>b^DM-3^GM-M^P<$
    uM-qM-M^XM-tM-kM-}^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@L^D^@^@^@^@^@^@M-/KM-66^@^@M-^@^@^A^@^@?M-`M-^K^@^@^@^@^@`^\^@^@M-~M-^?M-^?M-oM-~M-^?M-^?<R^@^@^@^_^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@UM-*EFI PART^@^@^A^@\^@^@^@]3M-%.^@^@^@^@^A^@^@^@^@^@^@^@M-^?_^\^@^@^@^@^@@^@^@^@^@^@^@^@M-J_^\^@^@^@^@^@UcM-)r^Oqc@M-^Rc^FM-2$LZM-p^L^@^@^@^@^@^@^@M-P^@^@^@M-^@^@^@^@M-{t^]F^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@$
    

    Original answer:

    Assuming this question is related to your previous question, this will work using any awk in any shell on every UNIX box:

    $ awk '/^__DATA__$/{n=NR+1} n && NR>n' file
    3<ED>M-^PM-^PM-^PM-^PM-^
    

    When it finds __DATA__ it sets a variable n to the line number to start printing after and then when n is set prints every line for which the line number is greater than n.

    The above was run against this input file from your previous question:

    $ cat -ev file
    echo "I: Installation finished!"$
    exit 0$
    $
    __DATA__$
    $
    3<ED>M-^PM-^PM-^PM-^PM-^$