Perl One-Liners | Perl命令行學(xué)習(xí)3 -a和-F參數(shù)

Perl命令行 -a參數(shù) -F參數(shù)

【上集回顧】

上次說到了-p-n參數(shù),其實再加上之前學(xué)的-e參數(shù)已經(jīng)可以做很多事情了,但是為了方便,Perl還有這樣一對搭檔組合的參數(shù),就是-a-F

【參數(shù)解釋】

-a : 將讀入的$_進行分割,保存到@F列表之中,類似于split /分隔符/ , $_; 而這個分隔符是由-F參數(shù)指定的,其實這個功能與awk工具相似

-F : 在添加-a參數(shù)時候,指定分隔符(可以是正則表達式),如果不加好像是由空格作為分隔符,一般對其進行設(shè)置

實例說明

為了更加清晰的說明,還是舉例子吧,打開終端或者git for windows

輸入

# 例如我需要把來自管道的數(shù)據(jù)按照空格分隔成一個一個單元,存到列表里面
echo "qwe asd zxc" | perl -n -a -F"\s+"-e '
  $" = "\n";
  foreach my $item (@F){
      print "$item\n";
  }
'
--------------------------------------
# 輸出
qwe
asd
zxc

這次的例子要比之前的例子復(fù)雜一點,我來一一說明

  1. echo在屏幕上打印出qwe asd zxc這個字符串,這字符串中間由空格分成三個部分,分別是qwe、asd、zxc
  2. echo的輸出進入管道 |
  3. perl逐行讀?。ㄒ驗橹挥幸恍?,所以直接讀完了)
  4. 讀取的字符串賦值給$_
  5. $_被分割為三份(應(yīng)-a的要求,根據(jù)-F(注意雙引號是貼著-F參數(shù)的,中間沒有空格隔開)的指定的\s+(意思是按照一個或者多個空格或者制表符分隔,這里也可以改為" +"),將$_分割為三份)
qwe asd zxc
   ^   ^
   |   |
   空格

# 根據(jù)空格來劃分 \s+ 表示如果有多個空格相連也一并視為一個整體
# 切割之后,空格都消失

    /   /
qwe/asd/zxc    成為 @F中的元素  ('qwe','asd','zxc')
  /   /
  1. 分隔的三份按照順序存在@F列表中
  2. 遍歷@F列表,將其中的內(nèi)容打印出來

其實你可能會說,用之前之前學(xué)的參數(shù)就夠了??!比如

echo "qwe asd zxc" | perl -n -e '
    my @F = split /\s+/,$_;
    $" = "\n";
    print "@F\n";
'

那為什么要這樣做呢?其實在平常的文本中比沒有感覺到,在linux或者mac系統(tǒng)下面,有很多信息就是以文本的形式給出來的,而且中間一般都是用空格或者制表符分隔的,就比如使用df命令查看磁盤使用情況

df
------------------------------------------
# 輸出
Filesystem           1K-blocks      Used Available Use% Mounted on
C:/Program Files/Git 104857596  63822260  41035336  61% /
D:                   318168060 313664144 125465657  40% /d
E:                    41942012  21699268  20242744  52% /e

可以看到很鮮明的由空格或者制表符分隔的信息形式。
問題來了,利用這兩個參數(shù),我們可以試著做一下事情

問題1:我需要將所有的盤符提取并打印出來?

來試一下

df | perl -n -a -F"\s+" -e '
  print $F[0],"\n";
'
------------------------------------------
# 輸出
Filesystem
C:/Program
D:
E:

可是標(biāo)題行不是我想要的,怎么除去呢?
有多種方法

  1. 使用特殊變量$.

$.意為當(dāng)前讀取的行數(shù)

df | perl -n -a -F"\s+" -e '
  # 第一行就是標(biāo)題行了,直接跳過它
  if($. == 1){
    next;
  }else{
    print $F[0],"\n";
  }
'
------------------------------------------
# 輸出
C:/Program
D:
E:
  1. 借助Linux命令
# awk中NR為內(nèi)置變量,與上面的perl中的 $. 變量意義相同,就是當(dāng)前讀取的行數(shù)
df | perl -n -a -F"\s+" -e '
  print $F[0],"\n";
' | awk 'NR>1{print $0}'
------------------------------------------
# 輸出
C:/Program
D:
E:

注意:你發(fā)現(xiàn)在現(xiàn)實盤符的時候C:/Program Files/Git顯示的是不完整的,只顯示了C:/Program,也就是它被分隔了??!這里要說明一下,文件夾是可以使用空格的(特別是像windows下面的系統(tǒng)文件夾C:/Program Files,的確是很煩人),這個時候使用空格分隔則要小心,一般在linux和mac下面碰不到這種情況。這里為了演示更加方便,然后便于初次的講解,我把C盤排除掉。但是要是的確有需要加入C盤來進行處理也是可以進行的,只是有點復(fù)雜,這里我不敘述,在文章末尾我進行一下探討。

問題2:我要計算D盤和E盤總共已經(jīng)使用的磁盤的內(nèi)存(排除了C盤,原因見上述說明)

# 與上面一樣,還是將df命令的結(jié)果讀取進來,然后分隔成各個元素存到@F中去
df | perl -n -a -F"\s+" -e '
BEGIN{
  $total = 0;
}
chomp;
# 排除C盤
if(m/^C:/){
  next;
}
if($. == 1){
  next;
}else{
  $total = $total + $F[2];
}
END{
  print "total use : $total\n";
}
'
----------------------------------------------------
# 輸出
total use : 335363412

上面用到了兩個特殊的代碼塊BEGIN{}END{}
這兩個代碼塊在perl的單行程序中會經(jīng)常用到
說明一下它們兩個的作用

# 單行程序中的結(jié)構(gòu)          # 流程解釋
_________________________________________________________
BEGIN{              |       +++++++++ 讀取文件之前
  代碼1;            |       + 代碼1 +  就運行代碼1
}                   |       +++++++++  只運行一次
                    |
                    |       ---> ++++
                    |       ---> +代+  然后每次讀取一行
代碼2;              |       ---> +碼+  運行一下代碼2
                    |       ---> +2 +
                    |       .... ++++
                    |
END{                |       +++++++++ 最后文件讀取完畢
  代碼3;            |       + 代碼3 + 運行代碼3
}                   |       +++++++++ 只運行一次
_________________________________________________________

其實BEGIN{}END{}塊放的順序和位置并不重要,也就是說可以這樣

# 形式1

BEGIN{  
  代碼1;
}       
END{    
  代碼3;
}  
代碼2;  
------------------
# 形式2
END{    
  代碼3;
}
代碼2;
BEGIN{  
  代碼1;
} 
------------------
# 形式3
代碼2; 
BEGIN{  
  代碼1;
}       
END{    
  代碼3;
} 

其實除了這些由空格分隔的,我們平常使用的excel中的兩個格式也是由特定的字符分隔的

  • CSV文件 : 由逗號分隔的文本文件
  • TSV文件 : 由制表符分割的文本文件

對于這種文件,使用這兩個參數(shù)進行搭配,就省了很多事兒,是吧

# 例如一個文件 123.csv
# 新建一個txt文本文件,將后綴名改成csv就可以
# 內(nèi)容為
name,apple,banana,orange,grape,strawberry
color,red,yellow,orange,purple,red
  • 示例1

目標(biāo):打印出第一列,也就是標(biāo)題

cat 123.csv | perl -n -a -F"," -e '
  print "$F[0]\n";
'

# 輸出
name
color
  • 示例2

目標(biāo):計算出現(xiàn)了多少種顏色

cat 123.csv | perl -n -a -F"," -e '
  # 如果第一列是color就執(zhí)行代碼
  if($F[0] eq 'color'){
    # 將第一個元素給扔掉
    shift @F;
    
    for my $color (@F){
      # 利用哈希對重復(fù)的顏色的合并
      # 而不是簡單的記錄這個列表中有多少元素
      # 因為存在重復(fù)的顏色
      # 紅色是兩份,它的值為2
      $hash{$color}++;
    }
  }
  END{
    # 使用scalar方法得到哈西鍵的個數(shù)
    print "Total number of color type : ",scalar(keys %hash),"\n";
  }
'
---------------------------------------------
# 輸出
Total number of color type : 4
  • 示例3
    目標(biāo):按照下面那樣的方式打印出來(之間是逗號相隔開),這個其實就是列表的翻轉(zhuǎn),這個例子稍微有點復(fù)雜,這個例子意義其實不大。但是結(jié)合了多個perl單行程序
name,color
apple,red
banana,yellow
orange,orange
grape,purple
strawberry,red

# 代碼開始
cat 123.csv | perl -n -a -F"," -e '

  # 因為沒有去處換行符,所以每一個元素后面均會帶有回車符和換行符
  # 這里將其除去
  $F[-1] =~ s/\r*\n//;
  
  my $title = shift @F;
  my @items = @F;
  my $item_num = scalar(@items) unless defined $item_num;
  $title_num++;
  
  # 列表里面的原始是有序的
  # 用它來記錄有順序的title
  push @title_list,$title;
  
  # 哈希里面的元素是無序的
  # 用它來記錄每個title對應(yīng)的該行的元素
  $hash{$title} = \@items;

  END{
    $" = ",";
    
    # 先輸出標(biāo)題行
    print "@title_list\n";
    
    # 然后打印出各個元素
    for my $row (0..$item_num-1){
      for my $key (@title_list){
        print $hash{$key}->[$row];
        
        print ",";
      }
      print "\n";
    }
  }
' | perl -p -e 's/,$//'

-----------------------------------------------------
# 結(jié)果
name,color
apple,red
banana,yellow
orange,orange
grape,purple
strawberry,red

補充說明

  • -a與-F參數(shù)的順序不重要,但是一定要放在-e參數(shù)之前
  • -F指定分隔符的時候后面的分隔符要貼著-F參數(shù),中間不要有空格之類,否則會報錯

探討

上面說到有時候文件夾會出現(xiàn)空格的情況,像上面出現(xiàn)的C:/Program Files/Git被分隔的情況
那這樣難道就沒有辦法來處理嗎?
再來看一下df的輸出結(jié)果

df
------------------------------------------
# 輸出
Filesystem           1K-blocks      Used Available Use% Mounted on
C:/Program Files/Git 104857596  63822260  41035336  61% /
D:                   318168060 313664144 125465657  40% /d
E:                    41942012  21699268  20242744  52% /e

雖然按照空格來分隔可能有些行不通了,但是能不能轉(zhuǎn)換一下思維,按照字符串的數(shù)量來劃分

|-------------------|---------|---------|---------|----|----------|
Filesystem           1K-blocks      Used Available Use% Mounted on
C:/Program Files/Git 104857596  63822260  41035336  61% /
D:                   318168060 313664144 125465657  40% /d
E:                    41942012  21699268  20242744  52% /e

可以看到每一列對應(yīng)的字符串長度(空格也會被計算)是一致的
來!試一試

# 這個時候不能直接用空格分隔了,得采用一些特殊的方法
# 設(shè)置行數(shù)
export col=1
df | perl -n -e '
  BEGIN{
    # 設(shè)置想要打印的列數(shù)
    # 傳入環(huán)境中變量
    $col = $ENV{'col'};
  }
  if($. == 1){
    @title_slice = (22,11,11,11,5,11);
    next;
  }
  my $offset = 0;
  map {$offset+= $_} @F[0..$col-2] if $col-2 > 0;
  print substr($_,$offset,$title_slice[$col-1]-1) =~ s/\s*(.+?)\s*/$1/r,"\n";
'
# 但是這樣需要人工去數(shù),也不是個好辦法

版權(quán)聲明:本文采用 知識共享署名-非商業(yè)性使用-禁止演繹 4.0 國際許可協(xié)議 (CC BY-NC-ND 4.0) 進行許可。

最后編輯于
?著作權(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)容

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