0

i believe i need to use grep because i am searching multiple files and i need for the file name to appear as a header on each line. i am using terminal on macOS.

so say i have one file, file1, in directory 'dir' with the following contents:

>species one; trnF(ggc)
GGCCC
AACGC
>species one; rrnS
TAGCA
GGCAC

and an additional file, file2, in directory 'dir' with the following contents

>species two; trnF(gga)
GGACG
CGACG
GCAC
>species two; rrnS
GCATG
GGCAG

i'd like to get the following output:

dir/file1:>species one; trnF(ggc)
dir/file1-GGCCC
dir/file1-AACGC
--
dir/file2:>species two; trnF(gga)
dir/file2-GGACG
dir/file2-CGACG
dir/file2-GCAC

i can get a certain specified number of lines after the line with the pattern using -A:

$ grep -A 2 'trnF' dir/file*

but the files have a different number of lines after the grep command so i get the following:

dir/file1:>species one; trnF(ggc)
dir/file1-GGCCC
dir/file1-AACGC
--
dir/file2:>species two; trnF(gga)
dir/file2-GGACG
dir/file2-CGACG

how can i get all of the contents until the next '>' symbol?

2

As you have discovered, grep -A is inflexible in that it only allows for specifying a static number of lines of context.

Instead, here's an awk command that detects when the selected sequence occurs, and outputs it until it encounter another sequence header that does not match the criteria:

awk -v name='trnF' -v OFS=':\t' '
    FNR == 1 && NR > 1 { print "--" }
    /^>/ { out=0 }
    $0 ~ "^>.*" name { out=1 }
    out == 1 { print FILENAME, $0 }' files

The -v name=something option will assign something to the name variable in the awk code, and it will be used as part of a regular expression that matches a fasta header line that contains the given string.

The output of the last piece of awk may look something like

file:   >species one; trnF(ggc)
file:   GGCCC
file:   AACGC
--
file1:  >species one; trnF(ggc)
file1:  GGCCC
file1:  AACGC
--

(the : followed by a tab used as a delimiter between the filename and the data may be changed by setting OFS to something else on the command line with -v OFS=somethingelse)

  • 1
    @LL3 Yes that would work. The only issue with this (that my original code had as well) is that it'll always print -- between files regardless of whether there was a match at all, but that's just a matter of keeping track of whether the current file had a match or not (I won't add that code ATM). – Kusalananda Jul 20 at 19:28
0
#! /bin/bash

for i in dir/file*
do
    [ -n "$is_first_file" ] && echo
    awk -v filename="$i" 'BEGIN { temp=1 }  { if ($1 ~ /^>/ && temp == 0){exit} } {if ( $1 ~ /^>/ && temp == 1 ) {print filename,"\b:","\b"$0,temp=0 } else { print filename,"\b-","\b"$0 }}' "$i"
    echo -n '--'
    is_first_file=no
done
echo -n -e "\b\b"

Not much experienced with awk, but i guess this should give you the exact output you want.

Also one mistake that i do often. Don’t run this script like sh script_name.

It uses -e in echo which would need bash.

Run it either like ./script_name or bash script_name.

  • You know you can always use printf as a replacement for echo -e in /bin/sh scripts. – Kusalananda Jul 20 at 18:28

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.