sed or Perl one liner + how to replace path in file only when full match

Viewed 150

We want to replace the path on /etc/fstab file from

/dev/sdb /var/kafka ext4 defaults,noatime 0 0

To ( expected output )

/dev/sdb /var/kafka/hadoop_kafka ext4 defaults,noatime 0 0

The sed syntax that we are wrote is

sed s'/\/var\/kafka/\/var\/kafka\/hadoop_kafka/g' /etc/fstab

so once we run then we get in fstab this

/dev/sdb /var/kafka/hadoop_kafka ext4 defaults,noatime 0 0

But once we run again the sed then we get wrong path in fstab as:

/dev/sdb /var/kafka/hadoop_kafka/hadoop_kafka ext4 defaults,noatime 0 0

So how to replace the path , only in case we match the path:

/var/kafka
4 Answers

The following 'awk' could assist you here

$ awk '($2=="/var/kafka"){$2="/var/kafka/hadoop_kafka"}1' file

The way awk operates is simple, its syntax, on the other hand, might be a bit unusual for the uninitiated. Awk operates on records, which by default are lines, and each record is spllit in fields. By default these fields are separated by a sequence of one or more blanks (spaces, tabs, ...). Per record, awk will process a sequence of pattern-action pairs written in the form (pattern){action}. You can read this very simply is If pattern is true, perform action. The default pattern is true and the default action is print.

When we look at the above we see the following two pattern-action pairs:

  • ($2=="/var/kafka"){$2="/var/kafka/hadoop_kafka"}. This we can translate as: If the second field equals "/var/kafka", then replace the second field with "/var/kafka/hadoop_kafka"
  • 1: This just says one, which equals to "true" and is a pattern, so we add the default action {print}. So this statement just prints the current line.

With your shown samples/attempts, please try following sed program.

sed -E 's/^([^ ]*\s+)(\/var\/kafka)(\s.*)/\1\2\/hadoop_kafka\3/ Input_file

Explanation: Firstly using -E option of sed to enable ERE(extended regex), then using s option to perform substitution here. In substitution part regex using back references concept(to keep matched values in a temp buffer memory, to be used later on in program).

  • ^([^ ]*\s+): Matching everything from till 1st occurrence of space comes in 1st capturing group.
  • (\/var\/kafka): Creating 2nd capturing group which makes sure that it matches /var/kafka in it.
  • (\s.*): Matching rest of the line here.

While performing substitution adding \/hadoop_kafka after 2nd capturing group as per requirement.

NOTE: Above code will print the values on terminal, once you are Happy with results use -i option to make a inplace save into Input_file.

You may try this sed:

sed -i.bak -E 's~(^|[[:blank:]])/var/kafka([[:blank:]])~\1/var/kafka/hadoop_kafka\2~g' /etc/fstab

RegEx Details:

  • (^|[[:blank:]]): Match start or a space or tab in capture group #1
  • /var/kafka: Match text /var/kafka
  • ([[:blank:]]): Match a space or tab in capture group #2
  • \1/var/kafka/hadoop_kafka\2: In replacement put back value captured in group #1 followed by /var/kafka/hadoop_kafka followed by value captured in group #2
perl -pe's{^\S+\s+/var/kafka\K(?=\s)}{/hadoop_kafka}'

How you'd use it:

perl -i -pe's{^\S+\s+/var/kafka\K(?=\s)}{/hadoop_kafka}' /etc/fstab

You want to replace the second field, which the is between whitespace after the leading sequence of non-whitespace.

s{
   ^ ( \S+ \s+ ) /var/kafka ( \s | $ )
}{
   $1 . "/var/kafka/hadoop_kafka" . $2
}ex

So, you want

perl -pe'
   s{
      ^ ( \S+ \s+ ) /var/kafka ( \s | $ )
   }{
      $1 . "/var/kafka/hadoop_kafka" . $2
   }ex
'

or the equivalent

perl -pe's{^\S+\s+/var/kafka\K(?=\s)}{/hadoop_kafka}'
Related