在 cmd 中使用 Perl 递归搜索和替换 (Windows)
我正在使用此命令在命令提示符中搜索字符串并将其替换为另一个字符串:
perl -pi -i.bak -e "s/Mohan/Sitaram/g" ab.txt
这会将文件 ab.txt
中的 Mohan
替换为 Sitaram
在当前目录中。
但是,我想将所有子目录中的所有 .txt
文件中所有出现的 Mohan
替换为 Sitaram
(递归地)。使用 *.txt
代替 ab.txt
不起作用。正则表达式可以正常工作,因为我已经下载了 Windows 的正则表达式包。它不仅仅适用于这个命令,说“
E:\>perl -pi -e "s/Sitaram/Mohan/g" *.txt
Can't open *.txt: Invalid argument.
有什么方法可以解决这个问题吗?”也许是不同的命令?
I am using this command to search and replace a string with another in the command prompt:
perl -pi -i.bak -e "s/Mohan/Sitaram/g" ab.txt
This replaces Mohan
with Sitaram
in the file ab.txt
in the current directory.
However I want to replace all occurrences of Mohan
with Sitaram
in all .txt
files in all the sub-directories (recursively). Using *.txt
instead of ab.txt
doesn’t work. Regular expressions work otherwise as I have downloaded the regex packages for Windows. It doesn’t work only for this command saying
E:\>perl -pi -e "s/Sitaram/Mohan/g" *.txt
Can't open *.txt: Invalid argument.
Is there any way to fix this? A different command perhaps?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
<代码>查找 . -名称“*.txt”| xargs perl -p -i -e "s/Sitaram/Mohan/g"
find
用于递归搜索所有 *.txt 文件。xargs
用于从标准输入构建和执行命令行。find . -name "*.txt" | xargs perl -p -i -e "s/Sitaram/Mohan/g"
find
is used to search all *.txt files recursively.xargs
is used to build and execute command lines from standard input.Windows 解决方案
在 Windows 上,可以使用
forfiles
命令。/s
选项告诉它递归地搜索目录。如果需要从当前工作目录以外的目录开始搜索,请提供
/p path\to\start
。Unix 解决方案
在 Unix 上,有一个比
forfiles
更通用的命令,称为xargs
,它将其标准输入行作为给定命令的参数传递。使用find
命令在目录中递归搜索.txt
文件。独立于平台的解决方案
您还可以在 Perl 中编写文件搜索和字符串替换的代码。
File::Find
核心模块可以帮助解决这个问题。 (核心模块 = 与解释器一起分发。)但是 Perl 代码会更长,我不想花时间编写它。使用上面链接的
File::Find
联机帮助页中的信息自行实现子程序。它应该测试文件名是否以.txt
结尾并且不是目录,创建其备份并通过备份的更改版本重写原始文件。Windows 上的引用会有所不同 - 也许将脚本写入文件将是唯一明智的解决方案。
OP 原始方法的问题
在 Unix shell 中,glob 模式(例如
*.txt
)由 shell 扩展,而 Windows cmd 保持它们不变并将它们直接传递给正在调用的程序。处理它们是它的工作。 Perl 显然不能做到这一点。第二个问题是,即使在 Unix 下,通配符也无法按预期工作。
*.txt
是当前目录下的所有.txt
文件,不包括子目录及其子目录中的文件...Windows solution
On Windows, a command can be executed for multiple files using the
forfiles
command. The/s
option tells it to search directories recursively.If starting the search from other than the current working directory is desired, supply
/p path\to\start
.Unix solution
On Unix, there is a more generic command than
forfiles
calledxargs
, which passes lines of its standard input as arguments to the given command. Directories are searched recursively for.txt
files using thefind
command.Platform-independent solution
You can also code both the search for files and string replacement in Perl. The
File::Find
core module can help with that. (Core module = distributed with the interpreter.)However the Perl code will be longer and I don’t want to spend time writing it. Implement the sub yourself using info from the
File::Find
manpage linked above. It should test if the file name ends with.txt
and is not a directory, create its backup and rewrite the original file by the changed version of the backup.The quoting will differ on Windows – perhaps writing the script into a file will be the only sane solution there.
Problems with OP’s original approach
In Unix shell, glob patterns (e.g.
*.txt
) are expanded by the shell, whereas Windows cmd leaves them untouched and passes them right to the program being invoked. It is its job to handle them. Perl cannot do that obviously.Second problem is that even under Unix, globbing would not work as desired.
*.txt
are all.txt
files in the current directory, not including those in subdirectories and their subdirectories…如果您打算使用 Perl,为什么不干脆全力以赴编写一个(简短的)Perl 程序来为您完成此任务呢?
这样,您就不会在 shell 和程序之间传递它,并且您拥有更通用且可以在多个操作系统上运行的东西。
我使用
File::Find
收集我想要在@files
数组中修改的所有文件。我可以将整个程序放入find
子例程中:整个程序以这种方式位于
wanted
子例程中。它更有效,因为我现在正在查找文件时进行替换。无需先遍历,找到文件,然后进行替换。然而,我觉得它的设计很糟糕。您还可以将整个文件放入一个数组中,而无需首先循环遍历它:
现在,您可以使用
grep
来检查是否有任何需要替换的内容:这更高效(不需要进行替换,除非该文件具有您要查找的字符串)。但是,它会占用内存(整个文件必须同时位于内存中),不要让这一行欺骗了您。整个文件仍然一次一行读入该数组,就像执行整个循环一样。
File::Find
和File::Copy
是标准 Perl 模块,因此所有 Perl 安装都包含它们。If you're going to bother with Perl, why not simply go all out and write a (short) Perl program to do this for you?
This way, you're not passing it off between the shell and your program, and you have something that's more universal and can run on multiple operating systems.
I use
File::Find
to gather all of the files that I want to modify in my@files
array. I could have put the whole thing inside thefind
subroutine:The whole program is in the
wanted
subroutine this way. It's more efficient because I'm now replacing as I am finding the files. No need to go through first, find the files, then do the replacement. However, it strikes me as bad design.You can also slurp your entire file into an array without looping through it at first:
Now, you can use
grep
to check to see if there's anything that needs to be replaced:This is more efficient (no need to do a replace unless that file has the string you're looking for). However, it takes up memory (the entire file must be in memory at one time), and don't let that one line fool you. The entire file is still read into that array one line at a time as if you did an entire loop.
The
File::Find
andFile::Copy
are standard Perl modules, so all Perl installations have them.