bash - comando - manejo de cadenas en shell script
¿Cómo encontrar/reemplazar e incrementar un número coincidente con sed/awk? (4)
Creo que encontrar archivos no es la parte difícil para ti. Por lo tanto, voy al grano a hacer el cálculo de +1. Si tiene gnu sed , podría hacerse de esta manera:
sed -r ''s/(.*)(/?cache_version=)([0-9]+)(.*)/echo "/1/2$((/3+1))/4"/ge'' file
Tomemos un ejemplo:
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
podrías agregar la opción -i si quieres.
editar
/e
permite pasar parte coincidente a comando externo, y hacer la sustitución con el resultado de la ejecución. Gnu sed solo.
vea este ejemplo: se utilizan comandos externos / herramienta echo
, bc
kent$ echo "result:3*3"|sed -r ''s/(result:)(.*)/echo /1$(echo "/2"/|bc)/ge''
da salida:
result:9
podrías usar otro comando externo poderoso, como cortar, sed (otra vez), awk ...
Directamente al grano, me pregunto cómo usar grep / find / sed / awk para hacer coincidir una cierta cadena (que termina con un número) e incrementar ese número en 1. Lo más cercano que he venido es concatenar un 1 a el final (que funciona bastante bien) porque el punto principal es simplemente cambiar el valor. Esto es lo que estoy haciendo actualmente:
find . -type f | xargs sed -i ''s//(/?cache_version/=[0-9]/+/)//11/g''
Como no pude encontrar la forma de incrementar el número, lo tomé todo y agregué un "1". Antes, tenía algo como esto:
find . -type f | xargs sed -i ''s//?cache_version/=/([0-9]/+/)/?cache_version=/11/g''
Entonces, al menos, entiendo cómo capturar lo que necesito.
En lugar de explicar para qué sirve esto, solo explicaré lo que quiero que haga. Debería encontrar texto en cualquier archivo, de forma recursiva, basado en el directorio actual (no es importante, podría ser cualquier directorio, así que lo configuraría más adelante), que coincide con "? Cache_version =" con un número. Luego incrementará ese número y lo reemplazará en el archivo.
Actualmente las cosas que tengo arriba funcionan, es solo que no puedo incrementar ese número encontrado al final. Sería mejor poder incrementar en lugar de agregar un "1" para que los valores futuros no sean "11", "111", "1111", "11111", y así sucesivamente.
He leído docenas de artículos / explicaciones, y con frecuencia la sugerencia es utilizar awk
, pero no puedo mezclarlos. Lo más cerca que llegué a usar awk
, que en realidad no reemplaza nada, es:
grep -Pro ''(?<=/?cache_version=)[0-9]+'' . | awk -F: ''{ print "match is", $2+1 }''
Me pregunto si hay alguna manera de canalizar un sed
al final y pasar el nombre del archivo original para que sed
pueda tener el nombre del archivo y el número incrementado (del awk
), o lo que sea que necesite xargs
.
Técnicamente, este número no tiene importancia; este reemplazo es principalmente para asegurarse de que haya un nuevo número allí, 100% seguro diferente al último. Así que mientras escribía esta pregunta, me di cuenta de que también podría usar el sistema de segundos de tiempo (la técnica utilizada a menudo por AJAX para eliminar el almacenamiento en caché de solicitudes "idénticas" posteriores). Terminé con esto, y parece perfecto:
CXREPLACETIME=`date +%s`; find . -type f | xargs sed -i "s//(/?cache_version/=/)[0-9]/+//1$CXREPLACETIME/g"
(Guardo el valor primero para que todos los archivos obtengan el mismo valor, en caso de que abarque varios segundos por el motivo que sea)
Pero aún me encantaría saber la pregunta original, al aumentar un número coincidente. Supongo que una solución fácil sería convertirlo en un script de bash, pero aún así, pensé que habría una manera más fácil de recorrer cada archivo de forma recursiva y verificar su contenido para una coincidencia que luego reemplazara, ya que simplemente está incrementando un número coincidente ... no mucho más lógica. Simplemente no quiero escribir en ningún otro archivo o algo así, debería hacerlo en su lugar, como lo hace sed
con la opción "i".
Este comando perl
buscará todos los archivos en el directorio actual (sin recorrerlo, necesitará el módulo File::Find
o similar para esa tarea más compleja) e incrementará el número de una línea que coincida con cache_version=
. Utiliza el indicador /e
de la expresión regular que evalúa la parte de reemplazo.
perl -i.bak -lpe ''BEGIN { sub inc { my ($num) = @_; ++$num } } s/(cache_version=)(/d+)/$1 . (inc($2))/eg'' *
Lo probé con un file
en el directorio actual con los siguientes datos:
hello
cache_version=3
bye
Copia de seguridad del archivo original ( ls -1
):
file
file.bak
Y file
ahora con:
hello
cache_version=4
bye
Espero que pueda ser útil para lo que estás buscando.
ACTUALIZAR para usar File::Find
para recorrer directorios. Acepta *
como argumento, pero los descartará con los encontrados con File::Find
. El directorio para comenzar la búsqueda es la corriente de ejecución del script. Está codificado en la línea 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
'' *
Esto es feo (estoy un poco oxidado), pero aquí empieza a usar 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))
Con un nombre de archivo original ( $orig
) de "algo1", sed divide el texto y las porciones numéricas en $text
y $num
, luego se combinan en la sección final con un número incrementado, lo que da como resultado something2
.
Solo un comienzo, ya que no considera casos con números dentro del nombre de archivo o nombres sin número al final, pero es de esperar que ayude con su objetivo original de usar sed.
Esto realmente se puede simplificar dentro de sed mediante el uso de búferes, creo (sed puede operar recursivamente), pero estoy realmente oxidado con ese aspecto.
Versión Pure sed :
Esta versión no tiene dependencias en otros comandos o variables de entorno. Utiliza portación explícita. Para portar, uso el símbolo @, pero se puede usar otro nombre si lo desea. Use algo que no está presente en su archivo de entrada. Primero encuentra SEARCHSTRING<number>
y le agrega un @. Repite los dígitos incrementales que tienen un acarreo pendiente (es decir, tienen un símbolo de acarreo después de él: [0-9]@
) Si se incrementó 9, este incremento arroja un acarreo en sí mismo, y el proceso se repetirá hasta que no haya más pendientes lleva. Finalmente, los acarreos que se cedieron pero que no se agregaron a un dígito se reemplazaron por 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