50

直截了当,我想知道如何使用 grep/find/sed/awk 来匹配某个字符串(以数字结尾)并将该数字增加 1。我最接近的是将 1 连接到最后(效果很好),因为要点是简单地更改值。这是我目前正在做的事情:

find . -type f | xargs sed -i 's/\(\?cache_version\=[0-9]\+\)/\11/g'

由于我不知道如何增加数字,我捕获了整个内容并添加了一个“1”。之前,我有这样的事情:

find . -type f | xargs sed -i 's/\?cache_version\=\([0-9]\+\)/?cache_version=\11/g'

所以至少我明白如何捕捉我需要的东西。

我不会解释这是为了什么,而是解释我想要它做什么。它应该根据当前目录递归地在任何文件中查找文本(不重要,它可以是任何目录,所以我稍后会配置它),匹配“?cache_version =”和一个数字。然后它将增加该数字并在文件中替换它。

目前我上面的东西有效,只是我不能在最后增加找到的数字。能够递增而不是附加“1”会更好,这样未来的值就不会是“11”、“111”、“1111”、“11111”等等。

我已经阅读了数十篇文章/解释,并且经常建议使用awk,但我不能终生将它们混合在一起。我最接近 usingawk的,它实际上并没有取代任何东西,是:

grep -Pro '(?<=\?cache_version=)[0-9]+' . | awk -F: '{ print "match is", $2+1 }'

我想知道是否有某种方法可以sed在末尾传递 a 并传递原始文件名,以便sed可以具有文件名和递增的数字(来自awk),或者它需要的任何东西xargs

从技术上讲,这个数字并不重要。这个替换主要是为了确保那里有一个新的数字,100% 肯定与上一个不同。所以当我写这个问题时,我意识到我不妨使用系统时间 - 自纪元以来的秒数(AJAX 经常使用该技术来消除对后续“相同”请求的缓存)。我最终得到了这个,它看起来很完美:

CXREPLACETIME=`date +%s`; find . -type f | xargs sed -i "s/\(\?cache_version\=\)[0-9]\+/\1$CXREPLACETIME/g"

(我首先存储该值,以便所有文件都获得相同的值,以防它因任何原因跨越数秒)

但我仍然很想知道关于增加匹配数字的原始问题。我猜一个简单的解决方案是将它变成一个 bash 脚本,但是,我认为有一种比递归循环遍历每个文件并检查其内容是否匹配然后替换更简单的方法,因为它只是增加一个匹配的数字...没有太多其他逻辑。我只是不想写入任何其他文件或类似的东西 - 它应该就地执行,就像sed使用“i”选项一样。

4

5 回答 5

69

我认为查找文件对您来说不是困难的部分。因此,我直奔主题,进行 +1 计算。如果您有gnu sed,可以通过以下方式完成:

sed -r 's/(.*)(\?cache_version=)([0-9]+)(.*)/echo "\1\2$((\3+1))\4"/ge' file

举个例子:

kent$  cat test 
ello
barbaz?cache_version=3fooooo
bye

kent$  sed -r 's/(.*)(\?cache_version=)([0-9]+)(.*)/echo "\1\2$((\3+1))\4"/ge' test     
ello                                                                             
barbaz?cache_version=4fooooo
bye

如果您愿意,可以添加 -i 选项。

编辑

/e允许您将匹配的部分传递给外部命令,并用执行结果进行替换。仅限 Gnu sed。

看这个例子:外部命令/工具echobc被使用

kent$  echo "result:3*3"|sed -r 's/(result:)(.*)/echo \1$(echo "\2"\|bc)/ge'       

给出输出:

result:9

您可以使用其他强大的外部命令,如 cut、sed(再次)、awk...

于 2013-01-15T23:32:48.220 回答
9

perl命令将搜索当前目录中的所有文件(无需遍历它,您将需要File::Find模块或类似文件来完成更复杂的任务)并将增加匹配的行数cache_version=。它使用/e评估替换部分的正则表达式的标志。

perl -i.bak -lpe 'BEGIN { sub inc { my ($num) = @_; ++$num } } s/(cache_version=)(\d+)/$1 . (inc($2))/eg' *

file在当前目录中使用以下数据对其进行了测试:

hello
cache_version=3
bye

它备份原始文件(ls -1):

file
file.bak

file现在有了:

hello
cache_version=4
bye

我希望它对您正在寻找的东西有用。


UPDATE用于File::Find遍历目录。它接受*作为参数,但会将它们与找到的参数一起丢弃File::Find。开始搜索的目录是脚本执行的当前目录。它在行中被硬编码find( \&wanted, "." )

perl -MFile::Find -i.bak -lpe '

    BEGIN { 
        sub inc { 
            my ($num) = @_; 
            ++$num 
        }

        sub wanted {
            if ( -f && ! -l ) {  
                push @ARGV, $File::Find::name;
            }
        }

        @ARGV = ();
        find( \&wanted, "." );
    }

    s/(cache_version=)(\d+)/$1 . (inc($2))/eg

' *
于 2013-01-15T23:16:07.210 回答
9

sed版本:

此版本不依赖于其他命令或环境变量。它使用显式携带。对于进位,我使用 @ 符号,但如果您愿意,可以使用其他名称。使用输入文件中不存在的内容。首先,它找到SEARCHSTRING<number>并附加一个 @ 到它。它重复具有未决进位的递增数字(即,在其后有一个进位符号:)[0-9]@如果递增 9,则此递增本身产生一个进位,并且该过程将重复,直到没有更多未决进位为止。最后,已产生但未添加到数字的进位被 1 替换。

sed "s/SEARCHSTRING[0-9]*[0-9]/&@/g;:a {s/0@/1/g;s/1@/2/g;s/2@/3/g;s/3@/4/g;s/4@/5/g;s/5@/6/g;s/6@/7/g;s/7@/8/g;s/8@/9/g;s/9@/@0/g;t a};s/@/1/g" numbers.txt
于 2015-09-29T10:27:29.960 回答
3

这很难看(我有点生疏),但这是使用 sed 的开始:

orig="something1" ;
text=`echo $orig | sed "s/\([^0-9]*\)\([0-9]*\)/\1/"` ;
num=`echo $orig | sed "s/\([^0-9]*\)\([0-9]*\)/\2/"` ;
echo $text$(($num + 1))

使用“something1”的原始文件名 ( $orig),sed 将文本和数字部分拆分为$textand $num,然后在最后一部分中将它们与递增的数字组合,从而产生something2.

只是一个开始,因为它不考虑文件名中带有数字或名称末尾没有数字的情况,但希望有助于您使用 sed 的最初目标。

这实际上可以在 sed 中通过使用缓冲区来简化,我相信(sed 可以递归操作),但我对它的这方面真的很生疏。

于 2013-01-15T23:26:37.813 回答
0
perl -pi -e 's/(\?cache_version=)(\d+)/$1.($2+1)/ge' FILE [FILE...]

或完整的解决方案:

find . -type f | xargs perl -pi -e 's/(\?cache_version=)(\d+)/$1.($2+1)/ge'

perl 替换运算符

  • /e修饰符对替换进行评估,就好像它是一个 Perl 语句一样,使用它的返回值作为替换文本。
  • .运算符在 Perl 中连接字符串。括号确保算术运算$2+1优先于串联。
  • /g修饰符将替换应用于行内的所有匹配字符串

perl 选项

  • -p确保 perl 将在每个文件的每一行上执行命令
  • -i确保每个文件都将就地编辑
  • -e指定执行的 perl 命令(在本例中为替换操作)
于 2021-12-17T01:55:42.017 回答