-1

I searched on previous questions but they were only with appending so adding after the occurrence. i need it to be before:

 world world
 world world
 world world

So sed must add "hello" for example at the start of the Nth occurrence of a specific text. in this case im adding it to the fourth world:

world world
world hello world
world world
itnera
  • 3

4 Answers4

2

sed is the wrong tool for this job. Use awk or perl instead. e.g.

$ perl -pe 's/world/++$i == 4 ? "hello world" : $&/ge' input.txt 
world world
world hello world
world world

Note that this uses the /e perl regex modifer, which causes the replacement portion of the s/// substitute operator to be executed as perl code rather than be interpreted as a string.

That code ++$i == 4 ? "hello world" : $& pre-increments a counter variable ($i) on every match, and if it's equal to 4, replace the match with "hello world", otherwise replace the match with itself ($&).

cas
  • 78,579
  • If you want to actually edit the file in place (rather than just print the modified version to stdout), use perl's -i option. e.g. perl -i.bak -pe '...' – cas Jan 16 '22 at 12:14
  • Also worth mentioning - if your input text might contain other words that contain "world" within them (e.g. "underworld" or "worldliness" or "worldwide") that you don't want to be counted and possibly replaced, use the word-boundary marker (\b) in your match regex. e.g. /\bworld\b/ instead of just /world/ – cas Jan 16 '22 at 12:18
  • thank you for your answer but unfortunately im not allowed to neither awk or perl. How could this be done with sed. i saw there were questions here with appending so adding it after the text so im guessing it can be added before it also. – itnera Jan 16 '22 at 12:28
  • @itnera That seems like a wholly irrational and arbitrary restriction. What Unix are you using where you are not "allowed" to use standard tools? What sed can't do is counting. Therefore, any sed-only solution would be very much more complicated and quite likely also much more fragile and unmaintainable. – Kusalananda Jan 16 '22 at 12:32
  • its for a project and i am limited to only builtins and some specific binaries where sed tr grep and some others are included but not awk or perl. yeah it gets very complicated. for example based on replies from other questions sed ':a;$!{N;ba};s/(hello)/\1\nWORLD/1' test.json' adds text after occurrence. i need it to prepend before the text – itnera Jan 16 '22 at 12:39
  • "How could this be done in sed?". no idea. sed is a useful tool but this is not something that I would waste my time and sanity on to try doing in sed - not even for my own needs, let alone for someone's homework question. It took me a few minutes to do this in perl. I have no idea how long it might take me to figure out how to do in sed...I'd probably give up (and use perl or something) after trying & failing for half an hour. In fact, I'd rather write and compile a tiny C program to do it than sed, or use some other compiled language. – cas Jan 16 '22 at 12:43
  • @itnera If you have a JSON document, then use a JSON parser like jq. Please update your question to include the JSON document you are working with. It is trivial to do this with jq if the input is JSON. – Kusalananda Jan 16 '22 at 12:45
  • 1
    Your example makes it an even worse idea to use sed....and even using a perl regex isn't appropriate. json is structured text, and can't be correctly parsed or edited with regexes alone. You need a json parser. Perl has one of those (its JSON library module), as does python and many other languages. Or you can use a command-line json parser like jq – cas Jan 16 '22 at 12:47
  • yeah i have to write my own "query" to manage a json db but jq is not allowed either. anyways thank you ill try to find a workaround for this – itnera Jan 16 '22 at 12:51
  • @itnera Sorry, and with all due respect, but it seems like it's you who's trying to find a workaround. – Kusalananda Jan 16 '22 at 12:52
  • talk to whoever assigned the project to you, because this is a task that's trivial when using an appropriate tool (like jq) but doing it in sed would require a fairly advanced understanding of sed and how sed's buffers and pattern spaces work. – cas Jan 16 '22 at 12:55
  • Yeah okay thank you again! – itnera Jan 16 '22 at 12:59
2

If you absolutely must use sed, you may try the following, but as the comments already made clear the sed route will be rickety.

sed -e '
  H;1h;g
  s/world/HELLO &/4;tn
  $!d;:n;n;bn
' yourfile
guest_7
  • 5,728
  • 1
  • 7
  • 13
  • yes this worked perfectly. thank you! – itnera Jan 16 '22 at 16:38
  • 1
    @itnera are you sure? Try changing one of the first 3 worlds to otherworldly - is what it does then correct? You'd also get some interesting results given various input if your target string contained regexp metachars since it's doing a partial regexp comparison when you should really be doing a full word string comparison. Are the constructs being used all POSIX-compliant or are some GNU extensions? Does that matter? I've been using sed for 40 years and couldn't answer that because I'd never use it for a task like this. – Ed Morton Jan 16 '22 at 20:35
1

I know you said "using sed" but if you ever have to do something like this in the real world, here's how using any awk in any shell on every Unix box and doing a full-word string comparison (see how-do-i-find-the-text-that-matches-a-pattern for why that matters):

$ awk -v n=4 '{
    for (i=1;i<=NF;i++) {
        if ( ($i == "world") && (++cnt == n) ) {
            $i = "hello " $i
        }
    }
    print
}' file
world world
world hello world
world world

Imagine your input was:

$ cat file
google.com mygoole.comedy
googleycom google.com
google.com google.com

and you wanted to put "hello" before the 4th google.com (which is now the last one in the input). With the above awk script you just change $i=="world" to $i=="google.com":

$ cat file
awk -v n=4 '{
    for (i=1;i<=NF;i++) {
        if ( ($i == "google.com") && (++cnt == n) ) {
            $i = "hello " $i
        }
    }
    print
}' file
google.com mygoole.comedy
googleycom google.com
google.com hello google.com

Now try to do the same with a sed script (especially if you only use POSIX syntax and no GNU extensions). Now try using in&out as the replacement text instead of hello and you'll find more problems with the sed script.

Ed Morton
  • 31,617
  • Thank you for your answer. you raise a problem i didnt think of but in my case since im modifying a json file im only going to look for curly bracket occurrences so the previous answer will work just fine. thanks again for your time – itnera Jan 17 '22 at 23:27
0
sed 'H;1h;$!d;x;s/world/hello &/4'