¿Eliminar espacios, guiones y guiones bajos en los nombres de archivo?

10

¿Cuál es un buen comando para eliminar espacios, guiones y guiones bajos de todos los archivos en un directorio o archivos seleccionados?

Utilizo el siguiente comando con Thunar Custom Actions para reducir los nombres de archivo:

for file in %N; do mv "$file" "$(echo "$file" | tr -s ' ' | tr ' A-Z' '-a-z' | tr -s '-' | tr -c '[:alnum:][:cntrl:].' '-')"; done

Pero ese comando solo reemplaza espacios con guiones / guiones y caracteres con minúscula.

He usado el siguiente comando en la terminal para eliminar espacios de miles de nombres de archivo en una carpeta, y funcionó bastante rápido:

 rename "s/ //g" *

Nuevamente, solo elimina espacios, y no guiones / guiones y guiones bajos también.

Idealmente, no quiero espacios, guiones / guiones y guiones bajos en mis nombres de archivo. Y sería genial si el comando se pudiera usar con Thunar Custom Actions en los archivos seleccionados.

usuario8547
fuente
2
Noto que un problema que tienen muchas de las soluciones propuestas es no verificar adecuadamente la existencia del nombre "nuevo" antes de mover el archivo. No hacerlo podría ser la fuente potencial de muchos problemas.
mdpc
¿Es posible modificar el comando de John1024 para verificar eso?
user8547
@ user8547rename -i "s/[-_ ]//g" *
Sparhawk
Gracias gavilán. Por cierto, para aquellos interesados ​​en usar esto como una acción personalizada de Thunar, el comando para Thunar es: para el archivo en% N; hacer mv "$ archivo" echo $file | sed -e 's/[ _-]//g'; hecho
usuario8547

Respuestas:

11

La versión renameque viene con el perlpaquete admite expresiones regulares:

rename "s/[-_ ]//g" *

Alternativamente,

rename -i "s/[-_ ]//g" *

La -ibandera hará renameuso del modo interactivo, indicando si el objetivo ya existe, en lugar de sobrescribir en silencio.

El cambio de nombre de Perl a veces se llama prename.

El cambio de nombre de Perl frente al cambio de nombre de util-linux

En sistemas similares a Debian, el cambio de nombre de perl parece ser el predeterminado y los comandos anteriores deberían funcionar.

En algunas distribuciones, la renameutilidad de util-linux es la predeterminada. Esta utilidad es completamente incompatible con Perl rename.

  • Todos: Primero, verifique si Perl's renameestá disponible bajo el nombre prename.

  • Debian: el cambio de nombre de Perl debería ser el predeterminado. También está disponible como prename. Sin renameembargo, el ejecutable está bajo el control de /etc/alternativesy, por lo tanto, podría haberse modificado a algo diferente.

  • archlinux: Ejecutar pacman -S perl-renamey el comando está disponible como perl-rename. Para un nombre más conveniente, cree un alias. (Punta de sombrero: ChiseledAbs)

  • Mac OSX Según esta respuesta , renamese puede instalar en OSX usando homebrew a través de:

    brew install rename 
  • Descarga directa: rename también está disponible en Perl Monks:

     wget 'http://www.perlmonks.org/?displaytype=displaycode;node_id=303814' -O rename
John1024
fuente
Creo que eso depende de lo que renameestés hablando. El de util-linux -2.24.2-1.fc20.x86_64 no admite expresiones regulares.
Cristian Ciupitu
1
@CristianCiupitu Acabo de revisar la página de manual para la versión de renombrar que encontraste. Según los argumentos, la versión de la renameque estaba utilizando el OP se parece a la perlversión y no a la util-linuxversión.
John1024
Para el registro, esta es la renamepágina del manual para la versión util-linux . De todos modos, además de esa nota, lo importante es que el OP obtuvo su respuesta (y usted me votó a favor :-D).
Cristian Ciupitu
@CristianCiupitu Gracias por encontrar eso. De vuelta a ti con un +1.
John1024
1
@ John1024 archlinux, pero descubrí cómo, solo ve pacman -S perl-renamey supongo que puedes alias.
ChiseledAbs
5

Reemplazaría todos esos trcomandos, con un sedcomando de sustitución, por ejemplo:

for file in %N; do 
    mv "$file" "$(echo "$file" | sed 's/[ _-]//g')"
done
Cristian Ciupitu
fuente
4

Sin contar mv, en realidad no necesitas un proceso externo para esto, puedes simplemente hacerlos explotar .

ifsqz() ( LC_ALL=C sqz=$1
    isf() { [ -e "$1" ] || [ -L "$1" ] ; }  
    set -- * ; set -f
    for f do isf "$f" || break
    IFS=$sqz; set -- $f; IFS=
    isf "$*" || mv -- "$f" "$*"
    done
)

Sin embargo, eso significa una mvinvocación por archivo, por lo que probablemente renamesea ​​mejor. Aunque esto debería funcionar dado solo un POSIX mven $PATHy un shell POSIX.

Entonces, se me ocurrió una especie de demo loca para esto. El conjunto de prueba se genera como:

tee - - - - <<CGEN |\
dd cbs=90 conv=unblock |\
sed 'G;$!N'";s/^/touch -- '/;s/$/'/" |sh
$( #BEGIN CGEN
   LC_ALL=C
   i= n='"$((i=((i=i+1)==10||i==39||i==47)>0?(i+1):i))"'
   printf '%b -_   ---___'  $(
   IFS=0; eval \
       printf '"\\\\%04o\\\\%04o "' "$(
       printf "$n"' "$i" '%s $(
       printf %.252d
#END
))"))
CGEN

En primer lugar, seré el primero en reconocer que el comando anterior produce resultados que pueden obtenerse más fácilmente por otros medios. Pero es probable que otros medios no demuestren tan bien qué se podría hacer con $IFSun poco de imaginación (¿enferma?) .

Entonces, el primer bit es bastante sencillo:

  • tee canaliza 5 copias de su entrada, el documento aquí llamado CGEN

  • dd bloquea su entrada por nuevas líneas a 90 bytes por bloque y canaliza eso a ...

  • sedune 2 de esos bloques en dos \ncaracteres ewline, pone 'comillas simples a los resultados y antepone la cadena touch --para cada ciclo de línea antes de pasar a ...

  • sh que luego ejecuta todas las entradas como comandos de shell

El #CGENpoco aunque ... Bueno, en pocas palabras ...

  • la parte inferior printfimprime 252 0s

  • el siguiente del último recibe 252 ''argumentos de cadena nula y para cada uno imprime el contenido de $nseguido por la cadena" $i "

  • evalinterpreta los argumentos del siguiente printfantes de imprimir los resultados de esa interpretación como números octales precedidos por 2 barras diagonales inversas por pieza

  • el último printfimprime los valores de byte para esos octales 2 a la vez seguido de la cadena -_ ---___para cada par

  • $nse inicializa en una ecuación que se incrementará $ien uno para cada evaluación, excepto que omite los valores 10, 39 o 47 - (que son \newline, comillas 'simples y barras /inclinadas en decimal ASCII respectivamente)

El resultado final es un directorio que contiene muchos nombres de archivo realmente feos que contienen cada byte en mi juego de caracteres del 1 al 255, excepto la comilla simple (solo se omite para evitar una sed s///declaración más ) y la /barra inclinada. Esos nombres de archivo se ven así:

(set -- *; printf '%s\n\n##############\n\n%s\n' "${9}" "${34}")  | cat -A

   ---___ww -_   ---___xx -_   ---___yy -_   ---___zz -_   ---___{{ -_   ---___|| -_   ---$
$
___}} -_   ---___~~ -_   ---___^?^? -_   ---___M-^@M-^@ -_   ---___M-^AM-^A -_   ---___M-^BM-^B -_   ---___M-^CM-^C$
$
##############$
$
 -_   ---___M-ZM-Z -_   ---___M-[M-[ -_   ---___M-\M-\ -_   ---___M-]M-] -_   ---___M-^M-^ -_   ---___M-_M-_ -_$
$
---___M-`M-` -_   ---___M-aM-a -_   ---___M-bM-b -_   ---___M-cM-c -_   ---___M-dM-d -_   ---___M-eM-e -_   ---___$

Ahora obtendré algunos datos sobre estos archivos:

chksqz() ( LC_ALL=C sqz=$1
    set -- * ; set -f ; IFS= ; tc="$*"
    printf '#%s\n' \
        "There are $# files in this test directory." \
        "All filenames combined contain a total of ${#tc} bytes."
    IFS=$sqz ; set -- $* ; IFS= ; sc="$*"  
    printf "%s '$sqz'" \
        "#Of which ${#sc} bytes are not"\
        " and $((${#tc}-${#sc})) bytes are"
    set +f ; unset IFS
    printf ".\n#%s\n#Total:\t%d\n#Other:\t%d\n#'$sqz':\t%d\n" \
        "And to confirm these figures:" \
        $(  printf %s * | wc -c 
            printf %s * | tr -d "$sqz" | wc -c
            printf %s * | tr -dc "$sqz" | wc -c
))
chksqz '_ -'

SALIDA

#There are 101 files in this test directory.
#All filenames combined contain a total of 17744 bytes.
#Of which 2692 bytes are not '_ -' and 15052 bytes are '_ -'.
#And to confirm these figures:
#Total: 17744
#Other: 2692
#'_ -': 15052

Okay. Ahora finalmente, a la acción:

ifsqz '_ -'
chksqz '_ -'

SALIDA

#There are 101 files in this test directory.
#All filenames combined contain a total of 2692 bytes.
#Of which 2692 bytes are not '_ -' and 0 bytes are '_ -'.
#And to confirm these figures:
#Total: 2692
#Other: 2692
#'_ -': 0

¡Éxito! Puedes verlo por ti mismo:

ls

????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????
??????????????????????
????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
??????????????????????????
????????????????????????
????????????????????
??????????????????
????????????????????????????
??
????????????????????????????
??????????????????????????
????????????????????????????
????????????????????????????
????????????????????!!""##
??????????????????!!""##$$
????????????????!!""##$$%%
????????????!!""##$$%%&&((
????????!!""??##$$%%&&(())
$$%%&&(())**??++,,..0011
%%&&(())**++??,,..00112233
&&(())**++,,??..0011223344
))**++,,..??0011223344556
**++,,..00??11223344556677
22334455667788??99::;;<<==>>
445566778899??::;;<<==>>??@@
5566778899::;;??<<==>>??@@AA
6778899::;;<<??==>>??@@AABB
8899::;;<<==??>>??@@AABBCCDD
\\]]^^``aa??bbccddeeffgghh
]]^^``aabbc??cddeeffgghhii
^^``aabbccdd??eeffgghhiijj
??@@AABBCCDDEE??FFGGHHIIJJKK
AABBCCDDEEFF??GGHHIIJJKKLLM
BBCCDDEEFFGG??HHIIJJKKLLMMNN
CCDDEEFFGGHHII??JJKKLLMMNNOO
EEFFGGHHIIJJ??KKLLMMNNOOPPQQ
ffgghhiijjkk??llmmnnooppqqrr
gghhiijjkkllmm??nnooppqqrrss
iijjkkllmmnn??ooppqqrrsstt
jjkkllmmnnoo??ppqqrrssttuuvv
kkllmmnnooppqq??rrssttuuvvww
LLMMNNOOPPQQRR??SSTTUUVVWWXX
MNNOOPPQQRRSS??TTUUVVWWXXYY
OOPPQQRRSSTT??UUVVWWXXYYZZ[[
PPQQRRSSTTUUVV??WWXXYYZZ[[\\
RRSSTTUUVVWW??XXYYZZ[[\\]]
ssttuuvvwwxx??yyzz{{||}}~~??
ttuuvvwwxxyyz??z{{||}}~~????
uuvvwwxxyyzz{{??||}}~~??????
wwxxyyzz{{||??}}~~??????????
xxyyzz{{||}}~~??????????????
YYZZ[[\\]]^^??``aabbccddee
ZZ[[\\]]^^``??aabbccddeeff
mikeserv
fuente
2
+1 para un uso creativo de IFS+printf
John1024
@ John1024 - lo que es realmente divertido:set -- 'some arbitrary' args; eval printf '"%s\n"' "$(IFS=0; printf ' "$@" %s' $(printf %025d))"
mikeserv
1
new="$(IFS=" -_"; printf %s $1)"bifurca un subshell (excepto en ksh93) y tiene problemas con el seguimiento de nuevas líneas. Otra opción es usar IFS=' -_'; set -- $1; IFS=; new="$*"(y cambiar su ciclo while a un ciclo for)
Stéphane Chazelas
1
[ -e x ]devolverá falso si xes un enlace simbólico a un archivo no existente o no accesible.
Stéphane Chazelas
1
Bonito caparazón de Kung-Fu!
contramodo
2

si tiene perl, generalmente tiene que cambiar el nombre. tu puedes hacer:

> type rename
rename is /usr/bin/rename

y muestra cómo se escribe este guión:

> cat /usr/bin/rename | head -n 5 #firt 5 lines for example
#!/usr/bin/perl -w
#
#  This script was developed by Robin Barker ([email protected]),
#  from Larry Wall's original script eg/rename from the perl source.
#

Este script no es compatible con el indicador -i (esta es la versión en mi sistema), pero tal vez el suyo sea compatible. ¿Qué hay de los argumentos? Primero están las expresiones regulares con formato PCRE, funciona como filtro, modifica el nombre de entrada al nombre de salida. Lista de nombres de entrada que da por asterisco '*'. por ejemplo, haces:

> cd /tmp
> rename 's/ //g' *

en real '*' se puede expandir a:

> rename 's/ //g' file1 file2 file3 othe files found in current directory

Cuando tienes archivos de conteo realmente grandes, estás en trampa. Shell expandirá su línea más de lo que acepta el sistema. entonces puede hacer una solución usando find o xargs. El uso de 'buscar' es un problema, ya que el cambio de nombre se llamará muchas veces igual al conteo de archivos en el directorio. mejor usar xargs con la opción -r. una llamada de cambio de nombre modifica muchos archivos. por ejemplo:

> ls | xargs -r rename 's/ //g'   #thats all, names will be appended at the end of this command.

último problema, qué significa:

's/ //g'

Esta es una expresión regular para modificar nombres. después de la primera '/' es el espacio. esto se detecta y se reemplaza por una cadena después del segundo '/'. Pero hay una cadena vacía que termina con el tercer '/', luego el espacio se reemplaza por nada. La opción 'g' hace que esta expresión sea repetitiva. la expresión caminará por todo el nombre desde el principio hasta el final y detectará todos los espacios.

Pero, ¿qué pasa si tienes un carácter de tabulación u otro personaje 'blanco'? Hay un reemplazo para este '\ s'. ¿Qué otros personajes innecesarios? simplemente agrégalo a la expresión. Todos se cierran con corchetes, por ejemplo:

's/[\s_-]//g'

esto es todo. ves similitud? Creo que deberías leer man perlrequick y man perlretut, esto te explica (espero) cómo funciona la expresión regular. puede usar el comando renombrar en su propio script si lo necesita.

Znik
fuente
1

El siguiente shciclo de shell eliminará todos los espacios, guiones bajos y guiones de los nombres de los archivos en el directorio actual, teniendo cuidado de no sobrescribir ningún archivo existente:

for f in *; do
    test -f "$f" || continue
    nf=$( echo "$f" | tr -d ' _-' )
    ! test -e "$nf" && echo mv "$f" "$nf"
done

Para bashy ksh, y siendo un poco más detallado con la lógica:

for f in *; do
    if [[ -f "$f" ]]; then
        nf=$( tr -d ' _-' <<<"$f" )
        if [[ ! -e "$nf" ]]; then
            echo mv "$f" "$nf"
        fi
    fi
done

Elimina echocuando estés seguro de que hace lo que quieres que haga.

El trcomando eliminará ( -d) cualquier carácter en el conjunto de caracteres dado ( ' _-'). Es importante tener el guión al comienzo o al final del conjunto, o se interpretará como un rango de caracteres.

Kusalananda
fuente