Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easy way of selecting certain lines from a file in a certain order

Tags:

linux

bash

I have a text file, with many lines. I also have a selected number of lines I want to print out, in certain order. Let's say, for example, "5, 3, 10, 6". In this order.

Is there some easy and "canonical" way of doing this? (with "standard" Linux tools, and bash)

When I tried the answers from this question

Bash tool to get nth line from a file

it always prints the lines in order they are in the file.

like image 850
Karel Bílek Avatar asked Jun 11 '14 14:06

Karel Bílek


People also ask

How do you select certain lines?

To select a line of text, place your cursor at the start of the line, and press Shift + down arrow. To select a paragraph, place your cursor at the start of the paragraph, and press Ctrl + Shift + down arrow.

How do you get the first 10 lines of a file?

To look at the first few lines of a file, type head filename, where filename is the name of the file you want to look at, and then press <Enter>. By default, head shows you the first 10 lines of a file. You can change this by typing head -number filename, where number is the number of lines you want to see.

Which command is used to extract specific lines records from a file?

The cut command offers many ways to extract portions of each line from a text file. It's similar to awk in some ways, but it has its own advantages and quirks.

How do you read a specific line from a text file in shell script?

Using the head and tail Commands Let's say we want to read line X. The idea is: First, we get line 1 to X using the head command: head -n X input. Then, we pipe the result from the first step to the tail command to get the last line: head -n X input | tail -1.


2 Answers

A one liner using sed:

for i in 5 3 10 6 ; do  sed -n "${i}p" < ff; done
like image 149
John C Avatar answered Sep 21 '22 15:09

John C


A rather efficient method if your file is not too large is to read it all in memory, in an array, one line per field using mapfile (this is a Bash ≥4 builtin):

mapfile -t array < file.txt

Then you can echo all the lines you want in any order, e.g.,

printf '%s\n' "${array[4]}" "${array[2]}" "${array[9]}" "${array[5]}"

to print the lines 5, 3, 10, 6. Now you'll feel it's a bit awkward that the array fields start with a 0 so that you have to offset your numbers. This can be easily cured with the -O option of mapfile:

mapfile -t -O 1 array < file.txt

this will start assigning to array at index 1, so that you can print your lines 5, 3, 10 and 6 as:

printf '%s\n' "${array[5]}" "${array[3]}" "${array[10]}" "${array[6]}"

Finally, you want to make a wrapper function for this:

printlines() {
    local i
    for i; do printf '%s\n' "${array[i]}"; done
}

so that you can just state:

printlines 5 3 10 6

And it's all pure Bash, no external tools!


As @glennjackmann suggests in the comments you can make the helper function also take care of reading the file (passed as argument):

printlinesof() {
    # $1 is filename
    # $2,... are the lines to print
    local i array
    mapfile -t -O 1 array < "$1" || return 1
    shift
    for i; do printf '%s\n' "${array[i]}"; done
}

Then you can use it as:

printlinesof file.txt 5 3 10 6

And if you also want to handle stdin:

printlinesof() {
    # $1 is filename or - for stdin
    # $2,... are the lines to print
    local i array file=$1
    [[ $file = - ]] && file=/dev/stdin
    mapfile -t -O 1 array < "$file" || return 1
    shift
    for i; do printf '%s\n' "${array[i]}"; done
}

so that

printf '%s\n' {a..z} | printlinesof - 5 3 10 6

will also work.

like image 29
gniourf_gniourf Avatar answered Sep 25 '22 15:09

gniourf_gniourf