I have text file. Task - get first and last line from file after

$ cat file | grep -E "1|2|3|4" | commandtoprint

$ cat file
1
2
3
4
5

Need this without cat output (only 1 and 5).

~$ cat file | tee >(head -n 1) >(wc -l)
1
2
3
4
5
5
1

Maybe awk and more shorter solution exist...

  • In your second example, wc -l has nothing to do with outputting the last line of a file. – David Richerby Jun 26 '14 at 8:06
up vote 93 down vote accepted

sed Solution:

sed -e 1b -e '$!d' file

When reading from stdin if would look like this (for example ps -ef):

ps -ef | sed -e 1b -e '$!d'
UID        PID  PPID  C STIME TTY          TIME CMD
root      1931  1837  0 20:05 pts/0    00:00:00 sed -e 1b -e $!d

head & tail Solution:

(head -n1 && tail -n1) <file

When data is coming from a command (ps -ef):

ps -ef 2>&1 | (head -n1 && tail -n1)
UID        PID  PPID  C STIME TTY          TIME CMD
root      2068  1837  0 20:13 pts/0    00:00:00 -bash

awk Solution:

awk 'NR==1; END{print}' file

And also the piped example with ps -ef:

ps -ef | awk 'NR==1; END{print}'
UID        PID  PPID  C STIME TTY          TIME CMD
root      1935  1837  0 20:07 pts/0    00:00:00 awk NR==1; END{print}
  • Thank you! It's the best answer because i cannot do (head -n1 file;tail -n1 file) i have very big command and pipe as last symbol. So | sed '1p;$!d' shorter one. – dmgl Jun 25 '14 at 11:09
  • Sorry for my English, if you don't understand me - it's my problem - tell me about it and i prepare better presentation for you. – dmgl Jun 25 '14 at 11:12
  • 4
    Does head && tail really work for you? seq 10 | (head -n1 && tail -n1) prints 1 only. – glenn jackman Jun 26 '14 at 10:16
  • 1
    @DavidConrad, to elaborate on what chaos said, -e 1b -- on the first line, branch to the end of the sed script, at which point the implicit "print" happens. If the input is only one line long, sed ends. Otherwise, for all lines except the last, delete. On the last line, the implicit "print" happens again. – glenn jackman Jun 26 '14 at 19:10
  • 1
    @mikeserv: ;-) check it when you get a chance. On a single line it efectively prevents doubling it on output, but on a multiple line work-case, it just preserves the last line... at least on GNU sed 4.2.2. Cheers. – Cbhihe Sep 19 '16 at 23:15

sed -n '1p;$p' file.txt will print 1st and last line of file.txt .

  • 10
    Note that if the input has only one line, it will be printed twice. You may prefer sed -e 1b -e '$!d' if you don't want that. – Stéphane Chazelas Jun 25 '14 at 11:06

A funny pure Bash≥4 way:

cb() { (($1-1>0)) && unset "ary[$1-1]"; }
mapfile -t -C cb -c 1 ary < file

After this, you'll have an array ary with first field (i.e., with index 0) being the first line of file, and its last field being the last line of file. The callback cb (optional if you want to slurp all lines in the array) unsets all the intermediate lines so as to not clutter memory. As a free by-product, you'll also have the number of lines in the file (as the last index of the array+1).

Demo:

$ mapfile -t -C cb -c 1 ary < <(printf '%s\n' {a..z})
$ declare -p ary
declare -a ary='([0]="a" [25]="z")'
$ # With only one line
$ mapfile -t -C cb -c 1 ary < <(printf '%s\n' "only one line")
$ declare -p ary
declare -a ary='([0]="only one line")'
$ # With an empty file
$ mapfile -t -C cb -c 1 ary < <(:)
declare -a ary='()'

Using Perl:

$ seq 10 |  perl -ne 'print if 1..1 or eof'
1
10

The above prints the first item in the output of ls /etc via the if 1..1, while the or eof will also print the last item. Hence the output above is showing the results from ls /etc, only printing these 2 items but omitting everything else.

  • 3
    Could you explain how this works? Please avoid giving such one-liner answers without an explanation of what they do and how. – terdon Jun 25 '14 at 18:35
  • 1
    what do /etc abrt and yum.repos.d have to do this question? – drs Jun 25 '14 at 18:48
  • @drs I edited the answer to avoid using 'ls' as sample input. – dolmen Jun 26 '14 at 8:58
  • @dolmen: might be a good idea to finish editing by suppressing all reference to /etc. I guess such was added after yr first edit, but, all the same, as it stands the answer makes no sense at all. Just a suggestion. – Cbhihe Sep 18 '16 at 8:42

Without cat:

$ cat file |tee >(head -n1) >(tail -n1) >/dev/null
1
5

or

$ (head -n1 file;tail -n1 file)
1
5
  • 7
    In the first one, the order of the lines is not guaranteed. – Stéphane Chazelas Jun 25 '14 at 11:08
$ seq 100 | { IFS= read -r first; echo "$first"; tail -1; }
1
100

With sed you could delete lines if NOT the 1st one AND NOT the la$t one.
Use ! to NOT (negate) a condition and the X{Y..} construct to combine X AND Y conditions:

cmd | sed '1!{$!d}'

or you could use a range - from 2nd to la$t - and delete all lines in that range except the la$t line:

cmd | sed '2,${$!d}'

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.