VERDVANA'S BLOG Verdvana

Perl实例


1 前言

        最近写脚本上瘾,毕竟太懒了,懒得看那么多report。积累了一些常用的代码片段,懒得每次再写了,搁在这随时复制。🤷‍♂️


2 chomp

        这玩意的用处是删除末尾的换行符,常跟标准输入句柄一起用,删掉输入完成时的那个回车:

chomp($Moudle_Name = <STDIN>);

        一个长得很像的chop函数,用来去掉变量最后一个字符。


3 获取时间

        获取本地时区时间并格式化:

my ($sec ,$min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
$mon=(sprintf "%02d", ($mon+1));    # 0-11,因此要+1
$mday=(sprintf "%02d", $mday);
$year=$year+1900;                   # 从1900年算起,因此要+1900

        也可以获取格林尼治时间:

my ($sec ,$min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = gmtime(time);
$mon=(sprintf "%02d", ($mon+1));    # 0-11,因此要+1
$mday=(sprintf "%02d", $mday);
$year=$year+1900;                   # 从1900年算起,因此要+1900

        这其中可以使用POSIX函数格式化:

use POSIX qw(strftime);
$time = strftime "%y%m%d",localtime;

4 获取路径

        获取当前路径:

use Cwd;
my $dir = getcwd;

        或者:

my $dir = $ENV{'PWD'};

        需要注意的是这里的路径是脚本被运行的路径不是脚本所在路径。


5 路径、文件搜索

        在当前路径下,级数可调的路径、文件搜索:

my $path        = "./*";
my $series_subf = 3;

sub scan_file{
  my @files = glob($_[0]);
  foreach(@files){
    my $series_subf = $_[1] - 1;
    if(-d $_){
      print "path: $_ \n";

      if($series_subf > 0){
        my $path = $_."/*";
        scan_file($path, $series_subf);
      }
    }
    elsif(-f $_){
      print "file: $_ \n";
    }
  }
}

scan_file($path, $series_subf);

6 文件处理(打开、新建、删除、寻找)

6.1 打开

        打开文件并逐行读取:

open( REPROCESSED_FILE, "PREPROCESSED_FILE.sv") || die "Cannot open HDL file\n";
while(<PREPROCESSED_FILE>){
  ……
}
close PREPROCESSED_FILE;

6.2 新建

        新建文件并写入字符:

open TB_FILE, '>'."$TB_Name.sv";
print TB_FILE $Parameter;
close HDL_FILE;

6.3 删除文件

        删除文件:

unlink "$TB_Name.sv";

7 变量匹配

        在使用变量匹配时,容易遇到变量内有需要转义的字符的情况,可以用“\Q…\E”进行全部转义:

$x =~ /\Q$y\E/;

8 格式化输出

        注意“.”前面不能有空格,否则会报错。

format list =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<<<
$nm, $lt, $num, $mc
.

open FP, "> ./length_match.rpt";
select(FP);
$~ = list;

my $i = 0;
foreach(@IR_Value){
	$nm = $Inst_name[$i];
  $lt = $Length[$i];
  $num= $Num_Master[$i];
  $mc = $Match[$i++];
  write;
}

close FP;
select(STDOUT); #换回标准输出

9 动态输出

        想要在脚本运行时输出在terminal上的字符动态更新,其实是使用ANSI控制码把光标退回去再次打印,实现动态输出的效果:

print "\033[s";         # 保存当前光标位置
foreach(@rpt){
  print "\033[u$_    "; # 恢复光标位置并覆盖打印
}
print "\n";

        顺带列出常用的ANSI控制码:

控制码 说明
[X;YH 将光标移至第X行,第Y列的位置
[nA 光标上移n行(屏幕顶端无效)
[nB 光标下移n行(屏幕底端无效)
[nC 光标右移n列(屏幕最右端无效)
[nD 光标左移n列(屏幕最左端无效)
[6n 报告光标位置
[s 保存当先光标位置
[u 恢复光标位置
[?25l 隐藏光标
[?25h 显示光标
[2J 清屏
[K 将光标位置开始至本行结束的所有字符清除
[n1;n2;…m 设定显示字符的属性,若有两个以上设定则以分号将代码(n1;n2;…)隔开。除非重新设定,否则该属性一直被保留
[0I 默认字体
[1I 宋体
[2I 黑体
[3I 楷体

        其中,显示字符的属性代码(即上表格第14行的“n1”“n2”…):

代码 意义
0 所有属性reset,即返回默认显示模式
1 高亮显示
4 加下划线
5 闪烁显示
7 反相显示
8 不可见
前景 背景 颜色
30 40 黑色
31 41 红色
32 42 绿色
33 43 黄色
34 44 蓝色
35 45 紫红色
36 46 青蓝色
37 47 白色

        显示循环函数的进度和百分比的子函数,需要在循环里执行:

# 进度条子函数
sub progress_bar{
  my($i,$number,$length)=@_;                    # 获取当前循环数,总循环数,进度条总长度
  my $percent = sprintf("%.2f",$i/$number*100); # 计算当前百分比
  my $n = $percent * $length / 100;             # 计算进度条进度
  print "$percent\%          \n\[\033[s";       # 打印百分比以及换行打印进度条背景的开头,并保存光标
  while($length>0){print "-";$length--;};       # 打印进度条背景
  print "\]\033[u";                             # 打印进度条背景的结尾,并恢复光标
  while($n>0){print ">";$n--;};                 # 打印进度条的进度
  print "\033[1A\033[100D";                     # 光标回到上一行的最左侧
  if($i == $number){print "\n\n";}              # 如果循环结束,则光标回归并另起一行
}

my $i;
# 执行循环
foreach(@rpt){
  system("~/scripts/summary.pl $PRJ");          # 执行循环内原本的操作

  my $rpt_number = @rpt;
  $i++;                                         # 当前循环数,从1开始
  progress_bar($i,$rpt_number,20);              # 执行进度条子函数
}
  
print "Complete!\n";

        效果如下:

img1

        需要注意的是在循环里就不能加其它打印函数了。


10 命令行参数

        可以在运行脚本时传递命令行参数,例如:

./get_csv.pl /usr/home/xx.rpt 6

        在脚本中获取参数:

my $file = ARGV[0];
my $num = ARGV[1];

11 通过匹配元素获取数组索引值

        翻了半天书也没找到,问了问GPT4.0,当场给了两个方法:

        使用grep函数,它可以返回一个数组,包含所有匹配元素的索引:

my @array = ("a", "b", "c", "d", "e");
my $element = "c";
my @matches = grep { $array [$_] eq $element } 0 .. $#array;
print "@matches\n"; # 2

        还可以使用List::MoreUtils模块中的first_index函数,它可以返回第一个匹配元素的索引:

use List::MoreUtils qw(first_index);
my @array = ('apple', 'banana', 'cherry', 'date');
my $index = first_index { $_ eq 'cherry' } @array;

        如果不是严格相等,这两种方法也都可以换成匹配。


        告辞。