简体   繁体   中英

Reading a subset of the lines in a text file, with bash

I have a file

line a - this is line a
line b - this is line b
line c - this is line c
line d - this is line d
line e - this is line e

The question is: How can I output the lines starting from "line b" till "line d" using bash commands? I mean, to obtain:

"line b - this is line b
 line c - this is line c
 line d - this is line d"
sed -n '/line b/,/line d/p' file

Your example is not enough to infer what you want in the general case, but assuming you want to remove the first and last line, you can simply use

tail -n+2 $filename | head -n-1

Here tail -n+2 prints all the lines starting from the second, and head -n-1 prints all the lines except the last.

for your set of sample data:

awk '/line b/,/line d/' file

Or

awk '/line d/{f=0;print}/line b/{f=1}f' file

If by bash, you mean actually bash alone, I can't help you. You really should be using the right tools for the job. If you mean standard UNIX utilities that you can call from bash, I would be using awk for that.

echo 'line a - this is line a
line b - this is line b
line c - this is line c
line d - this is line d
line e - this is line e' | awk '
    BEGIN {e=0}
    /^line b/ {e=1}
    /^line d/ {if (e==1) {print;exit}}
    {if (e==1) print}
'

This outputs:

line b - this is line b
line c - this is line c
line d - this is line d

The way it works is simple.

  • e is the echo flag, initially set to false (0).
  • when you find line b, set echo to true (1) - don't print yet. That will be handled by the last bullet point below.
  • when you find line d and echo is on, print it and exit.
  • when echo is on, print the line (this includes line b).

I've made an assumption here that you don't want to exit on a line d unless you're already echoing. If that's wrong, move the exit outside of the if statement for line d:

    /^line d/ {if (e==1) print;exit}

Then, if you get a line d before your line b, it will just exit without echoing anything.

The "/^line X/" -type clauses can be made very powerful to match pretty well anything you can throw at it.

You can do it using bash alone, though I agree with Pax that using other tools is probably a better solution. Here's a bash-only solution:

while read line
do
    t=${line#line b}
    if test "$t" != "$line"
    then
        echo $line
        while read line
        do
            echo $line
            t=${line#line d}
            if test "$t" != "$line"
            then
                exit 0
            fi
        done
    fi
done

Another approach which depends on what you mean:

pcregrep -m 'line b - this is line b
 line c - this is line c
 line d - this is line d' file

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM