string - filename - Extracto de subcadena en Bash
if string contains unix (20)
Aquí hay una solución de prefijo-sufijo (similar a la solución dada por JB y Darron) que coincide con el primer bloque de dígitos y no depende de los guiones bajos que lo rodean:
str=''someletters_12345_morele34ters.ext''
s1="${str#"${str%%[[:digit:]]*}"}" # strip off non-digit prefix from str
s2="${s1%%[^[:digit:]]*}" # strip off non-digit suffix from s1
echo "$s2" # 12345
Dado un nombre de archivo en la forma someletters_12345_moreleters.ext
, quiero extraer los 5 dígitos y colocarlos en una variable.
Entonces, para enfatizar el punto, tengo un nombre de archivo con x número de caracteres, luego una secuencia de cinco dígitos rodeada por un solo guión bajo en cada lado y luego otro conjunto de x número de caracteres. Quiero tomar el número de 5 dígitos y ponerlo en una variable.
Estoy muy interesado en la cantidad de maneras diferentes en que esto se puede lograr.
Así es como lo haría:
FN=someletters_12345_moreleters.ext
[[ $FN =~ _([[:digit:]]{5})_ ]] && NUM=${BASH_REMATCH[1]}
Nota: lo anterior es una expresión regular y está restringido a su escenario específico de cinco dígitos rodeado de guiones bajos. Cambie la expresión regular si necesita una coincidencia diferente.
En caso de que alguien quiera información más rigurosa, también puede buscarla en man bash como este
$ man bash [press return key]
/substring [press return key]
[press "n" key]
[press "n" key]
[press "n" key]
[press "n" key]
Resultado:
${parameter:offset} ${parameter:offset:length} Substring Expansion. Expands to up to length characters of parameter starting at the character specified by offset. If length is omitted, expands to the substring of parameter start‐ ing at the character specified by offset. length and offset are arithmetic expressions (see ARITHMETIC EVALUATION below). If offset evaluates to a number less than zero, the value is used as an offset from the end of the value of parameter. Arithmetic expressions starting with a - must be separated by whitespace from the preceding : to be distinguished from the Use Default Values expansion. If length evaluates to a number less than zero, and parameter is not @ and not an indexed or associative array, it is interpreted as an offset from the end of the value of parameter rather than a number of characters, and the expan‐ sion is the characters between the two offsets. If parameter is @, the result is length positional parameters beginning at off‐ set. If parameter is an indexed array name subscripted by @ or *, the result is the length members of the array beginning with ${parameter[offset]}. A negative offset is taken relative to one greater than the maximum index of the specified array. Sub‐ string expansion applied to an associative array produces unde‐ fined results. Note that a negative offset must be separated from the colon by at least one space to avoid being confused with the :- expansion. Substring indexing is zero-based unless the positional parameters are used, in which case the indexing starts at 1 by default. If offset is 0, and the positional parameters are used, $0 is prefixed to the list.
Given test.txt es un archivo que contiene "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
cut -b19-20 test.txt > test1.txt # This will extract chars 19 & 20 "ST"
while read -r; do;
> x=$REPLY
> done < test1.txt
echo $x
ST
Me encanta la capacidad de sed
para tratar con grupos de expresiones regulares:
> var="someletters_12345_moreletters.ext"
> digits=$( echo $var | sed "s/.*_/([0-9]/+/).*//1/p" -n )
> echo $digits
12345
Una opción un poco más general sería no asumir que tiene un guión bajo _
marca el inicio de la secuencia de dígitos, por lo tanto, por ejemplo, eliminando todos los números que no aparecen antes de su secuencia: s/[^0-9]/+/([0-9]/+/).*//1/p
.
> man sed | grep s/regexp/replacement -A 2
s/regexp/replacement/
Attempt to match regexp against the pattern space. If successful, replace that portion matched with replacement. The replacement may contain the special character & to
refer to that portion of the pattern space which matched, and the special escapes /1 through /9 to refer to the corresponding matching sub-expressions in the regexp.
Más sobre esto, en caso de que no esté tan seguro con las expresiones regulares:
-
s
es para _s_ubstitute -
[0-9]+
coincide con 1+ dígitos -
/1
vincula al grupo n.1 de la salida de expresiones regulares (el grupo 0 es la coincidencia completa, el grupo 1 es la coincidencia entre paréntesis en este caso) -
p
flag es para _p_rinting
Todos los escapes están ahí para hacer que el procesamiento de expresiones regulares de sed
funcione.
Me sorprende que no haya surgido esta solución de bash pura:
a="someletters_12345_moreleters.ext"
IFS="_"
set $a
echo $2
# prints 12345
¡Probablemente quiera restablecer IFS a qué valor era antes, o unset IFS
después!
Mi respuesta tendrá más control sobre lo que quieres obtener de tu cadena. Aquí está el código sobre cómo puedes extraer 12345
de tu cadena
str="someletters_12345_moreleters.ext"
str=${str#*_}
str=${str%_more*}
echo $str
Esto será más eficiente si desea extraer algo que tenga caracteres como abc
o cualquier carácter especial como _
o -
. Por ejemplo: si su cadena es así y desea todo lo que está después de someletters_
y antes _moreleters.ext
:
str="someletters_123-45-24a&13b-1_moreleters.ext"
Con mi código puedes mencionar exactamente lo que quieres. Explicación:
#*
Se eliminará la cadena anterior, incluida la clave correspondiente. Aquí la clave que mencionamos es _
%
Se eliminará la siguiente cadena, incluida la clave correspondiente. Aquí la clave que mencionamos es ''_more *''
Haga algunos experimentos usted mismo y esto le parecería interesante.
Ok, aquí va la sustitución de parámetros pura con una cadena vacía. La advertencia es que he definido los caracteres y caracteres como solo caracteres. Si son alfanuméricos, esto no funcionará como está.
filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
12345
Si x es constante, la siguiente expansión de parámetros realiza la extracción de subcadenas:
b=${a:12:5}
donde 12 es el desplazamiento (basado en cero) y 5 es la longitud
Si los guiones bajos alrededor de los dígitos son los únicos en la entrada, puede eliminar el prefijo y el sufijo (respectivamente) en dos pasos:
tmp=${a#*_} # remove prefix ending in "_"
b=${tmp%_*} # remove suffix starting with "_"
Si hay otros guiones bajos, probablemente sea factible de todos modos, aunque sea más complicado. Si alguien sabe cómo realizar ambas expansiones en una sola expresión, me gustaría saber también.
Ambas soluciones presentadas son bash puras, sin proceso de desove involucrado, por lo tanto, muy rápido.
Si nos centramos en el concepto de:
"Una corrida de (uno o varios) dígitos"
Podríamos usar varias herramientas externas para extraer los números.
Podríamos borrar fácilmente todos los demás caracteres, ya sea sed o tr:
name=''someletters_12345_moreleters.ext''
echo $name | sed ''s/[^0-9]*//g'' # 12345
echo $name | tr -c -d 0-9 # 12345
Pero si $ nombre contiene varias ejecuciones de números, lo anterior fallará:
Si "nombre = someletters_12345_moreleters_323_end.ext", entonces:
echo $name | sed ''s/[^0-9]*//g'' # 12345323
echo $name | tr -c -d 0-9 # 12345323
Necesitamos usar expresiones regulares (expresiones regulares).
Para seleccionar solo la primera ejecución (12345 no 323) en sed y perl:
echo $name | sed ''s/[^0-9]*/([0-9]/{1,/}/).*$//1/''
perl -e ''my $name=''$name'';my ($num)=$name=~/(/d+)/;print "$num/n";''
Pero también podríamos hacerlo directamente en bash (1) :
regex=[^0-9]*([0-9]{1,}).*$; /
[[ $name =~ $regex ]] && echo ${BASH_REMATCH[1]}
Esto nos permite extraer la PRIMERA ejecución de dígitos de cualquier longitud
Rodeado por cualquier otro texto / caracteres.
Nota : regex=[^0-9]*([0-9]{5,5}).*$;
sólo coincidirá con las carreras de 5 dígitos. :-)
(1) : más rápido que llamar una herramienta externa para cada texto corto. No es más rápido que hacer todo el procesamiento dentro de sed o awk para archivos grandes.
Siguiendo los requisitos
Tengo un nombre de archivo con x número de caracteres, luego una secuencia de cinco dígitos rodeada por un solo guión bajo en cada lado y luego otro conjunto de x número de caracteres. Quiero tomar el número de 5 dígitos y ponerlo en una variable.
Encontré algunas maneras grep
que pueden ser útiles:
$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]+"
12345
o mejor
$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]{5}"
12345
Y luego con -Po
sintaxis:
$ echo "someletters_12345_moreleters.ext" | grep -Po ''(?<=_)/d+''
12345
O si quieres que se ajuste exactamente a 5 caracteres:
$ echo "someletters_12345_moreleters.ext" | grep -Po ''(?<=_)/d{5}''
12345
Finalmente, para que se almacene en una variable, solo es necesario utilizar la sintaxis var=$(command)
.
Sin ningún subproceso puede:
shopt -s extglob
front=${input%%_+([a-zA-Z]).*}
digits=${front##+([a-zA-Z])_}
Una variante muy pequeña de esto también funcionará en ksh93.
Sobre la base de la respuesta de jor (que no me funciona):
substring=$(expr "$filename" : ''.*_/([^_]*/)_.*'')
Solución genérica donde el número puede estar en cualquier parte del nombre de archivo, usando la primera de tales secuencias:
number=$(echo $filename | egrep -o ''[[:digit:]]{5}'' | head -n1)
Otra solución para extraer exactamente una parte de una variable:
number=${filename:offset:length}
Si su nombre de archivo siempre tiene el formato stuff_digits_...
puede usar awk:
number=$(echo $filename | awk -F _ ''{ print $2 }'')
Otra solución para eliminar todo excepto los dígitos, usar
number=$(echo $filename | tr -cd ''[[:digit:]]'')
También está el comando bash builtin ''expr'':
INPUT="someletters_12345_moreleters.ext"
SUBSTRING=`expr match "$INPUT" ''.*_/([[:digit:]]*/)_.*'' `
echo $SUBSTRING
Un poco tarde, pero me encontré con este problema y encontré lo siguiente:
host:/tmp$ asd=someletters_12345_moreleters.ext
host:/tmp$ echo `expr $asd : ''.*_/(.*/)_''`
12345
host:/tmp$
Lo usé para obtener una resolución de milisegundos en un sistema integrado que no tiene% N para la fecha:
set `grep "now at" /proc/timer_list`
nano=$3
fraction=`expr $nano : ''.*/(.../)......''`
$debug nano is $nano, fraction is $fraction
Una solución de bash:
IFS="_" read -r x digs x <<<''someletters_12345_moreleters.ext''
Esto golpeará una variable llamada x
. La var x
podría cambiarse a la var _
input=''someletters_12345_moreleters.ext''
IFS="_" read -r _ digs _ <<<"$input"
Usar cut :
echo ''someletters_12345_moreleters.ext'' | cut -d''_'' -f 2
Más genérico:
INPUT=''someletters_12345_moreleters.ext''
SUBSTRING=$(echo $INPUT| cut -d''_'' -f 2)
echo $SUBSTRING
similar a substr (''abcdefg'', 2-1, 3) en php:
echo ''abcdefg''|tail -c +2|head -c 3
solo trata de usar cut -c startIndx-stopIndx