How to Read the Second-To-Last Line in a File Using Bash

How to read the second-to-last line in a file using Bash?

Try this:

tail -2 yourfile | head -1

Print second last line from variable in bash

You almost done this task by yourself. Try

VAR="1\n2\n3"; printf -- "$VAR"|tail -2|head -1

How to insert a string into second to last line of a file

You can use sed to insert a line before a specific line, given it's position :

sed '4i\My New Line\' my_file.txt

Will insert "My New Line" on the fourth line

You can use wc -l to get the number of line in the file :

$ wc -l < my_file.txt
5

Complete example to directly answer your question :

$ cat my_file.txt
Hello
World
There is another line

$ sed -i "`wc -l < my_file.txt`i\\My New Line\\" my_file.txt

$ cat my_file.txt
Hello
World
My New Line
There is another line
  • Adding -i so sed actually edit the file
  • Using double quotes, otherwise substitution and expansion is not performed within single quotes.
  • Escaping \

Replace the pattern on second-to-last line of a file

To change Sweet to Green on the second to last line but only if that line contains Sweet pepper:

$ sed 'x; ${/Sweet pepper/s/Sweet/Green/;p;x}; 1d' file.txt
cabbage
spinach
collard greens
corn salad
Green pepper
kale

To replace the whole of the second to last line, regardless of what it contains, to carrots:

$ sed 'x; ${s/.*/carrots/;p;x}; 1d' file.txt
cabbage
spinach
collard greens
corn salad
carrots
kale

How it works

Let's take this command and examine it one step at a time:

sed 'x; ${s/.*/carrots/;p;x}; 1d'
  • x

    This exchanges the pattern space (which holds the most recently read line) and the hold space.

    When this is done, the hold space will contain the most recently read line and the pattern space will contain the previous line.
    (The exception is when we have just read the first line. In that case, the hold space will have the first line and the pattern space will be empty.)

  • ${s/.*/carrots/;p;x}

    When we are on the last line, indicated by the $, the pattern space holds the second to last line and we can perform whatever substitutions or other commands that we like. When we are done, we print the second to last line with p. Lastly, we swap pattern and hold space again with x so that the pattern space will again contain the last line. sed will print this because, by default, at the end of the commands, sed prints whatever is in the pattern space.

  • 1d

    When we are on the first line, indicated by the 1, the patten space is empty (because there was no previous line) and we delete it (d).

A still simpler method

This method is easy to understand at the cost of slower execution speed:

$ tac file.txt | sed '2 {/Sweet pepper/s/Sweet/Green/}' | tac
cabbage
spinach
collard greens
corn salad
Green pepper
kale

And, for carrots:

$ tac file.txt | sed '2 s/.*/carrots/' | tac
cabbage
spinach
collard greens
corn salad
carrots
kale

How it works: Here, we use tac to reverse the order of the lines. Observe:

$ tac file.txt
kale
Sweet pepper
corn salad
collard greens
spinach
cabbage

In this way, the second-to-last line becomes line number 2. Thus, we just simply tell sed to operate on line number 2. Afterward, we use tac again to put the lines but in correct order.

How to fetch the 13th and the second last line from the standard output

You never know which line is "second to last" when reading a stream until you read the actual last line and the stream ends. The only possible way, is to always buffer the previous line, and when the stream ends, output the line from the buffer.

For example with sed:

$ seq 20 | sed -n '13p;${x;p};h'
13
19

etc. with similar tools.

seq 20 | awk 'NR==13{print} {last=current; current=$0} END{print last}'

seq 20 | { n=1; while IFS= read -r line; do if ((n++ == 13)); then printf "%s\n" "$line"; fi; previous=$current; current=$line; done && printf "%s\n" "$previous"; }

But you can also just buffer the whole output and count the number of lines and output the second to last line....

# with a file
seq 20 > tempfile ; sed -n "13p;$(( $(wc -l < tempfile) - 1 ))p" file
# with memory
seq 20 | { buf=$(cat); sed -n "13p;$(( $(wc -l <<<"$buf") - 1 ))p" <<<"$buf"; }

Bash: grabbing the second line and last line of output (ls -lrS) only

Using awk:

ls -lrS | awk 'NR==2 { print; } END { print; }'

It prints when the line number NR is 2 and again on the final line.

Note: As pointed out in the comments, $0 may or may not be available in an END block depending on your awk version.

How to only read the last line from a text file

Use tail ;)

line=$(tail -n 1 input_file)
echo $line

How do I read the Nth line of a file and print it to a new file?

Sed can help you.

Recall that sed will normally process all lines in a file AND print each line in the file.

You can turn off that feature, and have sed only print lines of interest by matching a pattern or line number.

So, to print the 2nd line of file 2, you can say

sed -n '2p' file2 > newFile2

To print the 2nd line and then stop processing add the q (for quit) command (you also need braces to group the 2 commands together), i.e.

sed -n '2{p;q;}' file2 > newFile2

(if you are processing large files, this can be quite a time saving).

To make that more general, you can change the number to a variable that will hold a number, i.e.

  lineNo=3
sed -n "${lineNo}{p;q;}" file3 > newFile3

If you want all of your sliced lines to go into 1 file, then use the shells 'append-redirection', i.e.

 for lineNo in 1 2 3 4 5 ; do
sed -n "${lineNo}{p;q;}" file${lineNo} >> aggregateFile
done

The other postings, with using the results of find ... to drive your filelist, are an excellent approach.

I hope this helps.



Related Topics



Leave a reply



Submit