Estoy tratando de eliminar la extensión de nombre de archivo de una lista de archivos que «he generado con lo siguiente script bash:
#!/bin/bash file_list=$(find . -type f) #assuming that the files are stored in the same directory trimmed_file_list=$(printf "%s\n" "${file_list[@]%.*}") printf "%s\n" "${trimmed_file_list[@]}"
Esta expansión recorta la extensión de la última entrada de la lista, pero no de las anteriores.
Por ejemplo, quiero la siguiente lista:
file1.pdf file2.pdf file3.png
Para convertirme en
file1 file2 file3
Pero en su lugar obtengo:
file1.pdf file2.pdf file3
Preferiría no hacer esto en un bucle, me gustaría usar la expansión de parámetros. Quiero evitar el corte porque solo quiero eliminar la última extensión del archivo.
Hay una gran cantidad de temas sobre la expansión de parámetros y parece que bash se está ahogando en find
» El uso de nuevas líneas … Realmente no estoy seguro. Si otro de los temas preexistentes explica este problema, no entiendo completamente lo que está pasando.
Temas que parecen relacionado pero no parece resolver mi problema:
- ¿Cómo puedo eliminar la extensión de un nombre de archivo en un script de shell?
- Expansión de parámetros de shell en matrices
- Eliminación de nombre de archivo extensiones con find -exec
Comentarios
- Su título menciona matrices, pero no hay una matriz en su código.
- @Kusalananda ¡Gracias! ‘ me he dado cuenta de eso desde que pregunté. Aún estoy aprendiendo 🙂
- @Kusalananda Yo ‘ he seguido adelante y modifiqué la pregunta para que coincida más con el problema. Eso debería evitar que las búsquedas de Google erróneas lleguen a la página. ¡Gracias por la información!
Responder
Podrías hacer todo con un solo comando.
find /path/to -type f -execdir bash -c "printf "%s\n" "${@%.*}"" bash {} +
Comentarios
- ¡Gracias! De hecho, necesito crear un par de copias de la matriz, una para un menú que tiene el nombre de archivo modificado y otra que retendrá el valor del índice original para poder llamar al archivo desde el menú. ¡Sin embargo, es realmente asombroso cuánto puede hacer bash! ¡La capacidad de integrar todo en un comando todavía me sorprende! : D
Respuesta
Creo que le gustaría hacer una matriz en lugar de una cadena:
IFS=$"\n" # split on newline only set -o noglob # disable the glob part file_list=($(find . -name "*.*" -type f))
O con bash 4.4+, sin romper en las rutas de archivo con caracteres de nueva línea:
readarray -td "" file_list < <(find . -name "*.*" -type f -print0)
Entonces la expansión de su parámetro debería funcionar, aunque aquí tendría más sentido usar una variable de matriz nuevamente:
trimmed_file_list=("${file_list[@]%.*}")
En su ejemplo de código, está creando una cadena luego pidiendo a la expansión de parámetros que elimine todo después del último carácter de punto en la cadena completa.
Comentarios
- ¡Gracias! Lo probaré tan pronto como llegue a mi escritorio por la mañana.
- Esto supone que ningún nombre de archivo en ese directorio contiene espacios en blanco o caracteres globales.
- @Wildcard, debería ser abordado por la última edición.
- @Wildcard ¡Gracias por el puntero! Estoy ‘ generando los archivos que se colocan en este directorio mediante programación, por lo que no debería ‘ ser un problema.
Respuesta
Su código no incluye ningún arreglo. Además, pone una lista de nombres de archivo en una cadena, $file_list
. El contenido de la cadena terminará con file3.png
y la sustitución de parámetros elimina .png
de la cadena, dejándolo con una sola cadena de nombres de archivo donde el último nombre de archivo no tiene un sufijo de nombre de archivo.
Poner varios objetos separados (nombres de ruta) en una sola cadena descalifica automáticamente que el script funcione correctamente para archivos cuyos nombres contienen espacios (o cualquier delimitador que use la cadena). El uso de una matriz no ayudaría ya que aún dividiría la salida de find
en espacios en blanco.
Para recortar la extensión de todos los nombres de archivo de archivos normales en o debajo del directorio actual:
find . -type f -name "*.*" -exec sh -c " for pathname do printf "Would move %s to %s...\n" "$pathname" "${pathname%.*}" # mv -i "$pathname" "${pathname%.*}" done" sh {} +
Esto busca archivos regulares cuyos nombres contienen al menos un punto. Los nombres de ruta de estos archivos se introducen en lotes en un pequeño script de shell que los recorre. El script de shell cambia el nombre de los archivos eliminando el último punto del nombre del archivo y todo lo que viene después. El comando mv
real está comentado por seguridad.
El comando find
actúa como un generador de nombres de ruta para el script de shell interno, y estamos garantizados para procesar correctamente los nombres de ruta que contienen espacios, nuevas líneas y pestañas. No es necesario almacenar la salida de los comandos en variables.
Si tiene una matriz de nombres de ruta, posiblemente creada mediante
shopt -s globstar nullglob file_list=( **/*.* ) # get pathnames of files with dots in their names
Entonces podrá generar los nombres de ruta sin sufijos con
printf "%s\n" "${file_list[@]%.*}"
No sé si esto podría ayudarlo. Si desea usar los nombres de ruta sin sufijos para algo , luego usar la salida del comando printf
anterior sería incorrecto hacer (volvería a no manejar un nombre de ruta extraño nuevamente). y la forma de eliminar los sufijos del nombre de archivo depende de lo que desee hacer con el resultado.
Relacionado:
- Comprender la opción -exec de `find`
- ¿Por qué el bucle sobre el resultado de find ‘ s es una mala práctica?
Comentarios
- ¡Gracias! ¡Tu dominio de bash es muy evidente! Yo ‘ todavía estoy raspando la superficie … Gracias por tomarse el tiempo para ayudar 🙂