8
votes

Comment incrémenter le dernier nombre d'une chaîne; frapper

J'ai une chaîne qui ressemble à quelque chose comme / foo / bar / baz59_ 5stuff.thing

Je voudrais augmenter le dernier nombre (5 dans l'exemple) de un, s'il est supérieur à une autre variable. Un 9 serait augmenté à 10. Notez que le dernier numéro peut également être composé de plusieurs chiffres; aussi que "stuff.thing" pourrait être n'importe quoi; autre qu'un nombre; il ne peut donc pas être codé en dur.

L'exemple ci-dessus aboutirait à / foo / bar / baz59_ 6stuff.thing

J'ai trouvé plusieurs questions (et réponses) qui extrairaient le dernier nombre de la chaîne, et évidemment qui pourraient ensuite être utilisées dans une comparaison. Le problème que j'ai est de savoir comment m'assurer que lorsque je remplace, je ne remplace que le dernier numéro (car évidemment, je ne peux pas simplement remplacer "5" par "6"). Quelqu'un peut-il faire des suggestions?

awk / sed / bash / grep sont tous viables.


7 commentaires

Si le dernier chiffre est 9, quel est le résultat souhaité? Et si c'était 59?


si le dernier nombre est 9; alors le désiré est 10. 59; 60. 99-> 100 etc. (question mise à jour pour la postérité)


Il est toujours précédé d'un trait de soulignement de ce dernier numéro?


@gboffi no; le caractère précédent n'est pas fixe.


s'il est supérieur à un argument de script , vous voulez dire la chaîne entière ou le nombre? si le nombre, voulez-vous dire incrémenter le dernier nombre de la chaîne qui est supérieur à un argument, ou incrémenter le dernier nombre de la chaîne s'il est supérieur à un argument?


pas clair ce que vous entendez par s'il est supérieur à un argument de script pour votre exemple est-il à incrémenter si l'argument est donné à 4?


@karakfa question mise à jour. Donc, si j'essaie de comparer à 3 par exemple; le 5 serait augmenté; mais si on compare à 7 alors le 5 serait inchangé.


7 Réponses :


1
votes

s'il est supérieur à un argument de script.

Si je comprends correctement (je suppose que vous passez un argument via un script et si sa valeur est supérieure au deuxième chiffre de champ de la chaîne, augmentez de 1 le chiffre de ce deuxième champ), pourriez-vous essayer de suivre une fois. p>

/script.ksh 7
/foo/bar/baz59_ 6stuff.thing

Voici un exemple lorsque je lance script.ksh, il donne la sortie suivante.

cat script.ksh
value=$1
echo "/foo/bar/baz59_ 5stuff.thing" | 
awk -v arg="$value" '
  match($2,/[0-9]+/){
    val=substr($2,RSTART,RLENGTH)
    val=val<arg?val+1:val
    $2=val substr($2,RSTART+RLENGTH)
}
1'

4 commentaires

@UKMonkey, pourriez-vous s'il vous plaît vérifier celui-ci également une fois et me faire savoir si cela fonctionne pour vous?


@UKMonkey, cool heureux que cela vous ait aidé, pourquoi j'ai spécifiquement demandé n'est-ce pas une autre solution qui a pris l'argument du script et l'a comparé, donc je voulais m'assurer d'avoir bien compris la question, si vous avez testé en passant un argument alors cool :)


c'était le principe derrière le remplacement qui me tenait à cœur - l'implémentation complète, comme comment brancher la chaîne, etc. est un souci avec lequel je ne voulais pas infecter la question.


@UKMonkey, eh bien (si je comprends correctement) si c'est une exigence complète, ne vous inquiétez pas maintenant, vous avez une solution complète maintenant :)



7
votes

Réponse mise à jour

Merci à @EdMorton pour avoir signalé l'exigence supplémentaire du nombre dépassant un seuil. Cela peut être fait comme ceci:

perl -pe 's/(\d+)(?!.*\d+)/$1+1/e' <<< "abc123_456.txt"
abc123_457.txt

Réponse originale

Vous pouvez évaluer / calculer un remplacement avec / e dans les expressions rationnelles Perl. Ici, j'ajoute simplement 1 à la chaîne de chiffres capturée mais vous pouvez faire des choses plus compliquées:

perl -spe 's/(\d+)(?!.*\d+)/$1>$thresh? $1+1 : $1/e' <<<  "abc123_456.txt" -- -thresh=500

Le (?!. * \ D +) est (espérons-le) une anticipation négative pour tout autre chiffre.

Le $ 1 représente toute séquence de chiffres capturés dans le groupe de capture (\ d +) code >.

Notez que cela nécessiterait des modifications pour gérer les nombres décimaux, les nombres négatifs et la notation scientifique - mais c'est possible.


5 commentaires

Cela semble fonctionner parfaitement; et gère même une chaîne contenant des guillemets


ou s /.* (?


@EdMorton Bien repéré! Je n'avais pas vu cela, mais je l'ai ajouté maintenant.


ou s / (\ d +) (? = \ D * $) / $ 1 + 1 / ea


Merci à tous pour vos suggestions alternatives.



4
votes

Utilisation de la correspondance d'expressions régulières bash:

if [[ $f =~ ([0-9]+)([^0-9]+)$ ]]; then
    prefix=${f%${BASH_REMATCH[0]}}           # remove "99stuff.thing" from $f
    number=$(( 10#${BASH_REMATCH[1]} + 1 ))  # use "10#" to force base10
    new=${prefix}${number}${BASH_REMATCH[2]}
    echo $new
fi
# => /foo/bar/baz59_ 100stuff.thing

OK, qu'est-ce que nous avons maintenant?

$ declare -p BASH_REMATCH
declare -ar BASH_REMATCH=([0]="99stuff.thing" [1]="99" [2]="stuff.thing")

Nous pouvons donc construire le nouveau nom de fichier

$ f="/foo/bar/baz59_ 99stuff.thing"
$ [[ $f =~ ([0-9]+)([^0-9]+)$ ]]


0 commentaires

1
votes

An awk:

/foo/bar/baz59_ 99stuff.thing

Modifier :

Oups, j'ai raté l'exigence d'argument ( augmenter le dernier nombre - - par un, s'il est supérieur à un argument de script ):

$ echo /foo/bar/baz59_ 99stuff.thing |
awk -v arg=100 '
/[0-9]/ {
    rstart=1
    while(match(substr($0,rstart),/[0-9]+/)) {
        rstart+=RSTART+RLENGTH-1
        rlength=RLENGTH
    }
    v=substr($0,rstart-rlength,rlength)
    if(0+v>arg) {                           # test if v greater that argument
        print substr($0,1,rstart-rlength-1) v+1 substr($0,rstart)
        next
    }
}1'

Sortie maintenant:

$ echo /foo/bar/baz59_ 99stuff.thing |
awk '
/[0-9]/ {
    rstart=1                                    # keep track of the start
    while(match(substr($0,rstart),/[0-9]+/)) {  # while numbers last
        rstart+=RSTART+RLENGTH-1                # increase rstart
        rlength=RLENGTH                         # remember length too
    }
    v=substr($0,rstart-rlength,rlength)+1                    # increase last number
    print substr($0,1,rstart-rlength-1) v substr($0,rstart)  # print in parts
    next
}1'                                             # in case there was no number
/foo/bar/baz59_ 100stuff.thing

p >


1 commentaires

Mis à jour avec l'exigence d'argument de script. Je l'ai manqué au début.



1
votes

Voici une approche gnu awk plus courte:

awk -v max=80 -f incr.awk <<< '/foo/bar/baz59_ 5stuff.thing'
/foo/bar/baz59_ 6stuff.thing

awk -v max=80 -f incr.awk <<< '/foo/bar/baz59_ 79stuff.thing'
/foo/bar/baz59_ 80stuff.thing


awk -v max=80 -f incr.awk <<< '/foo/bar/baz59_ 90stuff.thing'
/foo/bar/baz59_ 90stuff.thing

awk -v max=80 -f incr.awk <<< '/foo/bar/baz59_ 80stuff.thing'
/foo/bar/baz59_ 80stuff.thing

awk -v max=80 -f incr.awk <<< '/foo/bar/stuff.thing'
/foo/bar/stuff.thing

Ensuite, utilisez-la comme:

cat incr.awk
{
   n = split($0, a, /[0-9]+/, b)
   for(i=1; i<n; i++)
      s = s a[i] b[i] + (b[i] < max && i == n-1 ? 1 : 0)
   print s a[i]
}


0 commentaires

4
votes

Avec GNU awk pour le 3ème argument à match ():

$ awk -v t=7 'match($0,/(.*)([0-9]+)([^0-9]*)$/,a) && a[2]>t{a[2]++; $0=a[1] a[2] a[3]} 1' file
/foo/bar/baz59_ 5stuff.thing

Il suffit de définir t sur quelle que soit la valeur de votre seuil d'incrémentation, par exemple: p >

$ awk -v t=3 'match($0,/(.*)([0-9]+)([^0-9]*)$/,a) && a[2]>t{a[2]++; $0=a[1] a[2] a[3]} 1' file
/foo/bar/baz59_ 6stuff.thing


0 commentaires

0
votes

si le nombre 'testing' est dans la variable 'bound':

perl -pe 'BEGIN{$bound=6} s{(\d+)_(\d+)(?!.*\d+)}{ $i=$2+1;($i>$bound? $1+1:$1)."_".$i}e' <<<"/foo/bar/baz59_5stuff.thing"
/foo/bar/baz59_6stuff.thing


0 commentaires