6

Is there a unix one liner to do this?

head -n 3 test.txt > out_dir/test.head.txt
grep hello test.txt > out_dir/test.tmp.txt
cat out_dir/test.head.txt out_dir/test.tmp.txt > out_dir/test.hello.txt
rm out_dir/test.head.txt out_dir/test.tmp.txt

I.e., I want to get the header and some grep lines from a given file, simultaneously.

Adrian Heine
  • 4,051
  • 2
  • 30
  • 43
Dnaiel
  • 7,622
  • 23
  • 67
  • 126
  • With your command sequence, if one of the first three lines contains `hello`, it is duplicated. I guess that's a bug? Otherwise, the proposed solutions are not equivalent in that regard. – user2719058 Nov 12 '13 at 21:20
  • @user2719058 good catch, you are right the first 3 lines do not contain the grep character. – Dnaiel Nov 13 '13 at 00:46

5 Answers5

9

Use awk:

awk 'NR<=3 || /hello/' test.txt > out_dir/test.hello.txt
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 1
    Whenever you find yourself doing multiple head to grep to sed to grep to tail to cut type of stuff, you should always think `awk`. Awk isn't that hard to learn for basic file manipulation, and can handle multiple tasks with aplomb. – David W. Nov 12 '13 at 18:32
  • @DavidW.: Very good point, awk can replace all those multiple pipeed command in a single command most of time. – anubhava Nov 12 '13 at 18:48
8

You can say:

{ head -n 3 test.txt ; grep hello test.txt ; } > out_dir/test.hello.txt
devnull
  • 118,548
  • 33
  • 236
  • 227
3

Try using sed

sed -n '1,3p; /hello/p' test.txt > out_dir/test.hello.txt
jkshah
  • 11,387
  • 6
  • 35
  • 45
  • 2
    What if `hello` appears in the first three lines? You'll double up that header line. – David W. Nov 12 '13 at 18:50
  • @DavidW. Aha! good point. I see you've already provided solution to it. devnull's solution will also go for a toss in such scenario – jkshah Nov 12 '13 at 19:14
3

The awk solution is the best, but I'll add a sed solution for completeness:

$ sed -n test.txt -e '1,3p' -e '4,$s/hello/hello/p' test.txt > $output_file

The -n says not to print out a line unless specified. The -e are the commands '1,3p prints ou the first three lines 4,$s/hello/hello/p looks for all lines that contain the word hello, and substitutes hello back in. The p on the end prints out all lines the substitution operated upon.

There should be a way of using 4,$g/HELLO/p, but I couldn't get it to work. It's been a long time since I really messed with sed.

Community
  • 1
  • 1
David W.
  • 105,218
  • 39
  • 216
  • 337
  • You are using a non portable `sed` option and removing lines from the original file which is not what is asked for. – jlliagre Nov 12 '13 at 20:37
  • @jlliagre The `-i` is a standard *Unix* mapping, but hasn't made it into GNU utilities that are found on Linux. I am surprised by this because GNU usually leads in options. The `-e` is on both GNU and Unix platforms as well as `-n`. You can remove the `-i` and pipe it to another file which will remove the _incompatible issue_. The `awk` solution is better, but this was for completeness. I'll modify my answer. – David W. Nov 12 '13 at 22:04
  • `sed` portable options are only `-n`, `-e` and `-f`. `-i` is a Gnuism, not sure why you wrote the opposite. – jlliagre Nov 12 '13 at 22:19
  • It's also in BSD versions of `sed`. This includes OS X. The man.cx which is suppose to be the GNU version of the manpage didn't show the `-i` option. However, linux.man.de do. I don't have access to Solaris, and the _newest_ version of the SunOS/Solaris webpage is from 1998. I've removed `-i` from my answer anyway. – David W. Nov 12 '13 at 23:07
  • (Legacy) BSD `sed`didn't have `-i`. This option was first introduced in 2001-09-25 by Paolo Bonzini in the GNU sed source code. It was later adopted by other implementations but is still not in latest Solaris according to its manual web page, from 2013: http://docs.oracle.com/cd/E26502_01/html/E29030/sed-1.html#scrolltoc. – jlliagre Nov 13 '13 at 01:21
2

Of course, I would go awk but here is an ed solution for the pre-vi nostalgics:

ed test.txt <<%
4,$ v/hello/d
w test.hello.txt
%
jlliagre
  • 29,783
  • 6
  • 61
  • 72