My directories are organized as follows: one main directory, in which I have multiple directories that end with a number ranging from 314 to 727, followed by .3 . For example, 'mgm4761314.3'. Within these directories, I have fasta files named after a similar pattern (for example, mgm4761314.3.299.screen.passed.fna is in the directory mgm4761314.3).

There is only one such file (.fna) per directory.

What I've been trying to do is to write a loop that would go through all these folders and fasta files, and add the short number associated with the folder/file name (eg. 314, 315...727) to the headers. For example, for my first folder/fasta file, instead of:


I want:


The output would be exported to a file called, for instance, 314.fna.

I've sort of made it work for one number at a time, but I have plenty of files, so I'd appreciate if anyone could help me with the loop part! This is what I tried (and it didn't work). Using ubuntu.

for i in {314..727}; do sed "s/>/>${i}_/" [...]/*${i}.3/mgm4761${i}.3.299.screen.passed.fna > [...]/*${i}.3/${i}.fna; done
  • $\begingroup$ This should be easy enough, but please edit your question to clarify the directory structure. Does this need to be done to all files with an .fna extension in each directory? $\endgroup$
    – terdon
    Sep 26, 2019 at 15:37
  • $\begingroup$ @terdon Edited! Basically, there is only one file per directory (and all files are .fna) $\endgroup$
    – Laura
    Sep 26, 2019 at 15:46
  • $\begingroup$ Or could go perl -p -i -e 's/ $\endgroup$
    – M__
    Sep 26, 2019 at 16:29

1 Answer 1


You were so close! This should do the trick:

for i in {314..727}; do 
    sed -i.bak "s/^>/>${i}_/" mgm4761${i}.3/*.fna

Note that I am using -i which makes sed edit the original file. Because I use -i.bak, this will create a backup file with the original data. Once you are sure the command does what you need, you can delete all of these files with rm */*fna.bak.

  • $\begingroup$ This seems to be a step in the right direction! However, there's something about it which went weird. It does what it's supposed to, but it also duplicates the headers. Example: '>314_NS500799:83:H53J2AFXX:2:11101:11350:1048 >314_NS500799:83:H53J2AFXX:2:11101:11350:1048 GGGCAGTCCTTGCAGGGTGATACAGTCACCGTTTCTCTGAATGGGGAGGATCGTCCCCAAACTGTCAAGTACTACCGTTTGGCCCAGATCGTCTTTCAGAACCCGTACTCTTCACTGAATCCGCGCAAGCCGGTGCGCGACATCGTCGGCA >314_NS500799:83:H53J2AFXX:2:11101:2772:1050 >314_NS500799:83:H53J2AFXX:2:11101:2772:1050 etc' $\endgroup$
    – Laura
    Sep 26, 2019 at 16:21
  • $\begingroup$ IMO perl -p -i -e gives you more regex power, but this problem is easily solved just anchor the match and delete to the end $\endgroup$
    – M__
    Sep 26, 2019 at 16:31
  • $\begingroup$ @Laura argh! That's because I'm an idiot and left a debugging command I had used while testing my answer. If you've run it already, you will want to rename all the bak files back to the original. You can do this with: for i in */*bak; do mv "$i" "${i//.bak}"; done. Then, run the updated command (without the p at the end of the s///) and it should work as expected. $\endgroup$
    – terdon
    Sep 26, 2019 at 16:57
  • $\begingroup$ @MichaelG. yes, perl is indeed better (and is where sed got the -i from), but overkill here. The match is already anchored to the beginning. The issue was that I had used s/foo/bar/p and the p causes sed to print any line where the substitution was successful. I was using that along with -n which suppresses output to test that it was working as expected, but forgot to remove it when posting the answer. $\endgroup$
    – terdon
    Sep 26, 2019 at 16:59
  • $\begingroup$ @Laura if you didn't keep the backup files, you can remove the duplicated headers with perl -i -ne 'if(/^>/ && $_ eq $last){next}; print; $last=$_; ' */*fna. $\endgroup$
    – terdon
    Sep 26, 2019 at 17:03

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.