Search code examples
bashsedruntime-error

sed - "unknown command" when replacing pattern with contents of file


I try to manipulate some "templated" text files. Meaning that the files contain some "tokens" which need to be replaced by some actual strings - either hard coded, or readable from other files.

I have the following 2 lines:

    sed -r -e 's_<!-- cssintern -->_<style type="text/css">\n<!-- cssintern -->\n</style>\n_' -i "$out_dir_aca/books_1by1/independent/$(basename "$aca_frag").html"
    sed -e '^<!-- cssintern -->^' -e '{r '"$src_dir_frags_struct/styles1.css.frag" -e 'd}' -i "$out_dir_aca/books_1by1/independent/$(basename "$aca_frag").html"

The first one works fine. The problem is that the second one generates an "unkown command" error, and I ran out of ideas how to fix it. The debugging output is the following (error text on last line):

+ for aca_frag in $out_dir_aca/frags_1by1/*
+ '[' -f '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/2_txt_out/aca/frags_1by1/09.14 - Acatistul inaltarii Sfintei Cruci.frag' ']'
++ basename '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/2_txt_out/aca/frags_1by1/09.14 - Acatistul inaltarii Sfintei Cruci.frag'
+ echo '    09.14 - Acatistul inaltarii Sfintei Cruci.frag'
    09.14 - Acatistul inaltarii Sfintei Cruci.frag
+ cat '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/1_txt_in/##_frags_struct_##/doctypeXHTML.frag'
++ basename '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/2_txt_out/aca/frags_1by1/09.14 - Acatistul inaltarii Sfintei Cruci.frag'
+ cat '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/1_txt_in/##_frags_struct_##/head.frag'
++ basename '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/2_txt_out/aca/frags_1by1/09.14 - Acatistul inaltarii Sfintei Cruci.frag'
+ cat '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/1_txt_in/##_frags_struct_##/foot.frag'
++ basename '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/2_txt_out/aca/frags_1by1/09.14 - Acatistul inaltarii Sfintei Cruci.frag'
++ basename '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/2_txt_out/aca/frags_1by1/09.14 - Acatistul inaltarii Sfintei Cruci.frag'
+ sed -r -e 's_<!-- cssintern -->_<style type="text/css">\n<!-- cssintern -->\n</style>\n_' -i '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/2_txt_out/aca/books_1by1/independent/09.14 - Acatistul inaltarii Sfintei Cruci.frag.html'
++ basename '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/2_txt_out/aca/frags_1by1/09.14 - Acatistul inaltarii Sfintei Cruci.frag'
+ sed -re '^<!-- cssintern -->^' -re '{r /home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/1_txt_in/##_frags_struct_##/styles1.css.frag' -re 'd}' -i '/home/vici/Dropbox/MyFiles/work/___rug/_!_workarea/2_txt_out/aca/books_1by1/independent/09.14 - Acatistul inaltarii Sfintei Cruci.frag.html'
sed: -e expression #1, char 1: unknown command: `^'

I used for separators ^, _, /, I played in "all" ways with simple and double quotation marks, and everything else that I found on the internet. Nothing changes the fact that the error remains.

In the past I used this line

sed.exe -i -e '/%%H/{r ..\/##_frags_##\/out\/%%H' -e 'd}' .\out\out.frag

in a Windows cmd script, and worked fine. (%%H references the name of the file, when parsing all the files in the folder with a for). I am actually rewriting the old scripts in Linux, as exercize - hopefully to add simplicity and extra features.

I intend to adapt the following sed command:

sed -e '/<TEXT1>/{r File1' -e 'd}' -i File2

to my particular needs.

Note: I provided the exact file names and paths, thinking that they might provide an insight.

Note: I use Debian 10.7 in a virtual machine.

Note: the script file starts with #!/bin/bash -xv. -xv is obviously used only when debugging, otherwise missing.


Solution

  • The syntax is (comment mine):

    [2addr] {editing command       # No newline between '[2addr]' and '{'!
    editing command
    ...
    }
    

    Where [2addr] is for example the /filtering/ expressions. The option -e does (emphasis mine):

    If any -e or -f options are specified, the script of editing commands shall initially be empty. The commands specified by each -e or -f option shall be added to the script in the order specified. When each addition is made, if the previous addition (if any) was from a -e option, a <newline> shall be inserted before the new addition. The resulting script shall have the same properties as the script operand, described in the OPERANDS section.

    When there is a newline between [2addr] and { the script is invalid:

    $ sed -e '/something/' -e '{' -e '}' /dev/null
    sed: -e expression #1, char 11: missing command
    $ sed '
    /something/
    {
    }' /dev/null
    sed: -e expression #1, char 13: unknown command: `
    '
    

    Putting the { with [2addr] fixes the problem as it removes the newline:

    $ sed -e '/somethimg/{' -e '}' /dev/null
    

    So put it with the filtering expression:

    sed
       -e '\^<!-- cssintern -->^{'
       #   ^^                  ^^ here '{' is with /expression/
       # or just: -e '/<!-- cssintern -->/{'
       -e 'r blabla/styles1.css.frag'
       -e d
       -e '}' -i 'blabla/12.17 - Acatistul Sfantului Proroc Daniel.frag.html'
    

    Should be fine.