0

I build the following sed cli in order to add the following line

force_https_protocol=PROTOCOL_TLSv1_2

after the line:

[security]

the sed cli :

sed  -i '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2'  /etc/ambari-agent/conf/ambari-agent.ini

but the problem is when the line - force_https_protocol=PROTOCOL_TLSv1_2 is already after [security]

how to change the sed syntax in order to ignore adding the line if line already exist?

expected output

[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

wrong file:

[security]
force_https_protocol=PROTOCOL_TLSv1_2
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE
2
  • Is it guaranteed that force_https_protocol=PROTOCOL_TLSv1_2, when already present, will only appear on the lines that immediately follow [security]? In other words, can we assume that instances of force_https_protocol=PROTOCOL_TLSv1_2 that appear elsewhere after [security] (if any) are not a concern to you?
    – fra-san
    Mar 15 '20 at 20:26
  • yes - we not need to append if the line is after [security] , or if the line is somewhere not after [security] , then we need to append
    – yael
    Mar 15 '20 at 20:29
0

Assuming that all "force_https_protocol=PROTOCOL_TLSv1_2" in file are within the [security] tag and do not appear elsewhere

sed -e '/^force_https_protocol=PROTOCOL_TLSv1_2$/d'\
    -e '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' file

The idea is to delete all lines in file containing (only) the "force_https_protocol=PROTOCOL_TLSv1_2" string.

Explanation

/^force_https_protocol=PROTOCOL_TLSv1_2$/d'

Delete all lines containing the "force_https_protocol=PROTOCOL_TLSv1_2" string.

Tests

$ cat file
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

$ sed -e '/^force_https_protocol=PROTOCOL_TLSv1_2$/d'\
      -e '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' file
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE
0
0

1. Using AWK

Here is a possible solution to the more general problem of adding a string to a labeled block of text (i.e. the lines of text that follow a specific label in a file and precede the next label or the ond-of-file) only if string is not present yet.

A simple approach is to walk the file backwards, keep track of the last time we encountered string, print it every time we encounter a specific label (just before printing the label itself), but only if we haven't seen it (string) yet, and forget about having encountered it (string) any time we come across a label. And, of course, reverse the result as the last step.

tac file | awk '
    /^force_https_protocol=PROTOCOL_TLSv1_2$/ {
        seen = 1
    }
    /^\[security\]$/ {
        if ( ! seen ) {
            print "force_https_protocol=PROTOCOL_TLSv1_2"
        }
    }
    /^\[[^]]*\]$/ {
        seen = 0
    }
    1' | tac

tac, included in GNU Coreutils, concatenates and reverses files line by line. How to reverse a file with other/standard tools is covered in several Q/As on U&L (for instance, How does the command sed '1!G;h;$!d' reverse the contents of a file? and Reverse grepping).

This will not insert force_https_protocol=PROTOCOL_TLSv1_2 after the first [security] in the following text snippet:

[security]
keysdir=/var/lib/ambari-agent/keys
force_https_protocol=PROTOCOL_TLSv1_2
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
...

To simply insert string after every occurrence of a label unless string is already there, the same approach may lead to:

tac file | awk '
    /^force_https_protocol=PROTOCOL_TLSv1_2$/ {
        seen = NR
    }
    /^\[security\]$/ {
        if ( ( NR == 1 ) || ! ( NR == seen + 1 ) ) {
            print "force_https_protocol=PROTOCOL_TLSv1_2"
        }
    }
    1' | tac

(This will insert force_https_protocol=PROTOCOL_TLSv1_2 after [security] in the above text sample).

2. Using sed

With sed you can use a variation of the N;P;D; cycle (which is abundantly covered in other Q/As on U&L; for instance: How can I use sed to replace a multi-line string? or How to edit next line after pattern using sed?).

This will insert the line force_https_protocol=PROTOCOL_TLSv1_2 after a line composed solely of [security] only if the former is not already there.

sed '
  $! {
    N
    P
    /^\[security\]\n/ {
      /\nforce_https_protocol=PROTOCOL_TLSv1_2$/ b b
      i\
force_https_protocol=PROTOCOL_TLSv1_2
    }
    :b
    D
  }
  s/^\[security\]$/&\
force_https_protocol=PROTOCOL_TLSv1_2/'

This script:

  • for each line, except for the last one ($!), appends a newline followed by the next line to the pattern space (N) (which already contains the current line);
  • prints the first line in the pattern space (P); if that line is [security]...
    • ...and the next line is force_https_protocol=PROTOCOL_TLSv1_2, just deletes the first line from the pattern space and starts a new cycle (b b, :b D);
    • otherwise, prints force_https_protocol=PROTOCOL_TLSv1_2 to standard output (i\) before deleting the first line from the pattern space and starting a new cycle (D);
  • if the last line is [security], replaces it with itself followed by a newline and force_https_protocol=PROTOCOL_TLSv1_2 and prints the result (by means of the implied p).

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.