0

Can anyone please suggest how to solve this? Suppose I have a file, and I want to replace the first occurrence of a pattern in matching line with some dynamic value. The pattern here is as.

source file:

a b c as aa as
as b d f aa sa
df as kf as df

target file:

a b c 1 aa as
2 b d f aa sa
df 3 kf as df

closed as unclear what you're asking by jasonwryan, Michael Homer, Thomas Dickey, mdpc, grochmal Jan 26 '17 at 2:48

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 2
    a "dynamic" value, meaning "starting at 1, and incrementing by 1"? – Jeff Schaller Jan 25 '17 at 21:25
3

Since the replacement pattern changes every time, this might be more of an awk thing:

awk 'BEGIN { needle=1 } /as/ { $0=gensub( /as/, needle, 1, $0 ); needle=needle+1} { print } ' /path/to/input

The above works with GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.4, GNU MP 6.1.0)

  • I have revised and retested; it will now also print lines that don't have a match for /as/. – DopeGhoti Jan 25 '17 at 21:45
3

If you really want to use sed, this can be done nicely within a while loop:

count=1;
while read line
    do sed 's/as/'"$count"'/1' <<< "$line";
    count=$((count+1));
done < source_file >> target_file

The enclosing '' pair of "$count" is needed for the shell to do the proper expansion of the variable for use inside the sed command.

Even shorter (thanks to Kusalananda):

while read line
    do sed 's/as/'"$(( ++count ))"'/1' <<< "$line";
done < source_file > target_file

If you only want to increment if pattern was found:

count=1;
while read line;
    do sed '/as/! {q100}; s/as/'"$count"'/1' <<< "$line";
    [[ "$?" -eq 0 ]] && (( ++count ));
done < source_file > target_file
  • No need for the ; (only useful if several statements on same line), and you could change the >> to just >. – Kusalananda Jan 25 '17 at 21:51
  • @Kusalananda: Correct, but I'll leave the semicolon, because of my personal coding style preference :) . Concerning the working of the >, this means the file is just opnened once and not over and over again after every iteration step. – FloHe Jan 25 '17 at 21:56
  • 1
    >> means "append to" while > means "truncate first". Putting either after the done will only ever open the file for writing once. Had you put the output redirection after the sed instead, it would have made more sense to use >> than > (> would have emptied the file in each iteration). However, putting >> after sed is slightly less efficient (opening the file several times) than putting > after done (opening the file once). – Kusalananda Jan 25 '17 at 22:01
  • 2
    The enclosing '' pair of "$count" is needed... : You can avoid this by enclosing the whole sed in double quotes : sed "s/as/$(( ++count ))/1". Same result but more pretty :-) – George Vasiliou Jan 25 '17 at 22:03
  • 2
    @George Vasiliou: Thanks, you never stop learning :-). – FloHe Jan 25 '17 at 22:06
1

With perl

$ perl -pe 'BEGIN{$n=1} s/as/$n++/e' file
a b c 1 aa as
2 b d f aa sa
df 3 kf as df
0

Here's a small python script that can do what you ask.

#!/usr/bin/env python
import sys
counter = 0
with open(sys.argv[1]) as fd:
    for line in fd:
        new_words = []
        words = line.strip().split()
        found = False
        for word in words:
            val = word
            if word == 'as' and not found:
                counter += 1
                found = True
                val = str(counter)
            new_words.append(val)

        print(" ".join(new_words))

And a test run:

$ ./substitute_dynamic_val.py input.txt                                                                                  
a b c 1 aa as
2 b d f aa sa
df 3 kf as df

Not the answer you're looking for? Browse other questions tagged or ask your own question.