2010-12-03 24 views
17

Solo quiero obtener el número de un archivo que puede o no estar descomprimido. Sin embargo, parece que una expresión regular en sed no es compatible con ?. Esto es lo que he intentado:Usando? con sed

echo 'file_1.gz'|sed -n 's/.*_\(.*\)\(\.gz\)?/\1/p'

y nada fue devuelto. Luego añade un ? a la cadena que se analizaron:

echo 'file_1.gz?'|sed -n 's/.*_\(.*\)\(\.gz\)?/\1/p'

y tiene:

1

Por lo tanto, parece que el ? utilizado en la mayoría de expresiones regulares de no es compatible con sed, a la derecha ? Bueno, entonces, me gustaría que sed dé un 1 para file_1 y file_1.gz. ¿Cuál es la mejor manera de hacer eso en un script bash si el tiempo de ejecución es crítico?

Respuesta

28

El equivalente a x? es \(x\|\).

Sin embargo, muchas versiones de sed admiten una opción para habilitar "expresiones regulares extendidas" que incluye ?. En GNU sed, la bandera es -r. Tenga en cuenta que esto también cambia los paréntesis no guardados para hacer la agrupación. por ejemplo:

echo 'file_1.gz'|sed -n -r 's/.*_(.*)(\.gz)?/\1/p' 

En realidad, hay otro error en su expresión regular, que es que los codiciosos .* en los parens va a tragarse al ".gz" si es que existe. sed no tiene un equivalente no codicioso a * hasta donde yo sé, pero puede usar | para solucionar esto. | en sed (y muchas otras implementaciones de expresiones regulares) utilizará el partido más a la izquierda que funciona, por lo que puede hacer algo como esto:

echo 'file_1.gz'|sed -r 's/(.*_(.*)\.gz)|(.*_(.*))/\2\4/' 

Esto intenta hacer coincidir con .gz, y sólo intenta sin ella si Eso no t trabajo. Solo uno de los grupos 2 o 4 en realidad existirá (ya que están en lados opuestos del mismo |) por lo que solo los concatenamos para obtener el valor que queremos.

+0

Great answer. De hecho, no lo usé porque mi caso particular tenía un atajo. Sin embargo, gracias por mencionar la codicia de mi '. *' ..que en realidad arregló todo. – User1

+0

FWIW, en OS X (y posiblemente otros BSD), la bandera es ''E'. '-r' es inexistente. –

1
echo 'file_1.gz'|sed -n 's/.*_\(.*\)\?\(\.gz\)/\1/p' 

Trabajos. Debes poner la devolución en el lugar correcto y debes escapar.

+0

Pero 'echo 'file_1' | sed -n 's /.*_ \ (. * \) \? \ (\. Gz \)/\ 1/p'' no funciona. ¿Hubo un error tipográfico? – User1

+0

Funcionó para mí en mi caja –

0

que puedes usar awk que es superior a sed cuando se trata de desplegar el acaparamiento/análisis:

$ awk -F'[._]' '{print $2}' <<<"file_1" 
1 
$ awk -F'[._]' '{print $2}' <<<"file_1.gz" 
1 

Alternativamente, puedes usar la expansión de parámetros de Bash de este modo:

var=file_1.gz; 
temp=${var#*_}; 
file=${temp%.*} 
echo $file 

Nota : funciona cuando var=file_1 también

0

Una función que debe devolver un número que sigue el '_' en un nombre de archivo, independientemente de la extensión de archivo:

realname() { 
    local n=${$1##*/} 
    local rn="${n%.*}" 
    sed 's/^.*\_//g' ${$rn:-$n} 
} 
0

Parte de la solución reside en escapar del signo de interrogación o usando la opción -r.

sed 's/.*_\([^.]*\)\(\.\?[^.]\+\)\?$/\1/' 

o

sed -r 's/.*_([^.]*)(\.?[^.]+)?$/\1/' 

trabajará para:

file_1.gz 
file_12.txt 
file_123 

lo que resulta en:

1 
12 
123 
0

simplemente me di cuenta de que podía hacer algo muy sencillo:

echo 'file_1.gz'|sed -n 's/.*_\([0-9]*\).*/\1/p'

Aviso del [0-9]* en lugar de un .*. La respuesta de @Laurence Gonsalves me hizo darme cuenta de la codicia de mi publicación anterior.

4

Si usted está buscando una respuesta al ejemplo específico dado en la pregunta, o por qué se utiliza el ? incorrectamente (independientemente de la sintaxis), ver the answer by Laurence Gonsalves.

Si está buscando lugar para la respuesta a la pregunta general de por qué ? no exhibe su significado especial en la SED como era de esperar:

Por defecto, sed utiliza la sintaxis de las expresiones "POSIX básica regular ", por lo que el signo de interrogación se debe escapar como \? para aplicar su significado especial; de lo contrario, coincide con un signo de interrogación literal. Como alternativa, puede usar la opción -r o --regexp-extended para usar la "sintaxis de expresión regular extendida", que invierte el significado de caracteres especiales escapados y no escabrosos, incluido ?.

En palabras de la documentación sed de GNU (vista mediante la ejecución de 'información sed' en Linux):

La única diferencia entre las expresiones regulares básicas y extendidas está en el comportamiento de unos pocos caracteres: ' ? ',' + ', paréntesis y llaves (' {} '). Mientras que las expresiones regulares básicas requieren que se escapen si desea que se comporten como caracteres especiales, al usar expresiones regulares extendidas, debe escapar de ellas si quiere que coincida con un carácter literal.

y la opción se explica:

-r --regexp-extended

uso extendido de expresiones regulares en lugar de regulares básicas expresiones. Las expresiones regulares extendidas son aquellas que 'egrep' acepta; pueden ser más claros porque generalmente tienen menos barras invertidas, pero son una extensión de GNU y, por lo tanto, los scripts que los usan no son portátiles.

actualización

las nuevas versiones de GNU SED ahora dicen esto:

-E -r --regexp-extended

uso extendido de expresiones regulares en lugar de regulares básicas expresiones. Las expresiones regulares extendidas son aquellas que 'egrep' acepta; ellos pueden ser más claros porque generalmente tienen menos barras invertidas. Históricamente esta era una extensión de GNU, pero la extensión '-E' tiene desde que se agregó al estándar POSIX (http://austingroupbugs.net/view.php?id=528), por lo tanto, utilice '-E' para la portabilidad . GNU sed aceptó '-E' como opción no documentada durante años, y * BSD seds también han aceptado '-E' durante años, pero las secuencias de comandos que usan '-E' podrían no transferirse a otros sistemas anteriores.

Por lo tanto, si necesita conservar la compatibilidad con sed antiguo de GNU, quédese con -r. Pero si prefiere una mejor portabilidad multiplataforma en sistemas más modernos (por ejemplo, soporte Linux + Mac), vaya con -E (pero tenga en cuenta que todavía hay algunos caprichos y diferencias entre GNU sed y BSD sed, por lo que deberá asegurarse sus guiones son portátiles en cualquier caso).