¿Cómo puedo aplanar un directorio en el siguiente formato?
Antes de: ./aaa/bbb/ccc.png
Después: ./aaa-bbb-ccc.png
./foo/bar/baz.pngy ./foo-bar-baz.pngambos existen. ¿Asumo que no quieres reemplazar el último con el primero?
¿Cómo puedo aplanar un directorio en el siguiente formato?
Antes de: ./aaa/bbb/ccc.png
Después: ./aaa-bbb-ccc.png
./foo/bar/baz.pngy ./foo-bar-baz.pngambos 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 :
zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'
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 D calificador 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
' _ {} +
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.
Si bien ya hay buenas respuestas aquí, encuentro esto más intuitivo, dentro de bash:
find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;
¿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:
find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;
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.
find . -mindepth 2 -type f -name '*' |
perl -l000ne 'print $_; s/\//-/g; s/^\.-/.\// and print' |
xargs -0n2 mv
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
Probado con este subconjunto básico
rm -fr junk
rm -f junk*hello*
mkdir -p junk/junkier/junkiest
touch 'hello hello'
touch 'junk/hello hello'
touch 'junk/junkier/hello hello'
touch 'junk/junkier/junkiest/hello hello'
Resultando en
./hello hello
./junk-hello hello
./junk-junkier-hello hello
./junk-junkier-junkiest-hello hello
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; }
Simplemente canalice el nombre de archivo + ruta al trcomando.tr <replace> <with>
for FILE in `find aaa -name "*.png"`
do
NEWFILE=`echo $FILE | tr '/' '-'`
cp -v $FILE $NEWFILE
done
cplínea para cp -v "$FILE" "$NEWFILE"ayudar? (Además, no debe asumir solo archivos png.)
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.
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).
find is hashed (/bin/find), relevante cómo? Diferentes distribuciones son diferentes?
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.
$(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.