How to read first and last line from cat output?
Solution 1
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}
Solution 2
sed -n '1p;$p' file.txt
will print 1st and last line of file.txt .
Solution 3
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='()'
Solution 4
With sed
you could d
elete lines if NOT the 1
st 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 2
nd to la$
t - and delete all lines in that range except the la$
t line:
cmd | sed '2,${$!d;}'
Solution 5
Using Perl:
$ seq 10 | perl -ne 'print if 1..1 or eof'
1
10
The above prints the first item in the output of seq 10
via the if 1..1
, while the or eof
will also print the last item.
Related videos on Youtube
dmgl
Updated on September 18, 2022Comments
-
dmgl over 1 year
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...
-
Stéphane Chazelas almost 10 yearsNote 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 almost 10 yearsIn the first one, the order of the lines is not guaranteed.
-
dmgl almost 10 yearsThank 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 almost 10 yearsSorry 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 almost 10 years@StéphaneChazelas Thank you for this Note! Demo: file with only 1. Command
$ cat file | sed '1p;$!d'
#return1 1
and$ cat file | awk 'NR==1; END{print}'
return1 1
-
gerrit almost 10 yearsCan we generalise this if we replace 'file' by an arbitrary process writing to stdout?
-
chaos almost 10 years@gerrit i added your suggestions
-
mikeserv almost 10 years@chaos - though in almost every situation it would not matter, there are certain rare cases in which you might need to ensure you always get the first and last line directly following one another. With sed you can do
1h;$!d;x;G;1g
- that way the output of the first and last lines will always instantaneously follow one another, and in the event of a single line input you'll always only get the singe line out. -
terdon almost 10 yearsCould you explain how this works? Please avoid giving such one-liner answers without an explanation of what they do and how.
-
drs almost 10 yearswhat do
/etc
abrt
andyum.repos.d
have to do this question? -
David Conrad almost 10 yearsCan you explain the sed one(s)? Even after a look at the sed man page, I'm still confused (this is a common occurrence for me with the sed man page).
-
dolmen almost 10 years@drs I edited the answer to avoid using 'ls' as sample input.
-
Angel Todorov almost 10 yearsDoes
head && tail
really work for you?seq 10 | (head -n1 && tail -n1)
prints1
only. -
chaos almost 10 years@glennjackman That annoyed me too a bit. The
head && tail
part is not always working. Switch to line buffered stream, then it works:stdbuf -oL seq 10 | (head -n1 && tail -n1)
print1
and10
for me. -
Angel Todorov almost 10 years@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. -
Cbhihe over 7 years@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 over 7 years@mikeserv: Why do you use
1g
at the end of your sed script ? Using it crushes the contents of the pattern space with that of the hold space, which is just the last line. Shouldn't you be already well served byseq 10 | sed '1h;$!d;x;G'
? -
mikeserv over 7 years@cbhihe - not sure - just on my phone w/ 3% charge and only a single sock, but i would guess it is to prevent getting the first line twice if input is only a single line
-
Cbhihe over 7 years@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. -
mikeserv over 7 years@cbhihe - you know i would check it, but the CVPD has my computer on a $150 hock and the rest of my things burned up with my house when they torched it. its a weir'd life.
-
mikeserv over 5 years@cbhihe - that expression does
$!d
... so the only case when a1g
should crush the output is a case of a oneline input file, barring a continued live scenario with more than one last line... -
Cbhihe over 5 years@mikeserv: yup. agreed ! But talk about long lived thread and necroposting... 4 yrs and going strong. Hope all yr woes from back in Sep.'16 are behind you.
-
mikeserv almost 5 years@cbhihe - the mad hatter said unbirthdays make us young again. the dormouse helped him drink his wine. but i say,
if ! kill -0 $$; then ! $0 -ba</; fi
-
Kjetil S. over 2 yearsGood answer. I used it for an alias:
alias firstlast='perl -ne "print if 1..1 or eof"'
. Now I canseq 10|firstlast
. With$.
containing the line number, replacing1..1
with$.==1
might be more readable for those unfamiliar with Perl's flip-flop dot-dot operator. -
crsuarezf about 2 years@glennjackman try with
(head -n1 && tail -n1) <<< $(seq 10)