MacMan
MacMan

Reputation: 933

Find, Replace or Insert - command Line

I have a list similar to :

What do you want to do?
Import a text file by opening it in Excel
Import a text file by connecting to it
Export data to a text file by saving it
Change the delimiter that is used in a text file
Change the separator in all .csv text files

using SED I can find a match on 'connecting' and replace the line:

 sed 's^.*connecting.*^Import a text file by opening it^g' crontab

This should change the above list to :

What do you want to do?
Import a text file by opening it in Excel
Import a text file by opening it
Export data to a text file by saving it
Change the delimiter that is used in a text file
Change the separator in all .csv text files

However what I need to be able to do is:

if a line exists containing the word connecting, then replace that line, if the line doesn't exist add it to the end of the list as a new line.

I know I can do echo "Import a text file by opening it" >> list which would add the line to the end of the list, but is there anyway I can do this within one command ? or commands that can be run in one instance ?

Thanks

Upvotes: 0

Views: 430

Answers (2)

Wintermute
Wintermute

Reputation: 44063

An easy way is to use awk:

awk 'BEGIN { s = "Import a text file by opening it" } /connecting/ { $0 = s; n = 1 } 1; END { if(!n) print s }' filename

That works as follows:

BEGIN {                                    # Before anything else:
  s = "Import a text file by opening it"   # Remember the string by a shorter
                                           # name so we don't have to repeat it
}
/connecting/ {                             # If a line contains "connecting",
  $0 = s                                   # replace the line with that string
  n = 1                                    # and raise a flag that we've done so.
}
1                                          # print
END {                                      # in the end:
  if(!n) {                                 # If the string wasn't yet printed,
    print s                                # do it now.
  }
}

Alternatively, you can use sed's hold buffer. For example:

sed '1 { x; s/.*/Import a text file by opening it/; x; }; /connecting/ { s/.*//; x; }; $ { G; s/\n$//; }' filename

This works as follows:

1 {                                        # while processing the first line
  x                                        # swap hold buffer, pattern space
  s/.*/Import a text file by opening it/   # write text to pattern space
  x                                        # swap back.
}                                          # Now the hold buffer contains the
                                           # line we want to insert, and the
                                           # pattern space the first line.

/connecting/ {                             # For all lines: If a line contains
                                           # "connecting"
  s/.*//                                   # empty the pattern space
  x                                        # swap in hold buffer.
                                           # If this happened, the hold buffer
                                           # will be empty and the pattern space
                                           # will contain "Import a ..."
}
$ {                                        # Last line:
  G                                        # Append hold buffer to pattern space.
                                           # If the hold buffer is empty (i.e.,
                                           # was used somewhere else), this
                                           # appends a newline, so
  s/\n$//                                  # remove it if that happened.
}

Note that the sed code depends on the fact that there's only one line that contains "connecting." If there were more such lines, they would be replaced with empty lines because the hold buffer is empty when the second line comes around. It is possible to handle that case, but you'd have to decide what should happen in it. Since you replied in the comments that there's only one such line, I didn't feel the need to guess.

Upvotes: 2

Birei
Birei

Reputation: 36282

You can try , with similar regex syntax than but far more powerful for theses issues. It simply set a flag when at least one substitution has been done. After parsing the whole file, in the END {} block, add the comment if the $flag variable has not been set:

perl -pe '
    s/^.*connecting.*$/Import a text file by opening it/ and $done = 1;
    END { printf qq|%s\n|, q|Import a text file by opening it| unless $done }
' infile

When the line is found, it yields:

What do you want to do?
Import a text file by opening it in Excel
Import a text file by opening it
Export data to a text file by saving it
Change the delimiter that is used in a text file
Change the separator in all .csv text files

When it's not found, it yields:

What do you want to do?
Import a text file by opening it in Excel
Export data to a text file by saving it
Change the delimiter that is used in a text file
Change the separator in all .csv text files
Import a text file by opening it

If it appears more than once, change both but it doesn't append anything:

What do you want to do?
Import a text file by opening it in Excel
Import a text file by opening it
Export data to a text file by saving it
Change the delimiter that is used in a text file
Import a text file by opening it
Change the separator in all .csv text files

Upvotes: 0

Related Questions