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 Réponses :
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.thingVoici 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'
@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 :)
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.
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.
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]+)$ ]]
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 >
Mis à jour avec l'exigence d'argument de script. Je l'ai manqué au début.
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] }
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
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
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é.