0

I have a file with the following structure:

4168  Targus
        1010  Wireless Compact Laser Mouse
4242  USB Design by Example
        4201  Buttons and Lights HID device
        4220  Echo 1 Camera
4255  GoPro
        1000  9FF2 [Digital Photo Display]
        2000  HD2-14 [Hero 2 Camera]
4317  Broadcom Corp.
        0700  U.S. Robotics USR5426 802.11g Adapter
        0701  U.S. Robotics USR5425 Wireless MAXg Adapter
        0711  Belkin F5D7051 v3000 802.11g
        0720  Dynex DX-BUSB
        0721  Dynex DX-EBUSB
4348  WinChipHead
        5523  USB->RS 232 adapter with Prolific PL 2303 chipset
        5537  13.56Mhz RFID Card Reader and Writer
        5584  CH34x printer adapter cable
4572  Shuttle, Inc.
        4572  Shuttle PN31 Remote

Here each section is separated by non-whitespace character. I do not know the beginning letter of each section and its line number in the file.

What is the way to read lined between two sections, either in bash or in python 3?

In the above example, the first section will be line 4168 till next a line before the next line that starts with a non-whitespace character. So the first section is having a line

1010  Wireless Compact Laser Mouse

while the second section consists of:

4201  Buttons and Lights HID device
4220  Echo 1 Camera

Section selection is done by the given input. e.g. if 4242 is input, then the desired section to read will be:

4201  Buttons and Lights HID device
4220  Echo 1 Camera

Please note that I do not know the line number of 4242.

4
  • Is the data that you show annotated with line numbers? What does the actual data look like? I'm referring to the text the first section will be line 4168, which seems to indicate that 4168 in the example text is a line number that you have inserted.
    – Kusalananda
    Dec 1, 2021 at 21:04
  • @they Although it is for the OP to answer, I would be surprised, because there are not 73 lines between the section Targus and USB Design by Example ...
    – AdminBee
    Dec 2, 2021 at 14:28
  • @AdminBee I was thinking the data presented here was for illustration, a few sampled lines from a larger file, with annotation to be helpful.
    – Kusalananda
    Dec 2, 2021 at 16:02
  • @they Hmmm, ok that didn't occur to me :)
    – AdminBee
    Dec 2, 2021 at 16:10

3 Answers 3

3
num=4242
sed -n '
  /^'"$num"'\b/,/^[^[:blank:]]/{
    /^[[:blank:]]/ {s/^[[:blank:]]*//;p}
  }' data_file

Tested in GNU sed 4.7.

After the variable is expanded, 4242 appears in the sed code. /^4242\b/,/^[^[:blank:]]/ defines a range from a line where 4242 is at the very beginning and is a full word (e.g. to tell apart from 42421) to the first following line that has no leading blank (tab or space).

Inside the range, lines that begin with blank (/^[[:blank:]]/) have leading blanks removed (s/^[[:blank:]]*//) and get printed (p).

Notes:

  • If there are two or more sections identified by 4242 then you will get lines from all of them without any indication the lines are from separate sections.
  • Run the code in a subshell ((num=…; sed …)) to avoid setting (or changing) the num variable in the current shell.
2

Mandatory awk solution:

awk -v sect="4242" '$0~/^[[:digit:]]/ {if ($1==sect) {p=1;next} else p=0} p' input.txt
  • The section is specified as awk variable sect via the -v statement.
  • If a line that immediately starts with a digit is found, this is taken as section start. If the section number matches the desired section, we set a flag p (for "print") to 1, but skip processing to the next line (so we don't print the section start). If the section number doesn't match, we set the flag to 0.
  • The current line is printed only if p is 1.

If you want to strip leading whitespace from the output, amend the program as follows:

awk -v ... '$0~/^[[:digit:]]/ {if ($1==sect) {p=1;next} else p=0}
            p{sub(/^[[:space:]]+/,""); print}' input.txt
0

I propose this solution, assuming there's no @'s in the content of the file:

$ sed -e 's/^\([0-9]\)/@\1/' -n -e '/@4317/,/@/p' file | sed -e '/^@/d' -e 's/^[[:blank:]]*//'
0700  U.S. Robotics USR5426 802.11g Adapter
0701  U.S. Robotics USR5425 Wireless MAXg Adapter
0711  Belkin F5D7051 v3000 802.11g
0720  Dynex DX-BUSB
0721  Dynex DX-EBUSB


  • 's/^\([0-9]\)/@\1/' replaces a line starting number for @.
  • '/@4317/,/@/p' selects the content between two @'s, given an identifier number.
  • sed -e '/^@/d' -e 's/^[[:blank:]]*//' deletes de lines starting with @ and the starting blanks.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .