sencillos scripts script resueltos programas pasar parametros operaciones manejo ejercicios ejemplos cadenas aritmeticas bash wine

resueltos - scripts bash ejemplos



¿Cómo incrementar el número de versión en un script de shell? (7)

El siguiente script de control de versión simple está destinado a encontrar el último número de versión de un archivo dado, a incrementarlo, a ejecutar un comando dado con el archivo recién creado (por ejemplo, al editor), y luego guardarlo en estable. Ya que es simple, no comprueba nada ya que el script se modificará según sea necesario. Por ejemplo, si el resultado no será estable, el usuario puede omitir el último argumento.

Sin embargo, una de las principales preocupaciones de la funcionalidad actual es cómo implementar lo siguiente: si la última sección después del punto tiene dos dígitos, hasta 99; si solo es 1, inc hasta 9, luego pase a la sección anterior. Las versiones pueden tener cualquier número entero positivo de secciones.

1.2.3.44 -> 1.2.3.45 1.2.3.9 -> 1.2.4.0 1.2.3 -> 1.2.4 9 -> 10

El problema restante es que no espera a que un editor de vino con pestañas cierre el archivo; El objetivo es detectar cuándo se cierra la pestaña. Además, ¿podría explicar cuál es la mejor manera de asegurarse de que los nombres de mis variables no sobrescriban los existentes?

También puedes ofrecer otras mejoras.

#!/bin/bash #Tested on bash 4.1.5 #All arguments in order: "folder with file" "file pattern" cmd [stable name] folder="$1" file_pattern="$2" cmd="$3" stable="$4" cd "$folder" last_version=$(ls --format=single-column --almost-all | / grep "$file_pattern" | / sed -nr ''s/^[^0-9]*(([0-9]+/.)*[0-9]+).*//1/p'' | / sort -Vu | / tail -n 1) last_version_file=$(ls --format=single-column --almost-all | / grep "$file_pattern" | / grep $last_version | / tail -n 1) #tail -n 1 is only needed to get 1 line if there are backup files with the same version number new_version=$(echo $last_version | / gawk -F"." ''{$NF+=1}{print $0RT}'' OFS="." ORS="") #increments last section indefinitely new_version_file=$(echo "$last_version_file" | / sed -r "s/$last_version/$new_version/") cp "$last_version_file" "$new_version_file" "$cmd" "$new_version_file" & / wait #works with gedit but not with wine tabbed editor [[ "$stable" ]] && / cp "$new_version_file" "$stable" #True if the length of string is non-zero.

Actualización: Lo siguiente funciona en mi PC, lo actualizaré si se encuentran mejoras o soluciones a problemas no resueltos:

#!/bin/bash inc() { shopt -s extglob num=${last_version//./} let num++ re=${last_version//./)(} re=${re//[0-9]/.}'')'' re=${re#*)} count=${last_version//[0-9]/} count=$(wc -c<<<$count) out='''' for ((i=count-1;i>0;i--)) ; do out=''.//'$i$out done sed -r s/$re$/$out/ <<<$num } folder="$1" file_pattern="$2" cmd="$3" stable="$4" cd "$folder" last_version=$(ls --format=single-column --almost-all | / grep "$file_pattern" | / sed -nr ''s/^[^0-9]*(([0-9]+/.)*[0-9]+).*//1/p'' | / sort -Vu | / tail -n 1) #--almost-all do not list implied . and .. last_version_file=$(ls --format=single-column --almost-all | / grep "$file_pattern" | / grep $last_version | / tail -n 1) #tail -n 1 is only needed to get 1 line if there are backup files with the same version number new_version=$(inc) new_version_file=$(echo "$last_version_file" | / sed -r "s/$last_version/$new_version/") cp "$last_version_file" "$new_version_file" "$cmd" "$new_version_file" && / wait #works with gedit but not tabbed wine editor [[ "$stable" ]] && / cp "$new_version_file" "$stable" #True if the length of string is non-zero.

Aprecio la variedad de soluciones que se han ofrecido, ya que ayudan a obtener una perspectiva y hacer una comparación.


Aquí hay un par de opciones más flexibles. Ambos aceptan un segundo argumento para indicar qué posición incrementar.

1. Función simple

Para una entrada más predecible.

# Usage: increment_version <version> [<position>] increment_version() { local v=$1 if [ -z $2 ]; then local rgx=''^((?:[0-9]+/.)*)([0-9]+)($)'' else local rgx=''^((?:[0-9]+/.){''$(($2-1))''})([0-9]+)(/.|$)'' for (( p=`grep -o "/."<<<".$v"|wc -l`; p<$2; p++)); do v+=.0; done; fi val=`echo -e "$v" | perl -pe ''s/^.*''$rgx''.*$/$2/''` echo "$v" | perl -pe s/$rgx.*$''/${1}''`printf %0${#val}s $(($val+1))`/ } # EXAMPLE -------------> # RESULT increment_version 1 # 2 increment_version 1.0.0 # 1.0.1 increment_version 1 2 # 1.1 increment_version 1.1.1 2 # 1.2 increment_version 00.00.001 # 00.00.002

2. Función robusta.

Para usar con scripts, o más personalización para aplicar a varios sistemas de versiones. Podría usar un par de opciones más, pero tal como está ahora funciona para mis proyectos usando las secuencias de versión "major.minor [.maintenance [.build]]".

# Accepts a version string and prints it incremented by one. # Usage: increment_version <version> [<position>] [<leftmost>] increment_version() { local usage=" USAGE: $FUNCNAME [-l] [-t] <version> [<position>] [<leftmost>] -l : remove leading zeros -t : drop trailing zeros <version> : The version string. <position> : Optional. The position (starting with one) of the number within <version> to increment. If the position does not exist, it will be created. Defaults to last position. <leftmost> : The leftmost position that can be incremented. If does not exist, position will be created. This right-padding will occur even to right of <position>, unless passed the -t flag." # Get flags. local flag_remove_leading_zeros=0 local flag_drop_trailing_zeros=0 while [ "${1:0:1}" == "-" ]; do if [ "$1" == "--" ]; then shift; break elif [ "$1" == "-l" ]; then flag_remove_leading_zeros=1 elif [ "$1" == "-t" ]; then flag_drop_trailing_zeros=1 else echo -e "Invalid flag: ${1}/n$usage"; return 1; fi shift; done # Get arguments. if [ ${#@} -lt 1 ]; then echo "$usage"; return 1; fi local v="${1}" # version string local targetPos=${2-last} # target position local minPos=${3-${2-0}} # minimum position # Split version string into array using its periods. local IFSbak; IFSbak=IFS; IFS=''.'' # IFS restored at end of func to read -ra v <<< "$v" # avoid breaking other scripts. # Determine target position. if [ "${targetPos}" == "last" ]; then if [ "${minPos}" == "last" ]; then minPos=0; fi targetPos=$((${#v[@]}>${minPos}?${#v[@]}:$minPos)); fi if [[ ! ${targetPos} -gt 0 ]]; then echo -e "Invalid position: ''$targetPos''/n$usage"; return 1; fi (( targetPos-- )) || true # offset to match array index # Make sure minPosition exists. while [ ${#v[@]} -lt ${minPos} ]; do v+=("0"); done; # Increment target position. v[$targetPos]=`printf %0${#v[$targetPos]}d $((10#${v[$targetPos]}+1))`; # Remove leading zeros, if -l flag passed. if [ $flag_remove_leading_zeros == 1 ]; then for (( pos=0; $pos<${#v[@]}; pos++ )); do v[$pos]=$((${v[$pos]}*1)); done; fi # If targetPosition was not at end of array, reset following positions to # zero (or remove them if -t flag was passed). if [[ ${flag_drop_trailing_zeros} -eq "1" ]]; then for (( p=$((${#v[@]}-1)); $p>$targetPos; p-- )); do unset v[$p]; done else for (( p=$((${#v[@]}-1)); $p>$targetPos; p-- )); do v[$p]=0; done; fi echo "${v[*]}" IFS=IFSbak return 0 } # EXAMPLE -------------> # RESULT increment_version 1 # 2 increment_version 1 2 # 1.1 increment_version 1 3 # 1.0.1 increment_version 1.0.0 # 1.0.1 increment_version 1.2.3.9 # 1.2.3.10 increment_version 00.00.001 # 00.00.002 increment_version -l 00.001 # 0.2 increment_version 1.1.1.1 2 # 1.2.0.0 increment_version -t 1.1.1 2 # 1.2 increment_version v1.1.3 # v1.1.4 increment_version 1.2.9 2 4 # 1.3.0.0 increment_version -t 1.2.9 2 4 # 1.3 increment_version 1.2.9 last 4 # 1.2.9.1

Obviamente, esto es excesivo solo para incrementar una cadena de versión. Pero escribí esto porque necesitaba diferentes tipos de proyectos, y porque si la velocidad no es un problema, prefiero la reutilización en lugar de modificar el mismo código en docenas de scripts. Supongo que ese es mi lado orientado a objetos que se filtra en mis scripts.


Aquí hay una versión aún más corta que también admite un postfix (agradable para -SNAPSHOT)

$ cat versions 1.2.3.44 1.2.3.9 1.2.3 9 42.2-includes-postfix $ perl -pe ''s/^((/d+/.)*)(/d+)(.*)$/$1.($3+1).$4/e'' < versions 1.2.3.45 1.2.3.10 1.2.4 10 42.3-includes-postfix

Explicación

Usé regex para capturar 3 partes. Las cosas antes de la última posición, el número a incrementar y las cosas después.

  • ((/d+/.)*) - cosas del 1.1.1.1.1.
  • (/d+) - el último dígito
  • (.*) - las cosas después del último dígito

Luego uso la opción e para permitir expresiones en la parte de reemplazo. Tenga en cuenta que con la opción e / 1 se convierte en una variable $ 1 y debe concatenar las variables con el operador de puntos.

  • $1 - el grupo de captura de 1.1.1.1.1.
  • ($3+1) - incrementa el último dígito. nota $ 2 se usa en el subgrupo de $ 1 para obtener el 1 repetido.
  • $4 - las cosas después del último dígito

Cansado de bash? ¿Por qué no probar Perl?

$ cat versions 1.2.3.44 1.2.3.9 1.2.3 9 $ cat versions | perl -ne ''chomp; print join(".", splice(@{[split//./,$_]}, 0, -1), map {++$_} pop @{[split//./,$_]}), "/n";'' 1.2.3.45 1.2.3.10 1.2.4 10

Por supuesto, no cumple con el requisito.


La determinación de un número de versión para un proyecto de software se basa en su cambio relativo / funcionalidad / etapa de desarrollo / revisión. Los incrementos consecuentes en la numeración de la versión y la revisión es idealmente un proceso que debe ser realizado por un humano. Sin embargo, para no adivinar su motivación para escribir este guión, esta es mi sugerencia.

Incluya algo de lógica en su script que haga exactamente lo que usted describe en su requerimiento

"... si la última sección después del punto tiene dos dígitos, inc hasta 99; si solo es 1, inc hasta 9 ..."

Suponiendo que la tercera posición es el número de etapa de desarrollo $dNum y la cuarta (última) posición es el número de revisión $rNum :

if [ $(expr length $rNum) = "2" ] ; then if [ $rNum -lt 99 ]; then rNum=$(($rNum + 1)) else rNum=0 dNum=$(($dNum + 1)) #some additional logic for $dNum > 9 also needed fi elif [ $(expr length $dNum) = "1" ] ; then ... ... fi

Tal vez una función permita la forma más sucinta de manejar todas las posiciones (majNum.minNum.dNum.rNum).

Tendrá que separar el nombre del proyecto y los componentes del número de versión de su nombre de archivo en su script y luego construir el número de versión con todas sus posiciones, y finalmente reconstruir el nombre de archivo con algo como

new_version="$majNum.minNum.$dNum.$rNum" new_version_file="$old_file.$new_version"

Espero que ayude y verifique esta discusión de SO así como esta entrada de wikipedia si desea saber más acerca de las convenciones de versiones.


Pure Bash:

increment_version () { declare -a part=( ${1///./ } ) declare new declare -i carry=1 for (( CNTR=${#part[@]}-1; CNTR>=0; CNTR-=1 )); do len=${#part[CNTR]} new=$((part[CNTR]+carry)) [ ${#new} -gt $len ] && carry=1 || carry=0 [ $CNTR -gt 0 ] && part[CNTR]=${new: -len} || part[CNTR]=${new} done new="${part[*]}" echo -e "${new// /.}" } version=''1.2.3.44'' increment_version $version

resultado:

1.2.3.45

La cadena de versión se divide y se almacena en la parte de la matriz. El bucle va desde la última hasta la primera parte de la versión. La última parte se incrementará y posiblemente se reducirá a su longitud original. Se lleva un carry a la siguiente parte.


Usando sólo bash, wc y sed:

#! /bin/bash for v in 1.2.3.44 1.2.3.9 1.2.3 9 1.4.29.9 9.99.9 ; do echo -n $v ''-> '' num=${v//./} let num++ re=${v//./)(} re=${re//[0-9]/.}'')'' re=${re#*)} count=${v//[0-9]/} count=$(wc -c<<<$count) out='''' for ((i=count-1;i>0;i--)) ; do out=''./'$i$out done sed -r s/$re$/$out/ <<<$num done


$ echo 1.2.3.4 | awk -F. -v OFS=. ''NF==1{print ++$NF}; NF>1{if(length($NF+1)>length($NF))$(NF-1)++; $NF=sprintf("%0*d", length($NF), ($NF+1)%(10^length($NF))); print}'' 1.2.3.5

1.2.3.9 => 1.2.4.0 1.2.3.44 => 1.2.3.45 1.2.3.99 => 1.2.4.00 1.2.3.999=> 1.2.4.000 1.2.9 => 1.3.0 999 => 1000

ACTUALIZAR:

#!/usr/bin/gawk -f BEGIN{ v[1] = "1.2.3.4" v[2] = "1.2.3.44" v[3] = "1.2.3.99" v[4] = "1.2.3" v[5] = "9" v[6] = "9.9.9.9" v[7] = "99.99.99.99" v[8] = "99.0.99.99" v[9] = "" for(i in v) printf("#%d: %s => %s/n", i, v[i], inc(v[i])) | "sort | column -t" } function inc(s, a, len1, len2, len3, head, tail) { split(s, a, ".") len1 = length(a) if(len1==0) return -1 else if(len1==1) return s+1 len2 = length(a[len1]) len3 = length(a[len1]+1) head = join(a, 1, len1-1) tail = sprintf("%0*d", len2, (a[len1]+1)%(10^len2)) if(len2==len3) return head "." tail else return inc(head) "." tail } function join(a, x, y, s) { for(i=x; i<y; i++) s = s a[i] "." return s a[y] }

$ chmod +x inc.awk $ ./inc.awk #1: 1.2.3.4 => 1.2.3.5 #2: 1.2.3.44 => 1.2.3.45 #3: 1.2.3.99 => 1.2.4.00 #4: 1.2.3 => 1.2.4 #5: 9 => 10 #6: 9.9.9.9 => 10.0.0.0 #7: 99.99.99.99 => 100.00.00.00 #8: 99.0.99.99 => 99.1.00.00 #9: => -1