0

I am trying to replace text after a specific word but sed is changing the whole line. Extract one word after a specific word

Input file: sample.txt

My Hostname:internal is valid.
some log file entries
some log file entries

Output:

My Hostname:mmphate
some log file entries
some log file entries

Expected output:

My Hostname:mmphate is valid.
some log file entries
some log file entries

I have written the below script which is changing all the words after Hostname: I want to change only one word after Hostname:

#!/usr/bin/env bash

HOST=$(curl -s 169.254.169.254/latest/meta-data/local-hostname)
while getopts ih opt
do
  case $opt in
  i)
    ;;
  h)
    sed -e "s/Hostname:.*/Hostname:$HOST/g" sample.txt
    echo "Updated Hostname: $HOST"
    ;;
  esac
done
  • if you want to change only that specific word, use that 's/Hostname:internal/Hostname:mmphate/' .. .* will match everything after Hostname: – Sundeep Mar 23 '17 at 5:23
  • i want to change word after Hostname: . not specific word like internal – Mahesh Phate Mar 23 '17 at 5:27
  • then add that detail to question and change example to show two or more different words to change – Sundeep Mar 23 '17 at 5:27
  • 1
    define word.. does it have only alphabets? both lower/upper case? is it always followed by space? depending on that, you can use various ways.. "s/Hostname:[^ ]*/Hostname:$HOST/g" is one – Sundeep Mar 23 '17 at 6:13
  • 1
    @Rakesh.N With ' instead of " this won't work as $HOST will not get expanded. – Philippos Mar 23 '17 at 6:39
0

.* matches everything in the rest of the line, so everything is replaced. If you want to replace everything upto the next space then you need [^ ] instead of.`

0

When you perform double quote interpolations on the rhs of s/// you need to be aware of escaping proplery the inputs, for they may be special to sed and hence result in an error or even worse, no errors, but a result that was totally unintended. Consider, e.g, what would happen if your $HOST comprised an ampersand & or a /.

# definitions
TAB=`echo 'x' | tr 'x' '\011'`; # tab
SPC=`echo 'x' | tr 'x' '\040'`; # space
eval "`echo 'n=qsq' | tr 'qs' '\047\012'`"; # newline

# construct regexes
s="[$SPC$TAB]";  # spc/tab regex
S="[^$SPC$TAB]"; # nonwhitespace regex

# perform the escape operation
esc() {
   set -- "${1//\\/\\\\}" # escape backslash to prevent it from dissolving
   set -- "${1//\//\\\/}" # escape forward slash to prevent from clashing with delimiters
   set -- "${1//&/\\&}"   # escape ampersand since it has a specific meaning rhs of s//
   set -- "${1//\"/\\\"}" # escape double quotes in an interpolation
   set -- "${1//$n/\\$n}" # escape newlines
   printf '%s\n' "$@"
}

# grab the hostname
HOST=$(curl -s 169.254.169.254/latest/meta-data/local-hostname)

# escape hostname to enable it to be used seamlessly on the rhs of s///
host_esc=$(esc "$HOST")

# and then...
sed -e "s/\(${s}Hostname\):$S$S*/\1:$host_esc/g" sample.txt
0

Your problem is not reproducable :

$ name="george"
$ echo "My Hostname:internal is valid." |sed "s/internal/$name/g"
My Hostname:george is valid.

In case the word internal exists also in the log entries bellow My Hostname then you can use something like this:

$ sed -r "s/(My Hostname:)(internal)/\1$name/g" file4
My Hostname:george is valid.
internal log entry
log internal entry

If you don't have GNU Sed or your sed does not work as expected for some reason, for such simple replacements you can always switch to perl which works with the same way everywhere:

$ cat file4
My Hostname:internal is valid.
internal log entry
log internal entry

$ perl -pe "s/(My Hostname:)(internal)/\1$name/g" file4
My Hostname:george is valid.
internal log entry
log internal entry

Both sed and perl can be combined with -i for in place editing if required.

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.