【上集回顾】: 上一次提到了-e参数,一般在使用perl的单行程序时都会加上-e参数。-e参数就是将后面引号内的内容当作perl代码执行。

这一次来说一说另外两个常见的-p和-n参数,这两个参数与输入输出相关

简介

-p:将参数当作文件读取进来,并且每次读取一行遍历文件,并把执行-e参数之后的perl代码部分得到默认变量$_打印到标准输出中(可以是屏幕也可以进入管道)。

-n:将参数当作文件读取进来,并且每次读取一行遍历文件。

两个看起来共性有点大,那有什么区别呢?

在之前先来看一个例子吧!

例子

首先新建一个文件 123.txt

里面的内容为:

1
2
3
the string is hello
the number is 12345
the float is 1.2345

我怎么知道是不是逐行读取的呢?

123.txt所在位置右键,打开git bash here或者打开终端cd到当前目录

输入:

1
2
3
4
5
6
7
perl -n -e '++$n;print "$n  $_"' 123.txt

# 输出为

1 the string is hello
2 the number is 12345
3 the float is 1.2345

可以看到程序部分执行了三次,文件是逐行读取的

-p参数

  • 示例1
1
2
3
4
5
6
7
# 比如我想让含有字符串string的行打印出来
perl -p -e 'm/string/' 123.txt

# 输出为
the string is hello
the number is 12345
the float is 1.2345

可以看到和原来的文件是没有什么两样的

  • 示例1.1
1
2
3
4
5
6
7
# 输入
perl -p -e 's/string/number/' 123.txt

# 输出
the number is hello
the number is 12345
the float is 1.2345

第一行和之前的结果不一样了

但是假如说我想的是“只要那些有string这样字眼的行输出出来呢”?

于是我就想可不可以这样写:

  • 示例2
1
2
3
4
5
6
7
8
# 输入
perl -p -e 'if(m/string/){print $_}' 123.txt

# 输出为
the string is hello
the string is hello
the number is 12345
the float is 1.2345

可以看到文件第一行输出了两次,也就是说

-p参数不管你的代码中有什么判断语句来控制输出,每读取一行都会输出一下$_,这个与代码中的print不会合并,它不管究竟有没有print

那它是输出的什么呢,输出什么时候的$_呢?

来这样试一下:

  • 示例3
1
2
3
4
5
6
perl -p -e 's/string/number/;s/hello/world/' 123.txt

# 输出为
the number is world
the number is 12345
the float is 1.2345

传进来文件第一行的变量$_在perl代码中其实变换了两次

1
2
3
最开始时候:the string is hello
第一次变换:the number is hello
第二次变化:the number is world

可以看到,-p参数每次输出的$_的值是它从代码执行最后的值

再来调戏一下$_

  • 示例4
1
2
3
4
5
6
perl -p -e 'if(m/string/){$_ = "foo\n"}' 123.txt

# 输出为
foo
the number is 12345
the float is 1.2345

第一行已经变了,就是说$_是可以被改变,甚至改变巨大都可以

那为了刚才我们的目标,可以这样:

  • 示例5
1
2
3
4
5
# 如果不能匹配到 string 就将变量$_设置为空字符串,类似于清空 
perl -p -e 'if(!m/string/){$_ = ""}' 123.txt

# 输出为
the string is hello

哈哈!没难倒我们!达到目标了!

你有没有感觉不太好呢!这里判断条件倒是很简单,所以可以这样做,假如判断条件多了,代码量多了,每次都要这样来思考不是有点不顺吗?

这样吧,开门见山,有请-n参数登场

-n参数

先来按照-p的路子试一试-n

  • 示例6
1
2
3
4
# 我想要的是将含有string字符串的行打印出来
perl -n -e 'm/string/' 123.txt

# 输出为:

是的,什么都没有,的确是这样,不是我码字把它码掉了。其实有了上面的-p的说明,这里-n已经比较明确了,它其他方面的和-p一样,但是他不会输出$_,也就是说程序默认不会输出任何东西(当然不包括错误信息在内)

于是上面的问题就可以这样解了:

  • 示例7
1
2
3
4
perl -n -e 'if(m/string/){print $_}' 123.txt

输出为:
the string is hello

到了这里大家应该明白了-p和-n的区别,其实对于命令行来说,-p和-n用的都挺多的,就看你需要什么,怎么做起来更快,就选哪一个。

问题

问:假如我读取文件不想要后面的换行符出现,输出的时候我自己加换行符,怎么做?

答:其实和写perl脚本一样,其实可以这样做

1
perl -n -e 'chomp;if(/string/){print "$_\n"}' 123.txt

也就是加一个chomp。其实除了这种方式,还有一种加上参数-l可以达到先自动chomp,之后自动在$_后面加上\n然后输出

问:这个-p和-n参数的每行读取文件怎么和那个钻石操作符<>一样啊!

答:的确是这样的

1
2
3
4
5
6
7
8
# 平常写perl脚本我们一般写
open READ,"<","123.txt" or die "$!";
while(< READ >){
..........;
}

# 对于单行程序直接就是
perl -n -e '........' 123.txt

这样看来单行程序在写较短程序上比直接去写perl脚本来的快,来的节省

在这里再引申一下

如果我有一个fasta文件,假设名称为fasta.fasta 内容为

1
2
3
4
5
6
7
>cox1
ATGCGATCGTAGCATGCTAGCTACG
ACAAGCTAGCTAGCTAGCAGCATCT
ACGTGCAT
>rpl10
ATGAGCTAGCTAGCTAGACGTACGT
ACGTAG

假如在命令行中这样输入

1
perl -p -e 'if(/^>/){$_ = <> || ""}' fasta.fasta

输出为

1
2
3
4
5
ATGCGATCGTAGCATGCTAGCTACG
ACAAGCTAGCTAGCTAGCAGCATCT
ACGTGCAT
ATGAGCTAGCTAGCTAGACGTACGT
ACGTAG

问:为什么我一直没有输出啊!

答:你是不是把参数顺序写反了

  • 错误写法 perl -e -p 'print if /string/' 123.txt perl -e -n 'print if /string/' 123.txt perl -ep 'print if /string/' 123.txt
  • 正确写法 perl -p -e 'print if /string/' 123.txt perl -pe 'print if /string/' 123.txt

第三种情况虽然两个参数并在一起写了,可是有先后顺序,其实第三种就是第一种的写法。也就是说引号对引起来的代码需要靠近-e,而且-e参数一般放在所有参数最后一个

perl读取文件的方式

  • 放在命令行代码块的末尾
1
perl -n -e 'print $_' 123.txt
  • 结合Linux工具
1
cat 123.txt | perl -n -e 'print $_'
  • 使用文件句柄
1
2
3
4
5
6
perl -e '
open FILE,"<","123.txt" or die $!;
while(<FILE>){
print $_;
}
'