Perl One-Liners | Perl命令行学习3 -a和-F参数

Perl命令行 -a参数 -F参数

【上集回顾】

上次说到了-p-n参数,其实再加上之前学的-e参数已经可以做很多事情了,但是为了方便,Perl还有这样一对搭档组合的参数,就是-a-F

【参数解释】

-a : 将读入的$_进行分割,保存到@F列表之中,类似于split /分隔符/ , $_; 而这个分隔符是由-F参数指定的,其实这个功能与awk工具相似

-F : 在添加-a参数时候,指定分隔符(可以是正则表达式),如果不加好像是由空格作为分隔符,一般对其进行设置

实例说明

为了更加清晰的说明,还是举例子吧,打开终端或者git for windows

输入

# 例如我需要把来自管道的数据按照空格分隔成一个一个单元,存到列表里面
echo "qwe asd zxc" | perl -n -a -F"\s+"-e '
  $" = "\n";
  foreach my $item (@F){
      print "$item\n";
  }
'
--------------------------------------
# 输出
qwe
asd
zxc

这次的例子要比之前的例子复杂一点,我来一一说明

  1. echo在屏幕上打印出qwe asd zxc这个字符串,这字符串中间由空格分成三个部分,分别是qwe、asd、zxc
  2. echo的输出进入管道 |
  3. perl逐行读取(因为只有一行,所以直接读完了)
  4. 读取的字符串赋值给$_
  5. $_被分割为三份(应-a的要求,根据-F(注意双引号是贴着-F参数的,中间没有空格隔开)的指定的\s+(意思是按照一个或者多个空格或者制表符分隔,这里也可以改为" +"),将$_分割为三份)
qwe asd zxc
   ^   ^
   |   |
   空格

# 根据空格来划分 \s+ 表示如果有多个空格相连也一并视为一个整体
# 切割之后,空格都消失

    /   /
qwe/asd/zxc    成为 @F中的元素  ('qwe','asd','zxc')
  /   /
  1. 分隔的三份按照顺序存在@F列表中
  2. 遍历@F列表,将其中的内容打印出来

其实你可能会说,用之前之前学的参数就够了啊!比如

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

那为什么要这样做呢?其实在平常的文本中比没有感觉到,在linux或者mac系统下面,有很多信息就是以文本的形式给出来的,而且中间一般都是用空格或者制表符分隔的,就比如使用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

可以看到很鲜明的由空格或者制表符分隔的信息形式。
问题来了,利用这两个参数,我们可以试着做一下事情

问题1:我需要将所有的盘符提取并打印出来?

来试一下

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

可是标题行不是我想要的,怎么除去呢?
有多种方法

  1. 使用特殊变量$.

$.意为当前读取的行数

df | perl -n -a -F"\s+" -e '
  # 第一行就是标题行了,直接跳过它
  if($. == 1){
    next;
  }else{
    print $F[0],"\n";
  }
'
------------------------------------------
# 输出
C:/Program
D:
E:
  1. 借助Linux命令
# awk中NR为内置变量,与上面的perl中的 $. 变量意义相同,就是当前读取的行数
df | perl -n -a -F"\s+" -e '
  print $F[0],"\n";
' | awk 'NR>1{print $0}'
------------------------------------------
# 输出
C:/Program
D:
E:

注意:你发现在现实盘符的时候C:/Program Files/Git显示的是不完整的,只显示了C:/Program,也就是它被分隔了!!这里要说明一下,文件夹是可以使用空格的(特别是像windows下面的系统文件夹C:/Program Files,的确是很烦人),这个时候使用空格分隔则要小心,一般在linux和mac下面碰不到这种情况。这里为了演示更加方便,然后便于初次的讲解,我把C盘排除掉。但是要是的确有需要加入C盘来进行处理也是可以进行的,只是有点复杂,这里我不叙述,在文章末尾我进行一下探讨。

问题2:我要计算D盘和E盘总共已经使用的磁盘的内存(排除了C盘,原因见上述说明)

# 与上面一样,还是将df命令的结果读取进来,然后分隔成各个元素存到@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的单行程序中会经常用到
说明一下它们两个的作用

# 单行程序中的结构          # 流程解释
_________________________________________________________
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文件 : 由制表符分割的文本文件

对于这种文件,使用这两个参数进行搭配,就省了很多事儿,是吧

# 例如一个文件 123.csv
# 新建一个txt文本文件,将后缀名改成csv就可以
# 内容为
name,apple,banana,orange,grape,strawberry
color,red,yellow,orange,purple,red
  • 示例1

目标:打印出第一列,也就是标题

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

# 输出
name
color
  • 示例2

目标:计算出现了多少种颜色

cat 123.csv | perl -n -a -F"," -e '
  # 如果第一列是color就执行代码
  if($F[0] eq 'color'){
    # 将第一个元素给扔掉
    shift @F;
    
    for my $color (@F){
      # 利用哈希对重复的颜色的合并
      # 而不是简单的记录这个列表中有多少元素
      # 因为存在重复的颜色
      # 红色是两份,它的值为2
      $hash{$color}++;
    }
  }
  END{
    # 使用scalar方法得到哈西键的个数
    print "Total number of color type : ",scalar(keys %hash),"\n";
  }
'
---------------------------------------------
# 输出
Total number of color type : 4
  • 示例3
    目标:按照下面那样的方式打印出来(之间是逗号相隔开),这个其实就是列表的翻转,这个例子稍微有点复杂,这个例子意义其实不大。但是结合了多个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对应的该行的元素
  $hash{$title} = \@items;

  END{
    $" = ",";
    
    # 先输出标题行
    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/,$//'

-----------------------------------------------------
# 结果
name,color
apple,red
banana,yellow
orange,orange
grape,purple
strawberry,red

补充说明

  • -a与-F参数的顺序不重要,但是一定要放在-e参数之前
  • -F指定分隔符的时候后面的分隔符要贴着-F参数,中间不要有空格之类,否则会报错

探讨

上面说到有时候文件夹会出现空格的情况,像上面出现的C:/Program Files/Git被分隔的情况
那这样难道就没有办法来处理吗?
再来看一下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

虽然按照空格来分隔可能有些行不通了,但是能不能转换一下思维,按照字符串的数量来划分

|-------------------|---------|---------|---------|----|----------|
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

可以看到每一列对应的字符串长度(空格也会被计算)是一致的
来!试一试

# 这个时候不能直接用空格分隔了,得采用一些特殊的方法
# 设置行数
export col=1
df | perl -n -e '
  BEGIN{
    # 设置想要打印的列数
    # 传入环境中变量
    $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";
'
# 但是这样需要人工去数,也不是个好办法

版权声明:本文采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容