目錄
- pod文檔
- Getopt::Long
- perl單行
- 使用Hash遇到的坑
- Hash中的排序操作
- 安裝Perl模塊
- 6.1. 使用CPAN模塊自動安裝
- 6.2. 手工安裝
- 6.3. 非root用戶的另一個解決方案
- 循環(huán)匹配
- chomp帶來空行匹配的失敗
- perl中的grep函數(shù)
1. pod文檔
使用pod文檔可以實現(xiàn)程序usage說明
=head1 part1
doc in part1
=head2 part2
doc in part2
.
.
.
=cut # pod文檔結(jié)束的標(biāo)志
注意:每個=標(biāo)簽上下必須隔一行,否則就會錯誤解析。
用pod2doc $0可以將程序中的文檔打印出來,不過一般用在程序內(nèi)部,當(dāng)程序參數(shù)設(shè)定錯誤時打印pod文檔:
die `pod2doc $0` if (...);
2. Getopt::Long
首先需要在腳本開頭加上對該模塊的引用
use Getopt::Long;
使用GetOptions函數(shù)承接傳遞的參數(shù):
my ($var1,$var2,$var3,$var4); # 若使用"use strict"模式,則需要提前定義變量
GetOptions(
"i:s"=>\$var1,
"o:s"=>\$var2,
"n:i"=>\$var3,
"m:i"=>\$var4
);
3. perl單行
執(zhí)行perl -h可以查看perl的所有參數(shù)使用說明,使用perl單行需要使用到-e參數(shù)
$ perl -e 'print "hello world!\n"'
perl單行常用的場景為進(jìn)行文本的逐行讀取并操作,即隱式地開啟while(<>),需要使用-n參數(shù)
perl -ne
'BEGIN{}
...
END{}'
filename
BEGIN 和 END 區(qū)塊根據(jù)需要進(jìn)行添加
若需要在逐行讀取的同時,自動將行中的元素打散 (split),默認(rèn)以\s(空字符,即空格或制表符)作為分割符,則需要使用-a參數(shù),相當(dāng)于在執(zhí)行了@F = split $_,打散后的元素會保存在@F中
4. 使用Hash遇到的坑
在編寫perl腳本的過程中,我們常常會將讀入文件一行中的某兩項(一行可能有多列,列與列之間用制表符\t隔開)作為相對應(yīng)的兩項,分別作為Hash的鍵(key)和值(value),由于Hash這種數(shù)據(jù)結(jié)構(gòu)要求key是唯一,而value可以重復(fù),因此一般將唯一的那一項作為key,另一項作為value
但是,由于字符串首末端空字符的存在會導(dǎo)致一個意想不到的情況:
創(chuàng)建了兩個Hash,讓它們的key是一一對應(yīng)的,而各自存儲的value不同,當(dāng)時在某些key字符串首末端混入了空字符,例如Hash1有一個key為"KEY",Hash2有一個key為"KEY ",它們本來應(yīng)該是一樣的,但是由于空字符的存在,它們現(xiàn)在不一樣了
這時候的解決方法是在構(gòu)建Hash之前,不論實際的字符串的首末端有沒有空字符串,都嘗試將這些空字符串去掉:
# 假設(shè)讀入的文件只有用制表符隔開的兩列
while(<IN>){
chomp;
@recorder = split /\t/;
$recorder[0] =~ s/(^\s+)|(\s+$)//g; # 去除開頭和末尾的空字符串
$recorder[1] =~ s/(^\s+)|(\s+$)//g; # 去除開頭和末尾的空字符串
$hash{$recorder[0]} = $recorder[1];
}
5. Hash中的排序操作
對key進(jìn)行排序
其基本的語法結(jié)構(gòu)為:
sort <排序規(guī)則> <排序?qū)ο?gt;
若要對keys進(jìn)行排序則排序?qū)ο缶褪莐eys,所以最后一項要寫成keys %hash
# 按value排序
## 對hash的keys按hash value排序(按ASCII碼排序)
sort { $hash{$a} cmp $hash{$b} } keys %hash
## 對hash的keys按hash value排序(按數(shù)字大小排序)
sort { $hash{$a} <=> $hash{$b} } keys %hash
# 按key排序
# 對hash的keys按hash key排序
sort {$a<=>$b} keys %hash
6. 安裝Perl模塊
查看perl模塊的安裝目錄,主要就是@INC這個默認(rèn)變量
perl -e '{print "$_\n" foreach @INC}'
若要臨時添加perl模塊的安裝目錄,則在perl腳本中shebang(#!/usr/bin/perl)后緊接著添加push(@INC,"...");命令,若是在perl單行中,則寫成BEGIN{push(@INC,"...");}
若是要永久添加perl模塊的安裝目錄,則修改PERL5LIB環(huán)境變量即可:
export PERL5LIB=/PATH/TO/LIB
查看已安裝的Perl模塊:
# 查看系統(tǒng)中安裝的Perl模塊
find `perl -e 'print "@INC"'` -name '*.pm'
# 查看當(dāng)前環(huán)境下所有的模塊(一般為用戶自己安裝的)
instmodsh
查詢單個perl模塊的安裝路徑:
perldoc -l Getopt::Long
查看安裝的perl模塊的版本號
perl -MGetopt::Long -e 'print Getopt::Long->VERSION. "\n"'
裝Perl模塊有兩種方法
- 自動安裝 (使用CPAN模塊自動完成下載、編譯、安裝的全過程)
- 手工安裝 (去CPAN網(wǎng)站下載所需要的模塊,手工編譯、安裝)
6.1. 使用CPAN模塊自動安裝
首先你得已經(jīng)安裝了CPAN,若沒有執(zhí)行以下命令:
# yum install perl-CPAN
安裝前需要先聯(lián)上網(wǎng),有無root權(quán)限均可
$ perl -MCPAN -e shell
cpan>help
cpan>m
cpan>install Net::Server
cpan>quit
- 查詢:cpan[1]> d /模塊名字或者部分名字/
查詢結(jié)果中會給出所有含有模塊名字或者部分名字的模塊,選擇您所需要的模塊進(jìn)行下載
- 下載安裝:cpan[1]> install 模塊名字
同時會自動安裝很多依賴的模塊,非常方便。
6.2. 手工安裝
一般情況下不推薦這種安裝方式,但是總是會有迫不得已的時候,而且嘗試這種方式,能加深對perl模塊的理解。
比如從 CPAN下載了Net-Server模塊0.97版的壓縮文件Net-Server-0.97.tar.gz,假設(shè)放在/usr/local/src/下。
cd /usr/local/src
tar xvzf Net-Server-0.97.tar.gz
cd Net-Server-0.97
perl Makefile.PL
make test
如果測試結(jié)果報告all test ok,你就可以放心地安裝編譯好的模塊了。
6.3. 非root用戶的另一個解決方案
手動下載local::lib, 這個perl模塊,然后自己安裝在指定目錄,也是能解決模塊的問題!
下載之后解壓,進(jìn)入:
perl Makefile.PL --bootstrap=~/.perl ##這里設(shè)置你想把模塊放置的目錄
make test && make install
echo 'eval $(perl -I$HOME/.perl/lib/perl5 -Mlocal::lib=$HOME/.perl)' >> ~/.bashrc ##目錄與前面要一致
等待幾個小時即可?。?!
添加好環(huán)境變量之后,就可以用
perl -MCPAN -Mlocal::lib -e 'CPAN::install(LWP)'
或有更簡單的寫法:
cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib)
7. 循環(huán)匹配
在李恒的github博客 On the definition of sequence identity 當(dāng)中看到這樣一個Perl單行代碼:
$ perl -ane 'if(/NM:i:(\d+)/){$n=$1;$l=0;$l+=$1 while/(\d+)[MID]/g;print(($l-$n)/$l,"\n")}'
這行代碼的目的是為了計算SAM文件中每條記錄BLAST identity
BLAST identity是根據(jù)你比對到的堿基除去比對所涉及到的columns數(shù)目,換句話來說就是比對涉及到所有的堿基數(shù)目
例如這樣的雙序列比對:
Ref+: 1 CCAGTGTGGCCGATaCCCcagGTtgGC-ACGCATCGTTGCCTTGGTAAGC 49 |||||||||||||| ||| || || |||||||||||||||||||||| Qry+: 1 CCAGTGTGGCCGATgCCC---GT--GCtACGCATCGTTGCCTTGGTAAGC 45它們的BLAST identity就是
43/50=86%
那么要計算SAM文件中每條reads的BLAST identity,總長可以通過疊加CIGAR中對應(yīng)的M/I/D的數(shù)目得到,比對到的堿基數(shù)目等于總長減去NMtag(比對不上的堿基位置的標(biāo)記)

李恒的這行代碼中有一部分一開始沒有讀懂,就是下圖紅框中的那部分:

其實這是一種簡寫方式,正規(guī)完整且更容易讀懂的形式可以寫成下面這樣:
# 這里為了更好看,添加了適當(dāng)?shù)膿Q行和縮進(jìn)
$ perl -ane \
'if(/NM:i:(\d+)/){
$n=$1;
$l=0;
while(/(\d+)[MID]/g){
$l+=$1;
}
print(($l-$n)/$l,"\n");
}
'
while(/(\d+)[MID]/g)中的正則表達(dá)式/(\d+)[MID]/g,引起了我極大的好奇:它是在正則表達(dá)式后面添加了一個g字符,即開啟了全局匹配,又由于是在while( )中進(jìn)行的正則匹配,等于是開啟了循環(huán)匹配,即對于CIGAR字符串18M3D22M,正則表達(dá)式/(\d+)[MID]/g,先會匹配上18M,然后會匹配上3D,最后匹配上22M
很有意思的用法
8. chomp帶來空行匹配的失敗
我常用正則表達(dá)式/^\s+$/來進(jìn)行文件中空行的匹配
在用perl單行進(jìn)行文本處理,我喜歡用一個固定的格式:
$ perl -ne 'chomp;...' <input>
chomp在這里的作用是在每讀入一行后,去除末尾的換行符
此時如果你要匹配的空行是/^\n/形式的,即這行只有一個換行符,被chomp處理過之后這一行就變成了/^$/形式的空字符的行,此時如果再用正則表達(dá)式/^\s+$/進(jìn)行匹配,就會匹配失敗
那么遇到這種情況應(yīng)該怎么匹配呢?
使用/^\s?$/即可,元字符?的作用是匹配前面的字符0到多次
9. perl中的grep函數(shù)
grep有2種表達(dá)方式:
grep BLOCK LIST
grep EXPR, LIST
BLOCK表示一個code塊,通常用{ }表示;
EXPR表示一個表達(dá)式,通常是正則表達(dá)式。原文說EXPR可是任何東西,包括一個或多個變量,操作符,文字,函數(shù),或子函數(shù)調(diào)用;
LIST是要匹配的列表
grep的工作原理:
grep對列表里的每個元素進(jìn)行BLOCK或EXPR匹配,它遍歷列表,并臨時設(shè)置元素為
$_在列表上下文里,grep返回匹配命中的所有元素,結(jié)果也是個列表。在標(biāo)量上下文里,grep返回匹配命中的元素個數(shù)。
實例一:
open FILE "<myfile" or die "Can't open myfile: $!";
print grep /terrorism|nuclear/i <FILE>;
打開一個文件myfile,然后查找包含terrorism或nuclear的行。<FILE>返回一個列表,它包含了文件的完整內(nèi)容。可能你已發(fā)現(xiàn),如果文件很大的話,這種方式很耗費內(nèi)存,因為文件的所有內(nèi)容都拷貝到內(nèi)存里了
實例二:
foreach my $infile (grep { !/^\./ && -f "$indir/$_" } readdir(DIR)){
...
}
readdir(DIR)讀入指點文件夾句柄DIR下的所有文件(包括以.開頭的隱藏文件)的文件名,構(gòu)成一個文件名列表 (list),然后每一次讀入一個文件名保存到臨時變量$_中,傳遞給grep處理,先用!/^\./判斷文件名是否以.開頭,保證該文件不是隱藏文件,接著,通過-f "$indir/$_"判斷該文件是否存在
參考資料:
(2) Heng Li's blog: On the definition of sequence identity