14

I have a file that contains data like this:

report aaaaaaaa  
-  ..  
-th bbbbbbbbb  
-to ccccccccc

.. --.

Question: I want to remove any line that does not start with following strings :

report  
-th  
-to

that means the desire output will remove all those middle unwanted dots and hashes and will look like this :

report aaaaaaaa  
-th bbbbbbbbb  
-to ccccccccc

sed/awk/grep/etc any solution that will work.

7 Answers 7

19

Using sed to modify the file in place:

sed -i '/^\(report\|-t\(h\|o\)\)/!d' your_file

This instructs sed to delete all lines not matching the pattern. The pattern itself is ^ (start of line), followed by either report or -t followed by either h or o.

You should note that this is not actual in-place modification: sed creates a temporary backup copy and overwrites the original file with it.

If you want sed to keep a backup copy of the original file (which might be a good idea if the file contains critical data), give the -i switch an extension to create a backup file:

sed -i'.bak' -e '/^\(report\|-t\(h\|o\)\)/!d' your_file

will modify your_file and create a backup of the original called your_file.bak.

A side note

Please don't misconstrue my intentions or take offence at this, but I've noticed that you have many similar regex/text-processing related questions. I advise you to start learning sed, awk and grep on your own to help speed up your productivity. Again, don't get me wrong, I'm all too happy to help (as are most people around here); it's just that I think that you stand to benefit hugely from picking up these tools for your daily use.

Just to prove how helpful people are around here, consider @slm's suggestion in the comments below and feel free to drop by this chatroom any time for questions.

7
  • 2
    Your regular expression seems unnecessarily cryptic. I think you actually use more characters than if you had just listed the three options explicitly.
    – nispio
    Nov 6, 2013 at 21:59
  • 1
    @nispio I know, but it's likely to be more efficient if the file in question is large.
    – Joseph R.
    Nov 6, 2013 at 22:01
  • Interesting. I have always measured regexps in terms of length or readability. I have never given much thought to execution speed. I don't think I know enough about how they are evaluated to judge what is fast, but I assume that it is implementation specific too, right?
    – nispio
    Nov 6, 2013 at 22:03
  • 3
    Re-iterating what Joseph said about being willing to help, if you have general questions that don't fit in the Q&A style you can always try chatting us up in the chat room for this site. chat.stackexchange.com/rooms/26/unix-and-linux. Several of us live in there 8-)
    – slm
    Nov 6, 2013 at 22:03
  • @slm Thank you for this. I will add it to my answer.
    – Joseph R.
    Nov 6, 2013 at 22:08
11

You can use simple grep for this:

grep -E '^(report|-th|-to)' filename

or, shorter,

grep -E '^(report|-t[ho])' filename
3
  • 1
    It's not much of a savings, but you could combine the -th/-to into -t[ho].
    – Kevin
    Nov 6, 2013 at 22:47
  • You're missing -v to negate this regex. This filter for lines that do start with those strings. Mar 28, 2022 at 13:53
  • @thisismydesign and that is exactly what is requested (the double negation in the question doesn’t help). Oct 17, 2022 at 17:30
3

Using awk:

awk '/^report|^-t[ho]/' file
0
2

Using sed:

sed -n -e '/^report\|^-th\|^-to/p' filename
0
1

Using Perl:

perl -ne 'print if /^report|^-t[ho]/' filename > newfile

or, to edit in place (like sed, perl will also make a temporary backup so this is not true in place editing):

perl -i.bak -ne 'print if /^report|^-t[ho]/' filename

That will make a copy of the original file called filename.bak and overwrite your original file with the edited version.

1

The questioner has made two points:

  • wanting to remove any line not starting with "report" or "-th" or "-to".
  • the desired output should remove "all those middle unwanted dots and hashes (sic)"

The solutions, at this time, address the first point and thereby also the second. But suppose the the file is larger and looks like:

report aaaaaaaa  
-  ..  
-th bbbbbbbbb  
-to ccccccccc
anything else
.. --.
-tp ddd
-tq eee
     -  -----

Wouldn't addressing OP's second point do the needful?

sed -r -i.bak '/^[ |.|-]*$/d' input-file 

does the job of removing presumably unwanted lines containing just spaces, dots and dashes and retaining the rest, whatever that is.
I would think that the risk of either approach is that the nature of the file isn't properly defined.

0

Using Raku (formerly known as Perl_6)

~$ raku -ne '.put if m/^ [ report | \-th | \-to ] /;' file.txt

Sample Input:

report aaaaaaaa  
-  ..  
-th bbbbbbbbb  
-to ccccccccc

.. --.

Sample Output:

report aaaaaaaa  
-th bbbbbbbbb  
-to ccccccccc

This is a similar answer to the Perl answer posted by @terdon. Raku's -ne command line flags autochomps the linewise input, but using put prints with an EOL terminator (remember put in Raku as an acronym for print-using-terminator). The call .put is short for $_.put, which outputs the $_ topic variable (i.e. the input line).

Otherwise, grouping regex atoms in Raku is accomplished by square brackets, and any non-alnum character must be escaped to be understood literally (otherwise Raku presumes it to have special Regex meaning, like . dot).

https://raku.org

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .