【问题标题】:Bash script to replace all occurrences of string in files, including filenames用于替换文件中所有出现的字符串的 Bash 脚本,包括文件名
【发布时间】:2014-05-24 08:10:33
【问题描述】:

我希望能够在一个目录中减去“.git”目录的所有文件的文件内容和文件名中用另一个字符串替换出现的一个字符串。我有以下代码,这对于更改文件名很有用,但不会替换文件中的字符串。我做错了什么?

#!/bin/bash

# Replace occurances in files, lowercase
lower1=$(echo $1 | tr '[:upper:]' '[:lower:]')
lower2=$(echo $2 | tr '[:upper:]' '[:lower:]')
echo "Replacing $lower1 with $lower2..."
find . -type f \! -iregex '.\|./.git' -exec perl -i -pe 's/$lower1/$lower2/g' {} \;

# Replace all other occurances in files to capitalised
upper2=$(echo ${2:0:1} | tr '[:lower:]' '[:upper:]')${2:1}
echo "Replacing all $1 with $upper2..."
find . -type f \! -iregex '.\|./.git' -exec perl -i -pe 's/$1/$upper2/gi' {} \;

# Replace filenames. Use "bash -c" to pass files as arguments to mv command.
# This worked
find . -name "*$lower1*" -exec bash -c 'mv "$1" "${1/$2/$3}"' -- {} $lower1 $lower2 \;

我确实尝试过 sed,但这不允许不区分大小写。

我正在 OSX Mountain Lion 上对此进行测试,如果这会有所不同,但我确实希望它也可以在 Linux 上运行。我现在不在我的 Linux 机器上。

【问题讨论】:

  • sed 确实使用/i标志不区分大小写。
  • 显然不在 OSX 上。 i 标志不可移植。
  • 这里由于单引号,shell变量不会被插值:'s/$lower1/$lower2/g'
  • 是的,谢谢 bobbogo。我现在用双引号测试它。

标签: regex perl bash unix sh


【解决方案1】:

只需使用File::Find

以下代码将编辑整个目录树,因此我会小心使用它。也许先进行一些测试? :)

此脚本接受两个字符串作为参数,然后将所有出现的 $string1 替换为 $string2。对于完美的小写匹配,它将替换为小写 $string2,但对于所有其他匹配,它将替换为大写 $string2。

目前,它只会为文件重命名使用小写。

use strict;
use warnings;
use autodie;

use File::Find;

die "Usage: $0 From_string To_string\n" if @ARGV != 2;

my ($from, $to) = map lc, @ARGV;

finddepth(sub {
    return if $File::Find::dir =~ /.git\b/;

    # Inplace Edit of files
    if (-f) {
        local @ARGV = $_;
        local $^I = '.bak';
        while (<>) {
            s/\Q$from/$to/g;
            s/\Q$from/\U$to/ig;
            print; 
        }
        #unlink "$_$^I";  # Uncomment if you want to delete backups
    }

    # Rename File
    my $newfile = $_;
    if ($newfile =~ s/\Q$from/$to/ig) {
        rename $_, $newfile;
    }
}, '.');

【讨论】:

  • 感谢您的回答。我对 Perl 了解不多,所以我必须更深入地研究它,尽管看起来您的解决方案确实不是我想要的。我想用小写的新字符串替换小写的原始字符串,用大写的新字符串替换所有其他原始字符串。
  • 我也许能够弄清楚如何编写自己的 perl 脚本,但我认为确定我的 bash 脚本的问题并不难。
  • 是的,我仍然不清楚您要替换什么。如果您提供一些前后示例,我可能会更好地理解您的目标。无论如何,我提出的方法将适用于更改目录树中的所有文件内容和文件名,同时跳过 .git 目录。
  • 虽然,再次阅读您的脚本,我得到了更好的理解。一秒钟,可能会提出一些建议。
  • 更新了我的解决方案,以考虑到我对您的目标的新理解。
【解决方案2】:
find . -name .git -prune -o type f -exec perl -i -pe "s/\Q$1\E/\L$2\E/i" {} +
find . -name .git -prune -o type f -name "*$lower1*" -exec mmv "*$lower1*" "#1$lower2#2" {} +

如果您要进行大量文件重命名,则应检查中间结果是否被意外删除。 mmv 做得很好。

+ 是对find 的有用的性能提升。

【讨论】:

  • 谢谢,使用 + 后性能肯定会提高。但是 mmv 在 from 和 to 参数之后不接受参数。
  • 我试过用 ;使用 mmv 应该匹配所有子目录,但它似乎不起作用:mmv ";*$lower1*" "#1$lower2#2"mmv ";$lower1*" "#1$lower2#2"
  • 啊,mmv 对目录不起作用?我也需要它来处理目录。
  • @matthew mmv -r 重命名文件夹
  • @matthew 当您使用; 通配符时,不要忘记用完其中一个插槽。 mmv ";*$lower1*" "#1#2$lower2#3"
【解决方案3】:

这是我设法提出的解决方案。感谢所有帮助我实现目标的人:

#!/bin/bash

# Replace occurances in files, lowercase
lower1=$(echo $1 | tr '[:upper:]' '[:lower:]')
lower2=$(echo $2 | tr '[:upper:]' '[:lower:]')
echo "Replacing $lower1 with $lower2..."
find . -type f \! -iregex '.\|./.git' -exec perl -i'' -pe "s/$lower1/$lower2/g" {} +

# Replace all other occurances in files to capitalised
upper2=$(echo ${2:0:1} | tr '[:lower:]' '[:upper:]')${2:1}
echo "Replacing all $1 with $upper2..."
find . -type f \! -iregex '.\|./.git' -exec perl -i'' -pe "s/$1/$upper2/gi" {} +

# Replace filenames.
mmv -r ";*$lower1*" "#2$lower2#3"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-21
    • 2020-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 2019-11-03
    相关资源
    最近更新 更多