How to find/replace and increment a matched number with sed/awk?

后端 未结 4 936
情书的邮戳
情书的邮戳 2020-11-30 01:53

Straight to the point, I\'m wondering how to use grep/find/sed/awk to match a certain string (that ends with a number) and increment that number by 1. The closest I\'ve come

相关标签:
4条回答
  • 2020-11-30 02:10

    This is ugly (I'm a little rusty), but here's a start using 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))
    

    With an original filename ($orig) of "something1", sed splits off the text and numeric portions into $text and $num, then these are combined in the final section with an incremented number, resulting in something2.

    Just a start since it doesn't consider cases with numbers within the file name or names with no number at the end, but hopefully helps with your original goal of using sed.

    This can actually be simplified within sed by using buffers, I believe (sed can operate recursively), but I'm really rusty with that aspect of it.

    0 讨论(0)
  • 2020-11-30 02:11

    I think finding file isn't the difficult part for you. I therefore just go to the point, to do the +1 calculation. If you have gnu sed, it could be done in this way:

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

    let's take an example:

    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
    

    you could add -i option if you like.

    edit

    /e allows you to pass matched part to external command, and do substitution with the execution result. Gnu sed only.

    see this example: external command/tool echo, bc are used

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

    gives output:

    result:9
    

    you could use other powerful external command, like cut, sed (again), awk...

    0 讨论(0)
  • 2020-11-30 02:13

    Pure sed version:

    This version has no dependencies on other commands or environment variables. It uses explicit carrying. For carry I use the @ symbol, but another name can be used if you like. Use something that is not present in your input file. First it finds SEARCHSTRING<number> and appends a @ to it. It repeats incrementing digits that have a pending carry (that is, have a carry symbol after it: [0-9]@) If 9 was incremented, this increment yields a carry itself, and the process will repeat until there are no more pending carries. Finally, carries that were yielded but not added to a digit yet are replaced by 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
    
    0 讨论(0)
  • 2020-11-30 02:31

    This perl command will search all files in current directory (without traverse it, you will need File::Find module or similar for that more complex task) and will increment the number of a line that matches cache_version=. It uses the /e flag of the regular expression that evaluates the replacement part.

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

    I tested it with file in current directory with following data:

    hello
    cache_version=3
    bye
    

    It backups original file (ls -1):

    file
    file.bak
    

    And file now with:

    hello
    cache_version=4
    bye
    

    I hope it can be useful for what you are looking for.


    UPDATE to use File::Find for traversing directories. It accepts * as argument but will discard them with those found with File::Find. The directory to begin the search is the current of execution of the script. It is hardcoded in the line 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
    
    ' *
    
    0 讨论(0)
提交回复
热议问题