159

I am trying to get a specific line from a text file.

So far, online I have only seen stuff like sed, (I can only use the sh -not bash or sed or anything like that). I need to do this only using a basic shell script.

cat file | while read line do #do something done 

I know how to iterate through lines, as shown above, but what if I just need to get the contents of a particular line

7
  • do you know the line number? Commented Oct 11, 2013 at 21:30
  • 1
    Then you get to count. Commented Oct 11, 2013 at 21:30
  • yes, the line number is 5 @MehulRathod Commented Oct 11, 2013 at 21:30
  • 5
    Why is cat okay but sed is not? That makes no sense. Commented Oct 11, 2013 at 23:50
  • 13
    Because no-one can say no to cat. Aw... cute cat! Commented Aug 6, 2015 at 15:58

13 Answers 13

282

sed:

sed '5!d' file 

awk:

awk 'NR==5' file 
Sign up to request clarification or add additional context in comments.

7 Comments

What about with the sh command, I cannot use sed, awk. I should make this more clear in the question.
@GangstaGraham you said you know how to iterate through lines, how about adding a counter? if the counter reaches your target line number, get the line and break the loop. does it help?
@KanagaveluSugumar read sed's info page. 5!d means delete all lines except 5. shell var is possible, you need double quotes.
I would suggest adding another variant: sed -n 5p This seems more logical to remember for newbies, because -n means "no output by default" and p stands for "print", and there's no potentially confusing mention of deleting (when people talk of files, deleting lines tends to mean something different).
@JosipRodin you are right, -n '5p' works for this problem too. The difference here is, with 5!d you can add -i to write the change back to the file. however, with -n 5p you have to sed -n '5p' f > f2&& mv f2 f again, for this question, I am agree with your opinion.
|
39

You could use sed -n 5p file.

You can also get a range, e.g., sed -n 5,10p file.

1 Comment

How would you also bring x files after/before (context), like in grep -C? Would be useful to easily seek for errors on source code.
37

Assuming line is a variable which holds your required line number, if you can use head and tail, then it is quite simple:

head -n $line file | tail -1 

If not, this should work:

x=0 want=5 cat lines | while read line; do x=$(( x+1 )) if [ $x -eq "$want" ]; then echo $line break fi done 

5 Comments

This -eq comparison is for integers, so it wants a line number, not line content ($line). This has to be fixed by defining e.g. want=5 prior to the loop, and then using the -eq comparison on $want. [moved from a rejected edit]
@JosipRodin I made an independent edit suggestion based on your comment, as I agree with it. Hopefully this time it won't be rejected.
This is massively much slower than @faithonour 's solution below
@AntonOfTheWoods while using sed is obviously faster, it does not provide an answer to the question. If you missed it, the question explicitly states that sed is not usable, and sh is the only tool available.
True, he did accept the answer though! :-)
27

Best performance method

sed '5q;d' file 

Because sed stops reading any lines after the 5th one

Update experiment from Mr. Roger Dueck

I installed wcanadian-insane (6.6MB) and compared sed -n 1p /usr/share/dict/words and sed '1q;d' /usr/share/dict/words using the time command; the first took 0.043s, the second only 0.002s, so using 'q' is definitely a performance improvement!

5 Comments

This is also commonly written: sed -n 5q
I like this solution because sed stops reading any lines after the 5th one.
I installed wcanadian-insane (6.6MB) and compared sed -n 1p /usr/share/dict/words and sed '1q;d' /usr/share/dict/words using the time command; the first took 0.043s, the second only 0.002s, so using 'q' is definitely a performance improvement!
Warning: using q in a sed command from a pipe will result in broken pipe. In that case, have to resort to sed -n 5p
@WilliamPursell sed -n 5q prints nothing for me. I think the answer is the best variant, but sed -n '5{p;q}' also works for me.
13

If for example you want to get the lines 10 to 20 of a file you can use each of these two methods:

head -n 20 york.txt | tail -11 

or

sed -n '10,20p' york.txt 

p in above command stands for printing.

Here's what you'll see: enter image description here

Comments

6

You could use sed command.

If the preferred line number is 5:

sed -n '5p' filename #get the 5th line and prints the value (p stands for print) 

If the preferred line number is a range, e.g. 1-5 lines:

sed -n '1,5p' filename #get the 1 to 5th line and prints the values 

If need to get 1st and 5th line only, e.g. 1st Line, 5th Line:

sed -n '1p;5p;' filename #get the 1st and 5th line values only 

Comments

2

The standard way to do this sort of thing is to use external tools. Disallowing the use of external tools while writing a shell script is absurd. However, if you really don't want to use external tools, you can print line 5 with:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file 

Note that this will print logical line 5. That is, if input-file contains line continuations, they will be counted as a single line. You can change this behavior by adding -r to the read command. (Which is probably the desired behavior.)

5 Comments

$((++i)) appears to be a bashism; if the OP is restricted in using external tools, I wouldn't assume they'll have access to more than a plain /bin/sh
@JosipRodin No, it's a POSIX feature (but support for ++ increments is specifically marked as optional).
@tripleee it doesn't work with modern dash as /bin/sh, so I would not rely upon it.
But a simple workaround like $((i+=1)) works in Dash, too.
$(($i+1)) is the simple workaround I was thinking of.
2

I didn't particularly like any of the answers.

Here is how I did it.

# Convert the file into an array of strings lines=(`cat "foo.txt"`) # Print out the lines via array index echo "${lines[0]}" echo "${lines[1]}" echo "${lines[5]}" 

Comments

2

Assuming the question was asked for bash, here is the fastest simplest way to do this.

readarray -t a <file ; echo ${a[5-1]} 

You may may discard array a when not needed anymore.

Comments

1

In parallel with William Pursell's answer, here is a simple construct which should work even in the original v7 Bourne shell (and thus also places where Bash is not available).

i=0 while read line; do i=`expr "$i" + 1` case $i in 5) echo "$line"; break;; esac done <file 

Notice also the optimization to break out of the loop when we have obtained the line we were looking for.

Comments

1
#!/bin/bash for i in {1..50} do line=$(sed "${i}q;d" file.txt) echo $line done 

Comments

0

Easy with perl! If you want to get line 1, 3 and 5 from a file, say /etc/passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd 

3 Comments

seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]' but smartmatch is experimental and it's use discouraged
Not a single one of the other solutions are this concise, or allows this much flexibility. (Why does it seem that everything that saves time and makes things easier, is "discouraged" by "smart people", are we all supposed to stare at screens all day?)
Glad to see some solution with perl which is a Swiss army knife for file filtering. I would suggest small enhancement -> cat /etc/password | perl -ne 'next unless ++$l == 4; print' if you are interested in one single line. To keep your flexibility without experimental -> cat /etc/password | perl -ne 'next unless $ref{++$l}; print; BEGIN{ %ref = map{$_=>1} (1,4,5)}
0
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}" 

3 Comments

could you describe a little at least why this work to make it clearer to the person who asked the question?
So, the first grep selects all the lines adding line numbers at their beginnings. Then the second grep selects a specific line by matching the line number at start. And finally the line number is trimmed from the line start in echo.
This is both complex and inefficient compared to sed -n 5p, which of course can still be optimized to something like sed -n '5!d;p;q'

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.