277

Is there any bash command that will let you get the nth line of STDOUT?

That is to say, something that would take this

$ ls -l
-rw-r--r--@ 1 root  wheel my.txt
-rw-r--r--@ 1 root  wheel files.txt
-rw-r--r--@ 1 root  wheel here.txt

and do something like

$ ls -l | magic-command 2
-rw-r--r--@ 1 root  wheel files.txt

I realize this would be bad practice when writing scripts meant to be reused, BUT when working with the shell day to day it'd be useful to me to be able to filter my STDOUT in such a way.

I also realize this would be semi-trivial command to write (buffer STDOUT, return a specific line), but I want to know if there's some standard shell command to do this that would be available without me dropping a script into place.

1
  • 13
    I just voted this up for the use of word magic-command Commented Apr 26, 2019 at 14:22

12 Answers 12

416

Using sed, just for variety:

ls -l | sed -n 2p

Using this alternative, which looks more efficient since it stops reading the input when the required line is printed, may generate a SIGPIPE in the feeding process, which may in turn generate an unwanted error message:

ls -l | sed -n -e '2{p;q}'

I've seen that often enough that I usually use the first (which is easier to type, anyway), though ls is not a command that complains when it gets SIGPIPE.

For a range of lines:

ls -l | sed -n 2,4p

For several ranges of lines:

ls -l | sed -n -e 2,4p -e 20,30p
ls -l | sed -n -e '2,4p;20,30p'
Sign up to request clarification or add additional context in comments.

4 Comments

What does the p mean? like 2p 3p? In the sense I understand that it's for 2/3 line, but what's the theory/understanding behind it?
@LeoUfimtsev: the p is the sed statement that prints the lines identified by the range of lines that precedes it. Thus 2,4p means print lines 2, 3, 4.
And n means to NOT print all the other lines
Not "just for variety", but sed -n 1p is the only way I've come across to get the first line of output without setting exit status to SIGPIPE if the input is long enough (which makes the whole script fail if you have set -eo pipefail). Thank you so much!
112
ls -l | head -2 | tail -1

6 Comments

+2, but I'd suggest head -n 2 | tail -n 1 — modern heads and tails tend to issue warnings otherwise.
Using two processes to filter the data is a bit of overkill (but, given the power of machines these days, they'd probably cope). Generalizing to handle one range is not wholly trivial (for range N..M, you use head -n M | tail -n M-N+1), and generalizing to handle multiple ranges is not possible.
head piped into tail? horrible. Multiple better ways to do it.
Great thing about *nix command line: a million and one ways to do everything and everybody's got a favorite and a hates all the other ways... :-)
How to print a range of lines another way: $ ls -l | awk '{if (NR>=4 && NR<=64) print}' (can add more conditions easier, multiple ranges, etc...)
|
53

Alternative to the nice head / tail way:

ls -al | awk 'NR==2'

or

ls -al | sed -n '2p'

6 Comments

mine awk is better, but sed is nice.
No need for an "if" - see hacker's answer (except for the part about finding every file on the system). ;-)
what does '2p' stand for?
@shredding short answer print second line; long -> When used, only lines that match the pattern are given the command after the address. Briefly, when used with the /p flag, matching lines are printed twice: sed '/PATTERN/p' file. And of course PATTERN is any regular expression more
what if 2 is a variable? Every example uses single quotes, but with a var you need double quotes. Could we "program" awk to work also with single quotes when using vars? Example line =1; ls | awk 'NR==$line'. I know that bash uses double quotes with vars.
|
23

From sed1line:

# print line number 52
sed -n '52p'                 # method 1
sed '52!d'                   # method 2
sed '52q;d'                  # method 3, efficient on large files

From awk1line:

# print line number 52
awk 'NR==52'
awk 'NR==52 {print;exit}'          # more efficient on large files

Comments

9

For the sake of completeness ;-)

shorter code

find / | awk NR==3

shorter life

find / | awk 'NR==3 {print $0; exit}'

8 Comments

You're not kidding about completeness!
I am not ;-) Actually, I don' know why everyone's using ls — the original question was about someone's STDOUT, so I thought it's better to have it bigger.
At least with GNU awk, the default action is { print $0 }, so `awk 'NR==3' is a shorter way to write the same.
You should probably add "; exit" to that action. No point processing the rest of the lines when you're not going to do anything with them.
@camh: See Jonathan Leffer's answer about that: "may generate a SIGPIPE in the feeding process, which may in turn generate an unwanted error message".
|
7

Try this sed version:

ls -l | sed '2 ! d'

It says "delete all the lines that aren't the second one".

Comments

7

Another poster suggested

ls -l | head -2 | tail -1

but if you pipe head into tail, it looks like everything up to line N is processed twice.

Piping tail into head

ls -l | tail -n +2 | head -n1

would be more efficient?

Comments

6

You can use awk:

ls -l | awk 'NR==2'

Update

The above code will not get what we want because of off-by-one error: the ls -l command's first line is the total line. For that, the following revised code will work:

ls -l | awk 'NR==3'

Comments

4

Is Perl easily available to you?

$ perl -n -e 'if ($. == 7) { print; exit(0); }'

Obviously substitute whatever number you want for 7.

2 Comments

This is my fav since it's seems to be the easiest to generalised to what I was personally after which is every nth, perl -n -e 'if ($. %7==0) { print; exit(0); }'
@matkelcey also you can do $ awk 'NR % 7' filename to print every 7th line in file.
0

Yes, the most efficient way (as already pointed out by Jonathan Leffler) is to use sed with print & quit:

set -o pipefail                        # cf. help set
time -p ls -l | sed -n -e '2{p;q;}'    # only print the second line & quit (on Mac OS X)
echo "$?: ${PIPESTATUS[*]}"            # cf. man bash | less -p 'PIPESTATUS'

1 Comment

The use of pipefail and PIPESTATUS are very interesting and this adds a lot to this page.
0

Hmm

sed did not work in my case. I propose:

for "odd" lines 1,3,5,7... ls |awk '0 == (NR+1) % 2'

for "even" lines 2,4,6,8 ls |awk '0 == (NR) % 2'

Comments

-2

For more completeness..

ls -l | (for ((x=0;x<2;x++)) ; do read ; done ; head -n1)

Throw away lines until you get to the second, then print out the first line after that. So, it prints the 3rd line.

If it's just the second line..

ls -l | (read; head -n1)

Put as many 'read's as necessary.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.