2012-06-20 5 views
7

que tienen una variable fiesta llena con un nombre de archivo y la ruta:¿Es posible aplicar dos transformaciones de patrón coincidentes en una operación?

SONG="~/Music/Mine/Cool Title Bro.flac" 

En mis intentos de hacer el etiquetado drásticamente más fácil, he aplicado un poco de transformación de la variable para aislar el título:

echo "${SONG#\~/Music/Mine/}" # which prints: Cool Title Bro.flac 

Sé que también es posible eliminar el sufijo con ${SONG%%.flac}.

¿Pero es posible eliminar tanto el prefijo como el sufijo en una sola operación?

Este:

${SONG#\~/Music/Mine/%%.flac} 

no funciona presumiblemente debido a que intenta hacer coincidir un literal %%.flac como parte del prefijo. La inversa no funciona (%%.flac#~/[...]), e incluso me he vuelto loco y trató

${${SONG#~/Music/Mine/}%%.flac} 

que también no funciona.

Esto puede ser un excelente ejemplo de sobre-ingeniería de mi parte, pero sería excelente si hay una manera de hacerlo y aún no lo he descubierto.

+2

He echado un vistazo muy rápido a http://tldp.org/LDP/abs/html/parameter-substitution.html y creo que es posible que deba hacer esto en dos pasos separados utilizando los operadores de sustitución variable de bash. También tenga en cuenta que tuve que escapar de las barras diagonales en echo "$ {SONG # \ ~/Music \/Mine \ /" para que funcionase el reemplazo de la interfaz. Usé GNU bash, versión 4.1.10 (4) -lanzamiento (i686-pc-cygwin) –

+0

@RobKielty: solo tuve que escapar de la tilde. Tenga en cuenta que no está escapando del primer corte. –

+0

@DennisWilliamson Está en lo correcto, luego de una inspección adicional solo tuve que escapar del ~ –

Respuesta

3
echo "1:" 
if [[ "$SONG" =~ \~/Music/Mine/(.*)\.flac ]] ; then SONG=${BASH_REMATCH[1]} ; fi 
echo $SONG 

echo "2:" 
[[ "$SONG" =~ \~/Music/Mine/(.*)\.flac ]] && SONG=${BASH_REMATCH[1]} 
echo $SONG 

1 y 2 uso de bash expresiones regulares. El primer ejemplo tiene la ventaja adicional de poder entrar en una rama else si la cadena no coincide con el formato esperado. * El segundo ejemplo es un poco más limpio. En ambos casos, si ${SONG} no coincide con el patrón, no se modifica.

Pero usar awk o sed podría ser más fácil de entender. Por ejemplo:

echo "3:" 
SONG=$(echo "$SONG" | sed -r 's:~/Music/Mine/(.*)\.flac:\1:') 
echo $SONG 

[*] Nota Sede de DennisWilliamson continuación en relación con el uso de || para obtener una rama else.

+0

Solo necesita escaparse. '[[$ song = ~ \ ~/Music/Mine /(.*). flac]]' – ormaaj

+2

+1 Puedes hacer 'else' con la segunda versión agregando' || foo' al final. Aunque el OP quiere una sola operación, generalmente es mejor poner la expresión regular en una variable. Además, el punto debe escaparse para que sea literal '\ .flac'. Las llaves alrededor de 'SONG' son superfluas aquí. Siempre cite las variables cuando se expanden: 'echo" $ SONG ". –

+1

+1 para los primeros dos ejemplos.Creo que siempre es bueno explorar completamente las capacidades de bash y evitar, cuando sea posible, recurrir a bifurcaciones y ejecutar un subshell, luego bifurcando y ejecutando echo, creando un pipe y luego bifurcando y ejecutando sed y devolviendo el resultado al padre proceso de bash –

2

No, no es posible. Usa dos operaciones.

tmp="${SONG#\~/Music/Mine/}"; echo "${tmp%.flac}" 

Bien, es posible, si estás loco.

a="~/Music/Mine/Cool Title Bro.flac" 
echo "${a:b=$(b=${a%${a#\~/Music/Mine/}};echo ${#b}):$(c=${a%.flac};echo ${#c})-b}" 

También podría usar la agrupación regex en un shell que la admita. Ejemplo en jedwards answer

También se puede hacer utilizando la agrupación de patrones ksh93.

song='~/Music/Mine/Cool Title Bro.flac'; echo "${song/'~/Music/Mine/'+(*).flac/\1}" 
+0

"Considerado overkill"? Creo que las expresiones regulares serían el método preferido sobre su segundo ejemplo al menos. Además, con la expresión regular, puede detectar y detectar el caso en el que la cadena *** no coincide *** con lo que espera. No puedes hacer eso con ninguno de tus ejemplos. – jedwards

+0

@jedwards Regex está bien. Es una gran sintaxis torpe para hacer una transformación simple. – ormaaj

-1

si debería hacerlo en una sola línea, y no me importa la legibilidad voy a utilizar

echo "${SONG:13:${#SONG} - 18}" 

donde

  • 13 es la longitud de la cadena "~/música/mina/"
  • 18 es la longitud de la cadena "~/música/mina /" más la longitud de" .flac"

Una aplicación más fácil de esta manipulación de cadenas en bash será:

$ TEST="1234567890" 
$ echo ${TEST:3} 
4567890 
$ echo ${TEST:3:4} 
4567 
+0

No conocemos la longitud de la subcadena por lo que este método no se puede usar. –

+0

No necesita saber la longitud de la subcadena, solo la longitud de la extensión que desea eliminar y la longitud de la ruta. Edito mi publicación ya que hay un error en ella. De todos modos, la longitud puede obtenerse dinámicamente con el comando wc –

+2

No es necesario usar 'wc':' echo "$ {SONG: 13: $ {# SONG} - 18}" ' –

2

En algunos casos, los patrones extendidos bash 's son lo suficientemente flexibles como para lograr lo que desea. En primer lugar, usted tiene que encenderlos:

shopt -s extglob 

A continuación, puede especificar una lista de patrones que debe ser eliminado mediante el parámetro de expansión:

echo ${SONG//@(*\/|.*)} 

El patrón extendido @(*\/|.*) partidos ya sea todo lo que hasta a a/(que debe ser escapado, para evitar confundirlo con parte de la sintaxis de sustitución de parámetros), o un punto y todo lo que le sigue. El // indica que cada aparición del patrón debe ser sustituido.

0

Espero que no hayas malinterpretado tu pregunta. ¿Quieres aislar "Cool Title Bro"? Si es así, entonces

$ echo "~/Music/Mine/Cool Title Bro.flac" 
~/Music/Mine/Cool Title Bro.flac 
$ echo "~/Music/Mine/Cool Title Bro.flac"|sed 's/.*\/\(.*\).flac/\1/' 
Cool Title Bro 

Nada que un poco de expresiones regulares no puede manejar. O, alternativamente,

$ echo "~/Music/Mine/Cool Title Bro.flac"|sed 's/.*\/\(.*\)\..*/\1/' 
Cool Title Bro 

para trabajar con cualquier extensión de archivo.

Cuestiones relacionadas