1

I try to place the line "name: *****" just after the corresponding (innermost containing) "ul" each time an "ul" appears, so that it looks like this:

Before:

<ul>
   <ul>
      <li href="https://www.deepl.com/translator">DeepL</li>
      <li href="https://translate.google.com">Google Trad</li>
      name: "Translate",
   </ul>
   <li href="https://www.youtube.com/feed/subscriptions">Youtube</li>
   <ul>
      <li href="https://www.facebook.com/">Facebook</li>
      <li href="https://twitter.com/">Twitter</li>
      <li href="https://www.instagram.com">Instagram</li>
      <li href="https://discordapp.com">Discord</li>
      name: "Network",
   </ul>
   name: "Fav",
</ul>

After:

<ul>
   name: "Fav",
   <ul>
      name: "Translate",
      <li href="https://www.deepl.com/translator">DeepL</li>
      <li href="https://translate.google.com">Google Trad</li>
   </ul>
   <li href="https://www.youtube.com/feed/subscriptions">Youtube</li>
   <ul>
      name: "Network",
      <li href="https://www.facebook.com/">Facebook</li>
      <li href="https://twitter.com/">Twitter</li>
      <li href="https://www.instagram.com">Instagram</li>
      <li href="https://discordapp.com">Discord</li>
   </ul>
</ul>

So, I already tested a lot of things, like this:

sed -i -e 'N;s/<ul>\([.\n]*\)\n\(.*\),/\2\n\1' fav.html

This and all I found to now doesn't work because the "name" following the last "ul" is not always the one to replace.  If anyone has an idea, I'd be happy to hear it.

1 Answer 1

0

This may be impossible in sed.  (The challenge has been issued; I await being proved wrong.)  If you specifically require a sed solution, you might as well stop reading this.

I was able to do it with a combination of tac and awk:

tac fav.html | awk '
    /<\/ul>/    { flag=1;            level++; }
    /<ul>/      { print save[level]; level--; }
    flag  &&  /name/    { flag=0; save[level] = $0; next; }
                { print; }
    ' | tac > fav.html.new  &&  mv fav.html.new fav.html

tac fav.html reverses fav.html on a line-by-line basis (tac is cat spelled backwards) and so produces

</ul>
   name: "Fav",
   </ul>
      name: "Network",
      <li href="https://discordapp.com">Discord</li>
           ︙
   <ul>
    ︙
   <ul>
<ul>

The first two lines of the awk code count the <ul> nesting level.  Since they are in reverse order, </ul> increases the level and <ul> decreases it.  When we see a </ul>, we set flag to indicate that we are entering a <ul> block from the bottom.  When we find a name near the bottom of a <ul> block, we save it, and skip to the next line (without printing the name line).  When we find a <ul> (i.e., the beginning of a <ul> block), we print the saved name before we print the <ul> itself.

The final tac reverses the lines again, putting most of them back where they were originally, and putting each name after its corresponding <ul>.

1
  • No, the challenge comes from me. I thought it was possible via the sed with knowledge I probably didn't have. But if this method exists, it suits me perfectly, as long as it does the job. I've never used "tak" and "awk" before, so you're teaching me something. So thanks for this !
    – Elex XVII
    Apr 1, 2019 at 20:29

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.