7

Example:

1:20 2:25 3:0.432 2:-17 10:12

I want to replace all strings that begin with 2: to 2:0.

Output:

1:20 2:0 3:0.432 2:0 10:12
  • 1
    What about strings matching 2: exactly? Should those be replaced? – kos May 13 '15 at 13:20
15

Using sed:

sed -E 's/((^| )2:)[^ ]*/\10/g' in > out

Also, as inspired by souravc's answer, if there is not a chance of a 2: substring after the start of a string not containing a leading 2: substring (e.g. there is not a chance of a 1:202:25 string, which the following shortened command would replace to 1:202:0), the command might be shortened to this:

sed -E 's/2:[^ ]*/2:0/g' in > out

Command #1 / #2 breakdown:

  • -E: makes sed intepret the pattern as an ERE (Extended Regular Expression) pattern;
  • > out: redirects stdout to out;

sed command #1 breakdown:

  • s: asserts to performs a substitution
  • /: starts the pattern
  • (: starts the capturing group
  • (: starts grouping the allowed strings
  • ^: matches the start of the line
  • |: separates the second allowed string
  • : matches a character
  • ): stops grouping the allowed strings
  • 2: matches a 2 character
  • :: matches a : character
  • ): stops the capturing group
  • [^ ]*: matches any number of characters not
  • /: stops the pattern / starts the replacement string
  • \1: backreference replaced with the first capturing group
  • 0: adds a 0 character
  • /: stops the replacement string / starts the pattern flags
  • g: asserts to perform the substitution globally, i.e. to substitute each occurence of the pattern in the line

sed command #2 breakdown:

  • s: asserts to performs a substitution
  • /: starts the pattern
  • 2: matches a 2 character
  • :: matches a : character
  • [^ ]*: matches any number of characters not
  • /: stops the pattern / starts the replacement string
  • 2:0: adds a 2:0 string
  • /: stops the replacement string / starts the pattern flags
  • g: asserts to perform the substitution globally, i.e. to substitute each occurence of the pattern in the line
  • Quick as always :) +1 – A.B. May 13 '15 at 9:23
4

This one liner using sed

sed -i.bkp 's/2:\([0-9]*\)\|2:\(-\)\([0-9]*\)/2:0/g' input_file

will in line replace globally in input_file keep a backup file named input_file.bkp at the same directory.

This can be further shorten using extended regexes as suggested by kos, as

sed -ri.bkp 's/2:\-?[0-9]*/2:0/g' input_file
  • If you want to match both strings starting with 2: and 2:- just make the - optional matching using extended regexes (-r option); also you don't need to capture anything since you're not replacing anything: sed -ri.bkp 's/2:\-?[0-9]*/2:0/g' input_file – kos May 13 '15 at 9:01
2

I would use a basic awk loop:

$ awk '{for (i=1; i<=NF; i++) $i~/^2:/ && $i="2:0"}1' file
1:20 2:0 3:0.432 2:0 10:12

This loops through all the fields. Whenever one of them starts with 2:, it replaces all of it with 2:0. Finally, the 1 stands for True, so that all the line is printed.

1

Using python:

#!/usr/bin/env python2
import re
with open('test_dir/unix_se.txt') as f:
    for line in f:
        print re.sub(r'(?:(?<=(?: 2:))|(?<=(?:^2:)))[^ ]*', '0', line).rstrip()

Here we haves used the re.sub function of re module.

  • re.sub() has the pattern sub(pattern, repl, string, count=0, flags=0)

  • As we will not be using the values inside the group further, we have used the non-capturing group notation (?:)

  • (?:(?<=(?: 2:))|(?<=(?:^2:))) uses the zero width positive look behind to match 2: at the start or followed by a space.

  • [^ ]* will match zero or more characters before space, after 2: and then replace them with 0.

Here is an example:

Input:

2:456 1:20 2:25 3:0.432 2:-17 10:12
1:20 2:25 3:0.432 2:-17 10:12 2:543 2:-78

Output:

2:0 1:20 2:0 3:0.432 2:0 10:12
1:20 2:0 3:0.432 2:0 10:12 2:0 2:0
0

Thx @kos for the sed version:

Some little modifications for the perl way:

perl -pe 's/((^|\s)2:)[^\s]*/${1}0/g' testdata

Write back with:

perl -i -pe 's/((^|\s)2:)[^\s]*/${1}0/g' testdata

Explanation:

((^|\s)2:)[^\s]*

Regular expression visualization

Debuggex Demo

  • 1st Capturing group ((^|\s)2:)

    • 2nd Capturing group (^|\s)
    • 1st Alternative: ^

      ^ assert position at start of the string

    • 2nd Alternative: \s

      \s match any white space character [\r\n\t\f ]

    2: matches the characters 2: literally

  • [^\s]* match a single character not present in the list below

    Quantifier: * Between zero and unlimited times, as many times as possible, giving back as needed [greedy]

    \s match any white space character [\r\n\t\f ]


Or with a Positive Lookbehind, thx @steeldriver

perl -pe 's/(?<=2:)\S*/0/g' testdata

Explanation

(?<=2:)\S*

Regular expression visualization

  • (?<=2:) Positive Lookbehind - Assert that the regex below can be matched

    2: matches the characters 2: literally

  • \S* match any non-white space character [^\r\n\t\f ]

    Quantifier: * Between zero and unlimited times, as many times as possible, giving back as needed [greedy]

Debuggex Demo

  • 2
    Couldn't you just use s/(?<=2:)\S*/0/g (replace any sequences of non-whitespace characters following 2: with 0)? – steeldriver May 13 '15 at 12:20
  • @steeldriver No nevermind, I misinterpreted your statement, your suggestion is good – kos May 13 '15 at 12:49
  • @steeldriver Yes, lookbehind and lookahead are great. – A.B. May 13 '15 at 12:52
  • 1
    @kos actually I maybe should have suggested S+ rather than S* which will 'replace' an empty string following 2: i.e. 2: becomes 2:0, which may not be the intended behaviour – steeldriver May 13 '15 at 13:01
  • @steeldriver I agree. OP should clarify this – kos May 13 '15 at 13:18

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.