Bash string reemplaza múltiples caracteres con uno

8

Estoy reemplazando, desde un título de fuente, todos los caracteres, excepto letras y dígitos, con un guión para usar el resultado como un nombre de archivo seguro para cualquier sistema de archivos:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ echo ${t//[^A-Za-z0-9]/-}
Episodie-06--No-hope-of-riding-home--NEW----Advanced-grammar

Sin embargo, me gustaría condensar todos los guiones repetidos con uno solo como Episodie-06-No-hope-of-riding-home-NEW-Advanced-grammar

Descubrí que puedo lograrlo usando una sustitución de dos pases:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ tmp=${t//[^A-Za-z0-9]/-}
$ echo ${tmp//--/-}
Episodie-06-No-hope-of-riding-home-NEW--Advanced-grammar

Pensé que podría hacerlo en una sola pasada como:

$ echo ${t//[^A-Za-z0-9]+/-}

Pero no funciona.

¿Cualquier pista?

Nota: no quiero ir con sedu otras herramientas

neurino
fuente

Respuestas:

8

Necesita algo más poderoso que los comodines de shell tradicionales. En bash, configure la extglobopción, que le da acceso a expresiones regulares en patrones globales a través de una sintaxis inusual heredada de ksh.

shopt -s extglob
sanitized=${raw//+([^A-Za-z0-9])/-}
Gilles 'SO- deja de ser malvado'
fuente
Gracias, hubo un comentario de fered en respuesta jw013 con esta solución. ¿Alguna información sobre compatibilidad con otros shells de esta sintaxis? No me preocupa tanto, solo para saber más shopty qué shells lo respaldan.
neurino 01 de
@neurino shoptes específico de bash. La sintaxis del patrón que habilita siempre está disponible en todas las variantes de ksh. En zsh, esta sintaxis debe habilitarse con setopt ksh_glob. POSIX no tiene esa característica, sus comodines son menos potentes que las expresiones regulares. Los shells que no sean bash / ksh / zsh, que en la práctica en su mayoría significa ceniza hoy en día, tienden a adherirse a los comodines POSIX.
Gilles 'SO- deja de ser malvado'
así, en este punto prefiero más la compatibilidad y flexibilidad con un poco más sobrecarga: echo "$t" | sed -r 's/[^[:alnum:]]+/-/g; s/^-|-$//'. Acepto su respuesta, ya que hace exactamente lo que se preguntó.
neurino 01 de
@neurino Si desea la portabilidad a otros proyectiles, puede ir con la respuesta de Glenn Jackman . Por cierto, tenga en cuenta que la ${var/PATTERN/REPLACEMENT}construcción también es específica de ksh / bash / zsh.
Gilles 'SO- deja de ser malvado'
Prefiero, sedya que conozco mejor su sintaxis y comportamiento, puedo agregar fácilmente una declaración para eliminar guiones iniciales / finales, no necesito preocuparme por \nchar. ¿Es sedmucho menos disponible que tr?
neurino 01 de
7

tr es una buena herramienta para este trabajo

new=$( printf "%s" "$t" | tr -cs 'a-zA-Z0-9' '-' )
new=${new#-}; new=${new%-}
Glenn Jackman
fuente
Gracias, +1, nunca recuerdo sobre tr... Sin embargo, estaba tratando de hacerlo en Bash, de lo contrario iría con sed:echo "$t" | sed -r 's/[^A-Za-z0-9]+/-/g'
neurino
Abajo votó porque entra en conflicto conNote: I don't want to go with sed or other tools
Paul Calabro
3

Si quieres quedarte con puro bash, tendrás que conformarte con la solución de dos pasos. Las sustituciones de cadena bash usan globos , como en la expansión del nombre de ruta, y no expresiones regulares. Los únicos caracteres especiales en globos son *, ?y [], cuya áspera equivalentes en las expresiones regulares son .*, .y []. Eche un vistazo a la wiki de Wooledge y las secciones de la página de manual en y para obtener más información.bash(1)Parameter ExpansionPathname Expansion

Como comentario, es probable que una expansión de dos pasos en bash puro sea más rápida que intentar hacer lo mismo invocando un programa externo, por lo que no me preocuparía demasiado.

jw013
fuente
Gracias, revisaré el enlace. Mi preocupación es que tengo que hacer este trabajo más de una vez en todo el script, por lo que mi única preocupación era que el mismo código se repitiera una y otra vez y comprometiera la legibilidad. De todos modos, voy a encontrar una solución educada que voy a publicar. Saludos
neurino
Podría poner ese código en una función para evitar repetir el código.
jw013
Es lo que estoy haciendo, pero, como saben, las funciones bash no pueden devolver cadenas ... o, al menos, fue lo que pensé antes de hace 10 minutos :)
neurino
44
Aquí hay algunos ejemplos con do-sy-don't-s - Bash Extended Globbing .. Para el ejemplo anterior, sería:shopt -s extglob; t="${t//+([^A-Za-z0-9])/-}"
Peter.O
1
@fered: gracias, muy interesante, lo comprobaré. Su URL de enlace tiene un carácter adicional y devuelve un 404, el que funciona es Bash Extended Globbing
neurino