-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
9
  • Unfortunately i cant use neither of those Commented Jan 16, 2022 at 12:29
  • 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 Commented Jan 16, 2022 at 12:35
  • 1
    @itnera You'd better revisit the requirements for that project then. Also let us know how you removed perl and awk from your Linux system without breaking it. Commented Jan 16, 2022 at 12:36
  • if i use forbidden binaries the project doesnt count and i fail its not that i cant "physically" use them Commented Jan 16, 2022 at 12:43
  • BTW, is your project using busybox? because most installations of busybox have a minimal-but-adequate version of awk built-in. Commented Jan 16, 2022 at 12:59

4 Answers 4

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
2
  • yes this worked perfectly. thank you! Commented Jan 16, 2022 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. Commented Jan 16, 2022 at 20:35
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 ($&).

12
  • 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 '...' Commented Jan 16, 2022 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/ Commented Jan 16, 2022 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. Commented Jan 16, 2022 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. Commented Jan 16, 2022 at 12:32
  • 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 Commented Jan 16, 2022 at 12:47
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.

1
  • 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 Commented Jan 17, 2022 at 23:27
0
sed 'H;1h;$!d;x;s/world/hello &/4' 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.