Signs of Triviality

Opinions, mostly my own, on the importance of being and other things.
[homepage] [index] [jschauma@netmeister.org] [@jschauma] [RSS]

Unpatch (!= patch -R)

I have a package that needs to add a text fragment, stored in fileA, to another file fileB. Due to the current requirements and the control I have over the file, I can make the assumption that if the first line of fileA is not present, the others won't be there, either. So this becomes a trivial two-liner:

if ! grep -q "$(head -1 fileA)" >/dev/null 2>&1;
then
        cat fileA >> fileB
fi

Now of course the tricky part is to remove those lines at the time the package is uninstalled. One approach would be:

  • read fileB line by line, write to temporary file
  • if we encounter the first line of fileA stop

Now this becomes tricky when other packages (or users) can manipulate fileB in the mean time, so there may be content following the last line of fileA that I do not want to remove.

Ouch!So I tried to play around with diff(1) and patch(1), attempting to diff fileA fileB and then reverse apply the patch. But that doesn't work: the resulting patch describes how to create a file that only contains the lines from fileA or, alternatively, a way to add the lines from fileB. Not quite what I want.

I then attempted to rewrite the patch on the fly into the reverse of the same patch, ie an "unpatch". And that kind of worked:

diff fileA fileB | \
        sed -e 's/^[0-9].*//' -e 's/^> //' -e '/./,/^$/!d'

But that seems rather silly. Another, more obvious approach would be to:

  • read fileB line by line, write to a temporary file
  • if we encounter the first line of fileA, stop writing
  • if we encounter the last line of fileB, continue writing

And guess what, our good friend sed(1) will happily do the right thing for us:

sed -e "/$(head -1 fileA)/,/$(tail -1 fileA)/d" fileB

You need double quotes to have the subshell expansion work. Add '/./,/^$/!d' to suppress multiple blank lines. And yes, this may break depending on the contents of fileA, for example if the first or last line contains a / (and which is why I actually did end up using the "unpatch" approach noted above). But it seemd entertaining enough at the time...

If you have a better or simpler way to do this (one-liner, please)...

March 2, 2012


[Just-in-time translation of user-provided LESS via NodeJS - Yikes!] [index] [Unix? What Unix? This is Linux!]