Reemplazar puntos en el nombre del archivo con guiones bajos, excepto la extensión

9

¿Alguien puede sugerir cómo cambiar el nombre del archivo:

head.body.date.txt

A:

head_body_date.txt

¿Hay una declaración de una sola línea para cambiar el nombre en Unix ?

SG
fuente
99
¿Asumes que .tar.gzes una extensión? Además, ¡bienvenido a U&L!
EKons
2
¿Realmente te refieres a Unix en lugar de Linux? Cual Unix?
terdon
1
La respuesta obvia es mv head.body.date.txt head_body_date.txt. Si tiene otras restricciones o ejemplos reales, actualice su pregunta con estos. Tal como está ahora, no está claro si la pregunta se refiere a un archivo con ese nombre explícito, un único directorio donde todos los archivos tienen un formato particular, o si desea buscar en una jerarquía de directorios nombres particulares y cambiarlos.
Kusalananda

Respuestas:

9

Iterar sobre los nombres de archivo y usar la expansión de parámetros para la conversión:

for f in *.*.*.txt; do i="${f%.txt}"; echo mv -i -- "$f" "${i//./_}.txt"; done

El patrón de expansión de parámetros ${f//./_}reemplaza todas las .s con _s en el nombre del archivo ( $f).

Lo anterior hará una ejecución en seco, para permitir que tenga lugar el cambio de nombre real, elimine echo:

for f in *.*.*.txt; do i="${f%.txt}"; mv -i -- "$f" "${i//./_}.txt"; done

Si desea lidiar con cualquier extensión, no solo .txt:

for f in *.*.*.*; do pre="${f%.*}"; suf="${f##*.}"; \
                     echo mv -i -- "$f" "${pre//./_}.${suf}"; done

Después de marcar eliminar echopara la acción real:

for f in *.*.*.*; do pre="${f%.*}"; suf="${f##*.}"; \
                     mv -i -- "$f" "${pre//./_}.${suf}"; done

Genérico, para un número arbitrario de puntos, al menos uno:

for f in *.*; do pre="${f%.*}"; suf="${f##*.}"; \
                 mv -i -- "$f" "${pre//./_}.${suf}"; done
heemayl
fuente
¿hay alguna forma general si hay un número arbitrario de puntos?
Ciprian Tomoiagă
1
@CiprianTomoiaga Do:for f in *.*; do ...; done
heemayl el
Podría sugerir poner el *.*patrón directamente en el ejemplo, ya que ahora parece que el código funciona solo con un número fijo de puntos.
ilkkachu
@ilkkachu Bastante justo, editado.
heemayl
Muchas gracias heemay, funcionó para mí. y algo de aprendizaje!
SG
4

con perlbaserename

$ rename -n 's/\.[^.]+$(*SKIP)(*F)|\./_/g' head.body.date.txt 
rename(head.body.date.txt, head_body_date.txt)
  • \.[^.]+$(*SKIP)(*F) omita este patrón y busque coincidencias alternativas
  • |\./_/greemplazar todo .con_

O, usando anticipación negativa

$ rename -n 's/\.(?![^.]+$)/_/g' head.body.date.txt 
rename(head.body.date.txt, head_body_date.txt)

Una vez que esto esté bien, elimine la -nopción

Sundeep
fuente
3
rename 's/\.(?=[^.]*\.)/_/g' *.txt

Utiliza el reemplazo de expresiones regulares para encontrar todo menos la última instancia del .nombre de archivo (búsqueda anticipada sin captura) y reemplazarlos con _. Generaliza a *.*si te gusta.

(esta versión particular de renameparece estar instalada a través de util-linux). Estoy usando Ubuntu 12.04(sí, máquina muy desactualizada).

Jonathan Carroll
fuente
2

bash:

old=head.body.date.txt oldb=${old%.*} olde=${old##*.} ; \
mv -- "$old" "${oldb//./_}.${olde}"
agc
fuente
(1) Debe usar mv --, para protegerse de los nombres de archivo "antiguos" que comienzan con -. (2) Debe poner $oldentre comillas ( "$old"), para protegerse contra los nombres de archivo "antiguos" que contienen espacios o caracteres que son especiales para el shell (como *y ?). (3) Su respuesta falla en el caso algo patológico en el que la extensión contiene un guión bajo: head.body.date.t_xtse renombra a head_body_date_t.xt.
Scott
@ Scott, gracias. El código ahora se modifica según los tres elementos mencionados.
agc
1

Usando mv, sedy reven una línea:

mv "head.body.date.txt" "$(echo head.body.date.txt | rev | sed 's/\./_/2g' | rev)"

Si desea aplicarlo en todos los txtarchivos en el representante actual, usar globs parece complicado debido a cómomv funciona, pero puede hacer un forbucle en una línea:

for file in *.txt; do mv "$file" "$(echo $file | rev | sed 's/\./_/2g' | rev)"; done

¡Un poco más largo, pero puedes combinar varios patrones en tu bucle!

Valentin B.
fuente
2
(1) Arrghhh! No lo hagas $(ls …); solo hazlo for file in *.txt. (2) Se debe utilizar más citas: mv "$file" "$(…)".
Scott
¡Gracias Scott, debidamente anotado y editado! Todavía soy un joven con cáscara, por favor sea gentil;)
Valentin B.
En retrospectiva, usar renamecomo muchos otros han sugerido en sus respuestas parece más simple ...
Valentin B.
-1
 mv head.body.date.txt head_body_date.txt

Es tan simple como eso para un solo archivo a la vez. No especifica si desea que esto se haga recursivamente dentro de un directorio o en todo el directorio, por lo que sería su solución más conveniente y rápida.

coderodour
fuente