Perl進(jìn)階筆記(僅此一篇,持續(xù)更新)

目錄

    1. pod文檔
    1. Getopt::Long
    1. perl單行
    1. 使用Hash遇到的坑
    1. Hash中的排序操作
    1. 安裝Perl模塊
    • 6.1. 使用CPAN模塊自動安裝
    • 6.2. 手工安裝
    • 6.3. 非root用戶的另一個解決方案
    1. 循環(huán)匹配
    1. chomp帶來空行匹配的失敗
    1. 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/$_"判斷該文件是否存在


參考資料:

(1) 生信菜鳥團(tuán):perl模塊安裝大全

(2) Heng Li's blog: On the definition of sequence identity

(3) 【簡書】生信雜談:怎樣定義sequences比對的相似度?

(4) perl中g(shù)rep的詳細(xì)用法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 2009 有用的和有意思的循環(huán) 讓我們來看一個基本的例子. 這是一個最簡單清晰的語法的例子.在這并沒有使用括號來包...
    焉知非魚閱讀 651評論 0 0
  • ############原文粘貼#################### 用 Perl 實現(xiàn)的有用的單行程序 ...
    sunslj閱讀 1,266評論 0 4
  • 城市的夜晚總是那么亮堂,夜再沒有了那般漆黑,薄薄地像一層深藍(lán)色的紗般籠罩著大地。晚上,看著四處照明的路燈,看到對面...
    胖絨球閱讀 576評論 2 6
  • 夏天來了,龍蝦散落在大中國的各個角落,其實我不太愛這玩意,為啥?自從去年搬家之后,連續(xù)吃了兩個月的小龍蝦,已經(jīng)讓我...
    拯救地球者閱讀 421評論 0 1
  • 到2017年底了,最怕有人問起:你2016年的年初計劃完成了嗎?不對!你2015年的計劃完成了嗎? 答案肯定是沒完...
    伸個小懶腰閱讀 460評論 0 1

友情鏈接更多精彩內(nèi)容