I have a file A.tsv (field separator = \t):

for Research Use Only                       
Test Name   Il          
Run ID  2102                
Run Date    2021-02-04                  
Report Date/Time    2021-02-05 08:48                    
Instrument Serial                       
Flow Cell ID                    
Software Version                    
[Quality Control]                       
Lane 1,2,3,4    PASS                    
Lane 1,2,3,4 Index Set 1    PASS                    
[Patient Sample Results]                        
Sample ID   Internal Control    Result  Consensus Sequence  Lane    Index Set   Index ID
207 Pass        Not Available   1,2,3,4 1   UDP0001
205 Pass        Not Available   1,2,3,4 1   UDP0002

[Control Sample Results]                        
Sample ID   Control Type    Human Control   SARS-CoV-2  Lane    Index Set   Index ID
CONTROL-POS Positive Control    Not Detected    Detected    1,2,3,4 1   UDP0008

I want to only print those line in a new file :

Sample ID   Internal Control    Result  Consensus Sequence  Lane    Index Set   Index ID
207 Pass        Not Available   1,2,3,4 1   UDP0001
205 Pass        Not Available   1,2,3,4 1   UDP0002
CONTROL-POS Positive Control    Not Detected    Detected    1,2,3,4 1   UDP0008

So I want to print the lines after [Patient Sample Results] and [Control Sample Results], but only print the header line once.

That file is a subsampling and it can't be hardcoded be using line number.

So I tried somthing like :

awk '/Patient Sample Results/{getline; print}' A.tsv > data_info.tsv

But it prints only the line after the first pattern. Do you have a solution for my problem?

  • So if we call [ ... ] a "section title", you want to print all sections starting with [Patient Sample Results], excluding the section title, and including the header line, but only once? Are there further sections after the two yuo want printed, and do you want to exclude any of them (or just print all)? Is the "header line" always the first one after the section title? Are there empty lines that need to be treated specially? Also, can we assume that all sections are separated by exactly one empty line?
    – AdminBee
    Feb 11, 2021 at 8:33
  • Do the lines in your actual data have the trailing spaces/tabs that are present in the data in the question?
    – Kusalananda
    Feb 11, 2021 at 8:37

4 Answers 4


Assuming that your [ ... ] sections are separated by empty lines (which must not contain whitespace/tabs), and you want to print the content of all sections starting with [Patient Sample Results], the following should work:

awk -F"\n" -v RS="" '$1~/^\[Patient Sample Results\]/{s=2}
                     s{for (i=s;i<=NF;i++) print $i; s=3}' A.tsv > data_info.tsv

This will instruct awk to operate in "paragraph mode", treating any group of empty lines as record separator and the newline as field separator.

  • The "section titles" will now appear as the first "field" ($1) of your records.

  • Once the first field (=line) or the record begins with [Patient Sample Results], we set a flag s to 2 to indicate

    1. that we want to print results from now on, and
    2. that we want (initially) to print from the second line (=field) onward because the first time we want to print the "header" line.

    Note I used the regex comparison $1 ~ instead of a full string comparison $1== to guard against possible trailing space/tabs.

  • If s is set, print the fields (=lines) starting with number s, which will initially be 2. Afterwards, set it to 3 so we will skip the "header" line in the future.

Since the fields (=lines) are printed "as is", this will preserve the separator character found in the input file.

If your sections are separated by "empty" lines that actually contain whitespace, the following modification, which requires GNU awk for multi-character record separators, will guard against that (see @EdMorton's answer on StackOverflow e.g.):

awk -F'\n' -v RS='\n(([[:space:]]*\n)+|$)' ' ... '

This will turn any number of "lines that are completely empty or only contain whitespace characters" as record separator.


Maybe something like

awk 'BEGIN {FS="\t"}
     /^\[Patient Sample Results]/ { printing=1 ; next }
     !printing { next }
     /^\[/ { next }
     /^[ \t]*$/ { next }
     /^Sample/ { if (!printedheader) { print }; printedheader=1 ; next }
     { print }'
  1. Set the field separator to tab, not actually needed
  2. Note when we have to start printing
  3. Skip to the next line if we are not yet printing
  4. Don't print lines starting with [
  5. Skip blank lines, defined to be spaces and tabs only.
  6. If this is the header, print it the first time we see it, then skip to next line.
  7. Print anything left

Written to be as obvious as I can, rather than using awk idioms like 1 instead of { print }.

Edit. Change definition of blank line in response to comment.

  • 1
    As a sweeping statement it is usually a good idea to clean up the data as soon as possible. In this case I would delete all trailing whitespace, e.g. sed 's/[ \t]*$//' A.tsv | awk .... rather than attempting to clean up the output afterwards.
    – icarus
    Feb 11, 2021 at 9:25


awk '/\[(Patient|Control) Sample Results\]/{ hdr++; next }
     hdr==2        { hdr--; next }
     hdr && !rep   { print; rep=1; next }
     rep && $0!="" { print }
' infile

Using GNU sed we can do as follows and the order won't matter whether Patient or Control Sample Results are listed in the configuration file A.tsv

sed -En '
  /\[(Patient|Control) Sample Results]/,/^\s*$/{
' A.tsv

Some terminology: in a range of lines /begin/,/end/ the first and last are the bounding boxes of the range and are accessible via the // regex. Similarly, the interiors are accessible via //!. Provided that's the first regex we use upon entering a range.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.