Convertir sólo partes de un nombre de archivo a mayúsculas

Convertir sólo partes de un nombre de archivo a mayúsculas

En un programa de script de shell, necesito convertir los nombres de los archivos a mayúsculas si el nombre del archivo convertido aún no existe. En este caso particular, necesito cambiar solo el nombre base a mayúsculas y dejar la extensión (si la hay) como está.

Mi idea de resolver el problema es extraer primero el nombre base y la extensión por separado, convertir el nombre base a mayúsculas usando trel comando y luego verificar si el nombre base cambiado junto con la extensión existe en el directorio o no.

Si no existe, cambiaré el nombre del archivo original con el nombre base en mayúsculas usando mv. Ahora creo que esto se puede hacer de dos maneras: en primer lugar usando expry en segundo lugar usando cut( .espacio-período-espacio) como delimitador.

Si quiero usarlo exprpara extraer el nombre base (por ejemplo, del nombre del archivo, python1.pyo phonelist), entonces he escrito esto:

basefile=`expr "$filename" : '\(.*\)\.*.*' ` 

También he usado \.*para aquellos nombres de archivos que no tienen ninguna extensión porque \.* ignoraría cero o más apariciones de ., pero esta expresión para exprno funciona correctamente. Para cualquier nombre de archivo, devuelve el nombre de archivo completo tal como está.

¿Alguien puede explicar en qué me equivoco? También sugiera cómo puedo utilizar exprpara extraer solo la extensión del nombre del archivo.

Respuesta1

Si el shell es bash, usando solo la expansión de parámetros de bash:

file="aaa.bbb.dat"

name=${file%.*} # delete everything after last dot 
ext=${file##*.} # delete everything up to last dot
upcase=${name^^*}.$ext # uppercase everything

echo "$upcase"
AAA.BBB.dat

Probando con un caso más difícil:

file="déjà vu . dat "
name=${file%.*} # delete everything after last dot 
ext=${file##*.} # delete everything up to last dot
upcase=${name^^*}.$ext # uppercase everything
echo ":$upcase:"

Da:

:DÉJÀ VU . dat :

Entonces:

  • Las comillas dobles no son necesarias, hasta usar el resultado.
  • Las mayúsculas parecen estar bien incluso para caracteres que no son ASCII

Respuesta2

Cuando hay ambigüedad sobre hasta dónde se extiende un grupo, los motores de expresiones regulares favorecen primero la coincidencia más larga. Para cualquier nombre de archivo, \(.*\)coincide con el nombre completo y \.*.*coincide con la cadena vacía.

Necesitarás dos estuches: con o sin extensión. Tenga en cuenta también que si el nombre de un archivo comienza con ., ese no es el comienzo de una extensión.

No entiendo por qué quieres usar expr. La manipulación de los parámetros del Shell es más sencilla.

Al convertir a mayúsculas, tenga en cuenta que la trimplementación en Linux no admite configuraciones regionales que no sean ASCII. Sólo realiza manipulación de bytes. Por ejemplo, echo accentué | tr a-z A-Zresulta en ACCENTUé, no ACCENTUÉ. Utilice en su lugar una herramienta compatible con la configuración regional como awk. En bash, puedes usar ${filename^^?}, pero eso no está disponible en sh. Asegúrese de que su secuencia de comandos se esté ejecutando en la configuración regional correcta para la codificación de los nombres de los archivos.

Supongo que el nombre del archivo no contiene una parte de directorio. Si es así, sepárelo primero.

case $filename in
  ?*.*) # There is an extension
    base="${filename%.*}"; ext=".${filename##*.}";;
  *) # No extension
    base="$filename"; ext="";;
esac
upcased_base="$(printf %s. %base | awk '$0 = toupper($0)')"
upcased="${upcased_base%.}$ext"

El final .que %s.luego se elimina $upcased_basegarantiza que el script maneje correctamente los nombres de archivos con una nueva línea inmediatamente antes de la extensión. Sin esto, la sustitución del comando eliminaría las nuevas líneas finales. No necesita esto si ya se ha asegurado de que los nombres de sus archivos no contengan caracteres de nueva línea.

Respuesta3

Aquí hay una awksolución completamente basada en , donde colocaría la siguiente línea en su script de shell:

uppercasename="$(echo "$filename" | awk 'BEGIN{FS=OFS="."} NF==1{$1=toupper($1)} {for (i=1;i<NF;i++) $i=toupper($i)} 1')"

Esto utilizará el .como separador de campo.para entrada y saliday, si solo se encuentra un campo, conviértalo a mayúsculas y, en todos los demás casos, convierta todos los campos excepto el último a mayúsculas. Luego imprime el resultado (este es el significado de 1, que es una notación abreviada de {print}).

Si está utilizando bash, puede deshacerse de la tubería y declararla como

uppercasename="$(awk 'BEGIN{FS=OFS="."} NF==1{$1=toupper($1)} {for (i=1;i<NF;i++) $i=toupper($i)} 1' <<< "$filename")"

usando una cadena aquí.

Tenga en cuenta que esto está diseñado para que en el caso límite de un nombre de archivo que termina en a ., como en myfile.this.txt., lo trate como un "sufijo vacío pero presente" y lo convierta a MYFILE.THIS.TXT.. Además, si el nombre del archivoempiezacon a .y no tiene otra extensión (como en .myfile), lo mantendrá en minúscula.

información relacionada