我有代码:
open(FILE, "<$new_file") or die "Cant't open file \n";
@lines=<FILE>;
close FILE;
open(STDOUT, ">$new_file") or die "Can't open file\n";
$old_fh = select(OUTPUT_HANDLE);
$| = 1;
select($old_fh);
for(@lines){
s/(.*?xsl.*?)xsl/$1xslt/;
print;
}
close(STDOUT);
STDOUT -> autoflush(1);
print "file changed";
关闭STDOUT
关闭程序后,不会写下最后一次打印print "file changed"
。为什么是这样?
* 已编辑 * 打印消息我想在控制台上写入否文件
答案 0 :(得分:4)
我认为这是因为print
默认文件句柄是STDOUT
,此时它已经关闭。您可以重新打开它,或打印到其他文件句柄,例如STDERR
。
print STDERR "file changed";
答案 1 :(得分:3)
这是因为您已关闭STDOUT
中存储的文件句柄,因此print
无法再使用它。一般来说,将一个新的文件句柄打开到一个预定义的句柄名称中并不是一个好主意,因为它必然会导致混淆。使用词法文件句柄或输出文件只是一个不同的名称要清楚得多。是的,您必须在print
电话中指定文件句柄,但是您对STDOUT
发生的事情没有任何疑惑。
答案 2 :(得分:3)
print
语句将输出STDOUT
中的字符串,这是默认的输出文件句柄。
所以声明
print "This is a message";
与
相同 print STDOUT "This is a message";
在您的代码中,您已关闭STDOUT
,然后打印消息,这将无效。重新打开STDOUT
文件句柄或不要关闭它。当脚本结束时,文件句柄将自动关闭
答案 3 :(得分:2)
open OLDOUT, ">&", STDOUT;
close STDOUT;
open(STDOUT, ">$new_file") or die "Can't open file\n";
...
close(STDOUT);
open (STDOUT, ">&",OLDOUT);
print "file changed";
答案 4 :(得分:2)
您似乎对perl中file IO operations的完成情况感到困惑,所以我建议您仔细阅读。
你在做的是:
您最大的主要错误是尝试重新打开默认输出文件句柄STDOUT。我认为这是因为你不知道print
是如何工作的,即你可以提供一个文件句柄来打印到print FILEHANDLE "text"
。或者您不知道STDOUT是预定义的文件句柄。
您的其他错误:
use strict; use warnings;
。你写的程序不应该没有这些。它们会阻止您做坏事,并向您提供有关错误的信息,并且可以节省您数小时的调试时间。select
设置打印的默认文件句柄,阅读文档。这很少是您需要关注的事情。变量$|
为当前选定的文件句柄 设置autoflush(如果设置为真值) 。所以你实际上没有做到什么,因为OUTPUT_HANDLE是一个不存在的文件句柄。如果您跳过select
语句,则会为STDOUT设置autoflush。 (但你不会注意到任何差异)print
使用打印缓冲区,因为它很有效。我假设您正在尝试autoflush,因为您认为您的打印件被缓冲区捕获,这是不正确的。一般来说,这不是你需要担心的事情。程序结束时,所有打印缓冲区都会自动刷新。open my $fh, ...
代替全球,例如由于之前的陈述,建议使用open FILE, ..
,因为避免全局变量总是一个好主意。open FILEHANDLE, MODE, FILENAME
。这是因为您在文件名中冒着元字符的风险会损坏您的open
语句。 现在,正如我在评论中所说,这个 - 或者更确切地说,你想要的,因为这段代码是错误的 - 与{{1}的习惯用法非常相似命令行开关:
-p
这个简短的小片实际上完成了你的程序所做的一切,但做得更好。说明:
perl -pi.bak -e 's/(.*?xsl.*?)xsl/$1xslt/' file.txt
switch会自动假定您提供的代码位于-p
循环内,并在执行代码后打印每一行。while (<>) { }
开关告诉perl对文件进行inplace-edit,将备份副本保存在&#34; file.txt.bak&#34;。所以,这个单行程相当于这样的程序:
-i
这相当于:
$^I = ".bak"; # turns inplace-edit on
while (<>) { # diamond operator automatically uses STDIN or files from @ARGV
s/(.*?xsl.*?)xsl/$1xslt/;
print;
}
inplace-edit选项实际上会创建一个单独的文件,然后将其复制到原始文件上。如果使用备份选项,则首先备份原始文件。您不需要 知道此信息,只需知道使用my $file = shift; # first argument from @ARGV -- arguments
open my $fh, "<", $file or die $!;
open my $tmp, ">", "/tmp/foo.bar" or die $!; # not sure where tmpfile is
while (<$fh>) { # read lines from org file
s/(.*?xsl.*?)xsl/$1xslt/;
print $tmp $_; # print line to tmp file
}
rename($file, "$file.bak") or die $!; # save backup
rename("/tmp/foo.bar", $file) or die $!; # overwrite original file
开关将导致-i
(和-p
)选项实际执行原始文件的更改。
使用-n
开关并且不需要激活备份选项(Windows除外),但建议使用。一个好主意是在没有选项的情况下运行单行,因此输出将打印到屏幕上,然后在看到输出正常后添加它。
-i
您搜索包含&#34; xsl&#34;的字符串两次。 s/(.*?xsl.*?)xsl/$1xslt/;
的使用在第二种情况下是好的,但在第一种情况下则不然。每当你发现自己用通配符字符串开始正则表达式时,你可能做错了什么。 除非您试图捕获该部分。
在这种情况下,你捕获并删除它,只是把它放回去,这是完全没用的。所以第一项业务就是把这部分拿出来:
.*?
现在,删除一些东西然后把它放回去真的只是一个不删除它的魔术。我们不需要这样的魔术技巧,当我们不能首先删除它时。使用look-around assertions,您可以实现此目标。
在这种情况下,由于你有一个可变长度的表达式并需要一个look- 后面的断言,我们必须使用s/(xsl.*?)xsl/$1xslt/;
(助记符:保持)选项,因为可变长度后视镜未实现。
\K
所以,既然我们没有采取任何措施,我们就不需要使用s/xsl.*?\Kxsl/xslt/;
来回馈任何东西。现在,您可能会注意到,&#34;嘿,如果我更换了&#39; xsl&#39;使用&#39; xslt&#39;,我不需要删除&#39; xsl&#39;在所有。&#34;这是真的:
$1
您可以考虑使用此正则表达式的选项,例如s/xsl.*?xsl\K/t/;
,这会导致它忽略大小写,从而也匹配诸如&#34; XSL FOO XSL&#34;等字符串。或/i
选项允许它每行执行所有可能的匹配,而不仅仅是第一个匹配。阅读perlop中的更多内容。
完成的单行是:
/g