如何使用sed / awk查找/替换和增加匹配的数字?

时间:2013-01-15 22:52:38

标签: bash sed awk

直截了当,我想知道如何使用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,但我不能为我的生活混合它们。我最接近使用awk,实际上并没有取代任何东西,是:

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

我想知道是否有某种方法可以在最后管道sed并传递原始文件名,以便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 个答案:

答案 0 :(得分:52)

我认为找到文件对你来说并不困难。因此,我只是去做点+1,进行+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 only。

请参阅此示例:使用外部命令/工具echobc

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

给出输出:

result:9

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

答案 1 :(得分:8)

sed 版本:

此版本与其他命令或环境变量无关。 它使用明确的携带。对于携带,我使用@符号,但如果您愿意,可以使用其他名称。使用输入文件中不存在的内容。 首先,它找到[0-9]@并向其添加@。 它重复递增具有待处理进位的数字(即在其后面有一个进位符号: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 ) 如果9递增,则该增量产生一个进位本身,并且该过程将重复,直到没有更多未决的进位。 最后,产生但未添加到数字但仍由1代替的承载。

            int WIDTH_ARROW_BUTTON = 2 * 255;
        for (int i = 0; i < row.getLastCellNum(); i++) {
            sheet.autoSizeColumn(i);
            // For filter additional arrow width
            sheet.setColumnWidth(i, sheet.getColumnWidth(i) + WIDTH_ARROW_BUTTON);
        }

答案 2 :(得分:7)

perl命令将搜索当前目录中的所有文件(不遍历它,您将需要File::Find模块或类似的更复杂的任务)并将增加匹配的行数{ {1}}。它使用正则表达式的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

我希望它对您所寻找的内容有用。


UPDATE 使用hello cache_version=4 bye 遍历目录。它接受File::Find作为参数,但会将其与*中找到的那些一起丢弃。开始搜索的目录是脚本执行的当前状态。它在行File::Find中进行了硬编码。

find( \&wanted, "." )

答案 3 :(得分: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将文本和数字部分拆分为$text$num,然后将这些部分合并到最后一部分中递增的数字,导致something2

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

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