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

Ian*_*Ian 38 bash awk sed

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

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

由于我无法弄清楚如何增加数字,我抓住了整个事情,只是附加了一个"1".以前,我有这样的事情:

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

所以至少我理解如何捕捉我需要的东西.

我不会解释这是什么,而只是解释我想要它做什么.它应该基于当前目录递归地在任何文件中找到文本(不重要,它可以是任何目录,所以我稍后再配置),将"?cache_version ="与数字匹配.然后它将增加该数字并将其替换为文件.

目前我上面的东西工作,只是我不能在最后增加找到的数字.能够增加而不是附加"1"以便将来的值不是"11","111","1111","11111"等等会更好.

我已经经历了数十篇文章/解释,而且经常就是建议是使用awk,但我不能为我的生活混合它们.我最接近使用的awk,实际上并没有取代任何东西,是:

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

我想知道是否有一些方法可以sed在最后输出一个并传递原始文件名,以便sed可以拥有文件名和增加的数字(来自awk),或者它需要的任何东西xargs.

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

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

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

但我仍然希望知道原始问题,增加一个匹配的数字.我猜一个简单的解决办法就是让它成为一个bash脚本,但是,我认为有一种比递归循环遍历每个文件并检查其内容然后替换更简单的方法,因为它只是递增一个匹配的数字......没有其他逻辑.我只是不想写任何其他文件或类似的东西 - 它应该在适当的位置,就像sed"i"选项一样.

Ken*_*ent 56

我认为找到文件对你来说并不困难.因此,我只是去做点+1,进行+1计算.如果你有gnu sed,可以用这种方式完成:

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

我们来举个例子:

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
Run Code Online (Sandbox Code Playgroud)

如果你愿意,可以添加-i选项.

编辑

/e允许您将匹配的部分传递给外部命令,并用执行结果替换.Gnu只有.

看到这个例子:外部命令/工具echo,bc用于

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

给出输出:

result:9
Run Code Online (Sandbox Code Playgroud)

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

  • 啊,我明白了。我发誓我做到了。好的,可以解决“ &lt;”字符问题。现在唯一的问题是写回文件时会剥离`“`。您知道会发生什么吗?也许只是`-i`问题,但是很奇怪。以前有`”`,但是在脚本运行后消失了 (2认同)

小智 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
Run Code Online (Sandbox Code Playgroud)

  • 好的解决方案 为了不替换@符号,似乎可以用另一个不太常见的符号替换@,例如£:sed"s/cache_version = [0-9]*[0-9] /&£/ g;:a {s/0 £/ 1 /克; S/1£/ 2 /克; S/2£/ 3 /克; S/3£/ 4 /克; S/4£/ 5 /克; S/5£/ 6 /克; s/6£/ 7/g; s/7£/ 8/g; s/8£/ 9/g; s/9£/£0/g; ta}; s /£/ 1/g"$ 1 (2认同)

Bir*_*rei 7

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

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

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

hello
cache_version=3
bye
Run Code Online (Sandbox Code Playgroud)

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

file
file.bak
Run Code Online (Sandbox Code Playgroud)

file现在:

hello
cache_version=4
bye
Run Code Online (Sandbox Code Playgroud)

我希望它对你所寻找的东西有用.


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

' *
Run Code Online (Sandbox Code Playgroud)