PD: Tenga en cuenta también los nombres de archivo con caracteres extraños (por ejemplo, espacios)
Nathan JB
3
También deberá especificar cómo desea manejar el posible caso donde ./foo/bar/baz.pngy ./foo-bar-baz.pngambos existen. ¿Asumo que no quieres reemplazar el último con el primero?
jw013
En mi caso, es irrelevante, ¡pero la respuesta debería explicarlo para que otros puedan confiar en él! ¡Gracias!
Nathan JB
Respuestas:
7
Advertencia: escribí la mayoría de estos comandos directamente en mi navegador. Advertencia lector.
Explicación: El patrón **/*coincide con todos los archivos en subdirectorios del directorio actual, de forma recursiva (no coincide con los archivos del directorio actual, pero no es necesario cambiarles el nombre). Los primeros dos pares de paréntesis son grupos a los que se puede hacer referencia como $1y $2en el texto de reemplazo. El par final de paréntesis agrega el Dcalificador global para que no se omitan los archivos de puntos. -o -isignifica pasar la -iopción a mvpara que se le pregunte si se sobrescribirá un archivo existente.
Con solo herramientas POSIX:
find . -depth -exec sh -c '
for source; do
case $source in ./*/*)
target="$(printf %sz "${source#./}" | tr / -)";
mv -i -- "$source" "${target%z}";;
esac
done
' _ {} +
Explicación: la casedeclaración omite el directorio actual y los subdirectorios de nivel superior del directorio actual. targetcontiene el nombre del archivo de origen ( $0) con el principal ./despojado y todas las barras reemplazadas por guiones, más un final z. La final zestá allí en caso de que el nombre del archivo termine con una nueva línea: de lo contrario, la sustitución del comando lo eliminaría.
Si tu findno es compatible -exec … +(OpenBSD, te estoy mirando):
find . -depth -exec sh -c '
case $0 in ./*/*)
target="$(printf %sz "${0#./}" | tr / -)";
mv -i -- "$0" "${target%z}";;
esac
' {} \;
Con bash (o ksh93), no necesita llamar a un comando externo para reemplazar las barras por guiones, puede usar la expansión del parámetro ksh93 con la construcción de reemplazo de cadena ${VAR//STRING/REPLACEMENT}:
find . -depth -exec bash -c '
for source; do
case $source in ./*/*)
source=${source#./}
target="${source//\//-}";
mv -i -- "$source" "$target";;
esac
done
' _ {} +
Los for sourceejemplos pierden el primer archivo / directorio presentado ... Finalmente descubrí por qué usted (normalmente) ha utilizado _como $0:) ... y gracias por las excelentes respuestas. Aprendo mucho de ellos.
Peter.O
Tenga en cuenta que el ejemplo de zmv solo realiza una ejecución en seco: imprime los comandos de movimiento pero no los ejecuta. Para ejecutar realmente esos comandos, elimine la n en -Qn.
Peter
5
Si bien ya hay buenas respuestas aquí, encuentro esto más intuitivo, dentro de bash:
¿Dónde aaaestá tu directorio actual? Los archivos que contiene se moverán al directorio actual (dejando solo directorios vacíos en aaa). Las dos llamadas para trmanejar tanto directorios como espacios (sin lógica para las barras oblicuas escapadas, un ejercicio para el lector).
Considero que esto es más intuitivo y relativamente fácil de modificar. Es decir, podría cambiar los findparámetros si digo que solo quiero encontrar archivos .png. Puedo cambiar las trllamadas o agregar más según sea necesario. Y puedo agregar echoantes mvsi quiero verificar visualmente que hará lo correcto (luego repetir sin el extra echosolo si las cosas se ven bien:
Tenga en cuenta también que estoy usando find aaa... y no find .... o find aaa/... ya que los dos últimos dejarían artefactos divertidos en el nombre del archivo final.
Gracias por este revestimiento: funcionó bien para mí cuando lo hice desde fuera del directorio (que es exactamente lo que dijiste que hiciera). Cuando intenté hacerlo desde dentro del directorio (con la esperanza de evitar que se agregara el nombre del directorio raíz) todos los archivos "desaparecieron". Los había convertido en archivos ocultos en ese mismo directorio.
Nota: esto no funcionará para el nombre de archivo que contiene \n.
Esto, por supuesto, solo mueve farchivos de tipo ...
Los únicos conflictos de nombres serían de archivos preexistentes en el pwd
Aquí hay una lsversión ... comentarios bienvenidos. Funciona para nombres de archivo como este
"j1ळ\n\001\n/j2/j3/j4/\nh h\n", pero estoy realmente interesado en saber de cualquier escollo. Es más un ejercicio de "¿pueden los difamados lsrealmente hacerlo (con firmeza)?"
ls -AbQR1p * | # paths in "quoted" \escaped format
sed -n '/":$/,${/.[^/]$/p}' | # skip files in pwd, blank and dir/ lines
{ bwd="$PWD"; while read -n1; # save base dir strip leading "quote
read -r p; do # read path
printf -vs "${p%\"*}" # strip trailing quote"(:)
[[ $p == *: ]] && { # this is a directory
cd "$bwd/$s" # change into new directory
rnp="${s//\//-}"- # relative name prefix
} || mv "$s" "$bwd/$rnp$s"
done; }
Esto no maneja nombres de archivo extraños de forma segura. Los espacios lo romperán (entre otras cosas).
jw013
A primera vista, esta es una solución limpia y legible, pero @ jw013 tiene razón, no manejará bien los espacios. ¿Cambiaría la cplínea para cp -v "$FILE" "$NEWFILE"ayudar? (Además, no debe asumir solo archivos png.)
Nathan JB
2
@ NathanJ.Brauer Agregar comillas a la cplínea es insuficiente. Todo el enfoque de analizar la findsalida con un forbucle es defectuoso. Las únicas formas seguras de usar findson con -exec, o -print0si están disponibles (menos portátiles que -exec). Sugeriría una alternativa más sólida, pero su pregunta está subespecificada en este momento.
jw013
-2
Seguro para espacios en nombres de archivo:
#!/bin/bash
/bin/find $PWD -type f | while read FILE
do
_new="${FILE//\//-}"
_new="${_new:1}"
#echo $FILE
#echo $_new
/bin/mv "$FILE" "$_new"
done
Corrió con el mío en cplugar de mvpor razones obvias, pero debería producir lo mismo.
type find-> find is /usr/bin/finden mi máquina. Usar PATHcomo nombre de variable en un script es una idea terrible. Además, su secuencia de comandos manipula los nombres de los archivos con espacios o barras diagonales inversas, ya que utiliza incorrectamente el readcomando (busque en este sitio IFS read -r).
Gilles 'SO- deja de ser malvado'
find is hashed (/bin/find), relevante cómo? Diferentes distribuciones son diferentes?
Tim
Sabía que debería haberme alejado de esto, cebo buitre.
Tim
@Tim Siempre puede eliminar la respuesta para recuperar su representante (y el mío también porque cuesta rep para votar a favor). La codificación de rutas en scripts parece sugerir que se está perdiendo el punto de la PATHvariable de entorno, que es algo que todo sistema Unix usa. Si escribe /bin/find, su script está realmente roto en cada sistema (Gilles, el mío y probablemente el OP) que no tiene /bin/find(por ejemplo, b / c está en /usr/bin/find). El objetivo de la PATHvariable de entorno es hacer que esto no sea un problema.
jw013
Por cierto, $(pwd)ya está disponible en una variable: $PWD. Pero debe no utilizar una ruta absoluta aquí: si el script se invoca desde, digamos, /home/tim, intenta cambiar el nombre /home/tim/some/patha /home-tim-some-path.
./foo/bar/baz.png
y./foo-bar-baz.png
ambos existen. ¿Asumo que no quieres reemplazar el último con el primero?Respuestas:
Advertencia: escribí la mayoría de estos comandos directamente en mi navegador. Advertencia lector.
Con zsh y zmv :
Explicación: El patrón
**/*
coincide con todos los archivos en subdirectorios del directorio actual, de forma recursiva (no coincide con los archivos del directorio actual, pero no es necesario cambiarles el nombre). Los primeros dos pares de paréntesis son grupos a los que se puede hacer referencia como$1
y$2
en el texto de reemplazo. El par final de paréntesis agrega elD
calificador global para que no se omitan los archivos de puntos.-o -i
significa pasar la-i
opción amv
para que se le pregunte si se sobrescribirá un archivo existente.Con solo herramientas POSIX:
Explicación: la
case
declaración omite el directorio actual y los subdirectorios de nivel superior del directorio actual.target
contiene el nombre del archivo de origen ($0
) con el principal./
despojado y todas las barras reemplazadas por guiones, más un finalz
. La finalz
está allí en caso de que el nombre del archivo termine con una nueva línea: de lo contrario, la sustitución del comando lo eliminaría.Si tu
find
no es compatible-exec … +
(OpenBSD, te estoy mirando):Con bash (o ksh93), no necesita llamar a un comando externo para reemplazar las barras por guiones, puede usar la expansión del parámetro ksh93 con la construcción de reemplazo de cadena
${VAR//STRING/REPLACEMENT}
:fuente
for source
ejemplos pierden el primer archivo / directorio presentado ... Finalmente descubrí por qué usted (normalmente) ha utilizado_
como$0
:) ... y gracias por las excelentes respuestas. Aprendo mucho de ellos.Si bien ya hay buenas respuestas aquí, encuentro esto más intuitivo, dentro de
bash
:¿Dónde
aaa
está tu directorio actual? Los archivos que contiene se moverán al directorio actual (dejando solo directorios vacíos en aaa). Las dos llamadas paratr
manejar tanto directorios como espacios (sin lógica para las barras oblicuas escapadas, un ejercicio para el lector).Considero que esto es más intuitivo y relativamente fácil de modificar. Es decir, podría cambiar los
find
parámetros si digo que solo quiero encontrar archivos .png. Puedo cambiar lastr
llamadas o agregar más según sea necesario. Y puedo agregarecho
antesmv
si quiero verificar visualmente que hará lo correcto (luego repetir sin el extraecho
solo si las cosas se ven bien:Tenga en cuenta también que estoy usando
find aaa
... y nofind .
... ofind aaa/
... ya que los dos últimos dejarían artefactos divertidos en el nombre del archivo final.fuente
Nota: esto no funcionará para el nombre de archivo que contiene
\n
.Esto, por supuesto, solo mueve
f
archivos de tipo ...Los únicos conflictos de nombres serían de archivos preexistentes en el pwd
Probado con este subconjunto básico
Resultando en
fuente
Aquí hay una
ls
versión ... comentarios bienvenidos. Funciona para nombres de archivo como este"j1ळ\n\001\n/j2/j3/j4/\nh h\n"
, pero estoy realmente interesado en saber de cualquier escollo. Es más un ejercicio de "¿pueden los difamadosls
realmente hacerlo (con firmeza)?"fuente
Simplemente canalice el nombre de archivo + ruta al
tr
comando.tr <replace> <with>
fuente
cp
línea paracp -v "$FILE" "$NEWFILE"
ayudar? (Además, no debe asumir solo archivos png.)cp
línea es insuficiente. Todo el enfoque de analizar lafind
salida con unfor
bucle es defectuoso. Las únicas formas seguras de usarfind
son con-exec
, o-print0
si están disponibles (menos portátiles que-exec
). Sugeriría una alternativa más sólida, pero su pregunta está subespecificada en este momento.Seguro para espacios en nombres de archivo:
Corrió con el mío en
cp
lugar demv
por razones obvias, pero debería producir lo mismo.fuente
type find
->find is /usr/bin/find
en mi máquina. UsarPATH
como nombre de variable en un script es una idea terrible. Además, su secuencia de comandos manipula los nombres de los archivos con espacios o barras diagonales inversas, ya que utiliza incorrectamente elread
comando (busque en este sitioIFS read -r
).find is hashed (/bin/find)
, relevante cómo? Diferentes distribuciones son diferentes?PATH
variable de entorno, que es algo que todo sistema Unix usa. Si escribe/bin/find
, su script está realmente roto en cada sistema (Gilles, el mío y probablemente el OP) que no tiene/bin/find
(por ejemplo, b / c está en/usr/bin/find
). El objetivo de laPATH
variable de entorno es hacer que esto no sea un problema.$(pwd)
ya está disponible en una variable:$PWD
. Pero debe no utilizar una ruta absoluta aquí: si el script se invoca desde, digamos,/home/tim
, intenta cambiar el nombre/home/tim/some/path
a/home-tim-some-path
.