Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
什么';在Perl单行程序中,管道元素或将它们指定为参数之间的区别是什么?_Perl_Bash_Ubuntu - Fatal编程技术网

什么';在Perl单行程序中,管道元素或将它们指定为参数之间的区别是什么?

什么';在Perl单行程序中,管道元素或将它们指定为参数之间的区别是什么?,perl,bash,ubuntu,Perl,Bash,Ubuntu,在学习Perl的同时,我也在学习Linux(Ubuntu),所以这是一段消磨时间的好时光 以下两者之间的区别是什么: find . -type f | perl -nle '... #aka yada yada' 及 第一个将文件名传递给Perl,第二个将文件内容传递给Perl。在Unix或Perl的特殊属性下总是这样吗 第一个命令将文件名(每行一个)发送到程序的STDIN,后者-n导致perl循环(因为没有命令行参数) 第二个调用perl,并将文件名列表作为参数。如果在-n中传递参数,则会打

在学习Perl的同时,我也在学习Linux(Ubuntu),所以这是一段消磨时间的好时光

以下两者之间的区别是什么:

find . -type f | perl -nle '... #aka yada yada'


第一个将文件名传递给Perl,第二个将文件内容传递给Perl。在Unix或Perl的特殊属性下总是这样吗

第一个命令将文件名(每行一个)发送到程序的
STDIN
,后者
-n
导致
perl
循环(因为没有命令行参数)

第二个调用
perl
,并将文件名列表作为参数。如果在
-n
中传递参数,则会打开每个参数并读取每个文件中的每一行

因此,第一个操作文件名,第二个操作文件内容

您可以通过以下方式看到perl正在为您编写的代码:

产生

BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    print $_;
}
-e syntax OK
该命令实际上运行
cat/etc/passwd
,而不是打开名为
cat/etc/passwd |
的文件。为了防止这种行为,建议检查
@ARGV
是否有可疑名称,或使用模块为您清除
@ARGV

perl -MARGV::readonly -nle print "echo foo|"
Can't open < echo foo|: No such file or directory.
perl-MARGV::readonly-nle打印“echo foo |”
无法打开
第一个生成文件列表并将其“管道”到perl。perl然后通过读取标准输入来读取列表:

 while( <> ) { ... }
第二个参数生成文件名列表并将其转换为命令行参数,然后显示在程序中的
@ARGV

 foreach( @ARGV ) { ... }
这是Perl也不特有的特性。shell以程序可以访问的某种数据结构提供命令后的位。其他语言有相似的结构,即使它们看起来不一样

但是,菱形操作符
将自动遍历您在命令行上指定的文件名,以便
循环时仍然有效。这是Perl特有的特性

当你有一长串的参数时,第二种方法的问题就会显现出来。有些shell限制了可以在命令行上显示的内容的数量。因为这个原因,我不太喜欢第二个版本

但是,您可以将其转换为一个自包含的Perl程序,而不是使用find(1)(shell版本):

$ find2perl . -type f
输出是一个不必依赖任何外部命令的Perl程序。

您询问“第一个将文件名传递给Perl,第二个将文件内容传递给Perl。这在Unix或Perl的特殊属性下总是正确的吗?”此行为不是Perl特有的。部分工作是由Unix完成的。这更像是一个被广泛遵循的惯例。管道行为(命令后接
|
)由操作系统完成。程序对其命令行输入或生成的输出所做的操作是特定于命令的

例子。请在Bash中的计算机上完成操作

$ mkdir pipetestdir; cd pipetestdir    
$ for f in {a..z}; do printf "%s\n" "File: $f, line: "{1..1000} > $f.txt; done
这将创建一个空目录,将cd放入其中,并在空目录中创建26个文件,每个文件有1000行

使用Ubuntu/Linux实用程序
cat*.txt
可以查看文件的内容。Bash向所有26个
.txt
文件发送
*.txt
文件。使用
wc-l*.txt
可以验证所有26个文件的行数。您可以使用
wc-l{a..e}.txt的形式,其中Bash使用扩展。您可以将这些表单放在管道上,然后使用
cat*.txt | wc-l
仅获取所有26个文件的单行计数。在第一个示例中,
wc-l*.txt
打开26个文件,计算行数,并显示结果。在
cat*.txt | wc-l
的第二个示例中,程序
cat
正在打开26个文件并生成连接到STDOUT的文本流;
|
将其转换为一个管道,直接指向下一个程序;在这种情况下,
wc-l
在其标准输入上接收该输出,并计算该输出的行数,而不考虑单独的文件

使用Perl一行程序,您可以轻松地搜索这些文件。例如:

$ perl -lne 'print if /^.*666/' *.txt    # the devil's line from 26 files...
您可以使用
egrep
awk
执行相同的操作:

$ egrep '^.*666$' *.txt
$ awk "/^.*666$/ {print}" *.txt
如果将该表单转换为管道,则操作的是Perl(或awk或egrep)左侧上一个命令的输出。前一部分的STDOUT的输出被馈送到Perl的STDIN。如果该命令生成文件名,则您正在对文件名进行操作:

$ ls *.txt | perl -lne 'print if /c|d|z/'
$ find . -name '*.txt' | perl -lne 'print if /c|d|z/'
除非先用
cat
展开它们:

$ cat *.txt | perl -lne 'print if /^.*?(c|d|z).*?666$/'
其输出与此类似:

$ perl -lne 'print if /^.*?(c|d|z).*?666$/' *.txt
也许这就是你对表格的可互换性感到困惑的地方?他们不是!两件截然不同的事情正在发生。如果您使用
cat*.txt | perl'.'
将所有文件连接成一个长文本流,并发送到管道中的下一个阶段;在本例中,
perl'…'
。Perl无法区分哪个文本来自哪个文件。只是因为我们在创建文件时在每个文件中都做了标记,我们才能看到哪个文件是哪个文件

在另一种形式中,
perl'…'*.txt
,perl正在打开文件,并对每个文本流和文件具有完全控制权。您可以控制是否打开文件,是否打印文件名,等等

但是,要避免使用特定形式的
cata.txt | perl'.'
(即在单个文件上使用cat)来避免可怕的:-}

您特别询问了表格:

$ perl -nle '... # same yada yada' `find . -type f`
作为brian d foy,命令行的长度有限制,您应该警惕这种形式。您还可以让文件名以意外的方式以反勾号中断。与
xargs
一起使用
find
而不是back tick表单:

$ find . -type f -print0 | xargs -0 perl -nle 'print if /^.*666$/'
要查看中断文件名的问题,请键入以下命令:

$ mv z.txt "file name with spaces" 
$ perl -ple '' `find . -name "file*"`       #fails...
$ find . -name "file*" -print0 | xargs -0 perl -ple '' #works...
$ find . -type f -exec perl -wnl -e '/\s1$/ and print' {} + #alternative

谢谢我确实观察到了你的行为
$ perl -lne 'print if /^.*?(c|d|z).*?666$/' *.txt
$ perl -nle '... # same yada yada' `find . -type f`
$ find . -type f -print0 | xargs -0 perl -nle 'print if /^.*666$/'
$ mv z.txt "file name with spaces" 
$ perl -ple '' `find . -name "file*"`       #fails...
$ find . -name "file*" -print0 | xargs -0 perl -ple '' #works...
$ find . -type f -exec perl -wnl -e '/\s1$/ and print' {} + #alternative