Linux: Spostare 1 milione di file in cartelle create prefisso a base di
Domanda
Ho una directory chiamata "immagini" riempiti con circa un milione di immagini. Sì.
Voglio scrivere un comando di shell per rinominare tutte quelle immagini nel seguente formato:
originale: filename.jpg
nuovo: /f/i/l/filename.jpg
Qualche suggerimento?
Grazie,
Dan
Soluzione
for i in *.*; do mkdir -p ${i:0:1}/${i:1:1}/${i:2:1}/; mv $i ${i:0:1}/${i:1:1}/${i:2:1}/; done;
La parte ${i:0:1}/${i:1:1}/${i:2:1}
probabilmente potrebbe essere una variabile, o più corto o diversi, ma il comando sopra ottiene il lavoro fatto. Probabilmente affrontare problemi di prestazioni, ma se si vuole veramente usarlo, restringere il *.*
a un minor numero di opzioni (a*.*
, b*.*
o quello che si adatta)
edit: aggiunto un $
prima i
per mv
, come notato da Dan
Altri suggerimenti
È possibile generare il nuovo nome del file utilizzando, ad esempio, sed:
$ echo "test.jpg" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'
t/e/s/test.jpg
Quindi, si può fare qualcosa di simile (assumendo che tutte le directory sono già stati creati):
for f in *; do
mv -i "$f" "$(echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/')"
done
o, se non è possibile utilizzare la sintassi bash $(
:
for f in *; do
mv -i "$f" "`echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'`"
done
Tuttavia, considerando il numero di file, si può fare, a usare perl in quanto questo è un sacco di processi sed e mv per deporre le uova:
#!/usr/bin/perl -w
use strict;
# warning: untested
opendir DIR, "." or die "opendir: $!";
my @files = readdir(DIR); # can't change dir while reading: read in advance
closedir DIR;
foreach my $f (@files) {
(my $new_name = $f) =~ s!^((.)(.)(.).*)$!$2/$3/$4/$1/;
-e $new_name and die "$new_name already exists";
rename($f, $new_name);
}
che Perl è sicuramente limitato solo tra persone dello stesso file system, anche se è possibile utilizzare File::Copy::move
per aggirare questo.
Si può fare come uno script bash:
#!/bin/bash
base=base
mkdir -p $base/shorts
for n in *
do
if [ ${#n} -lt 3 ]
then
mv $n $base/shorts
else
dir=$base/${n:0:1}/${n:1:1}/${n:2:1}
mkdir -p $dir
mv $n $dir
fi
done
Inutile dire, potrebbe essere necessario preoccuparsi di spazi e i file con nomi brevi.
Suggerisco uno script python breve. La maggior parte degli strumenti di shell saranno esitano a più di tanto di ingresso (anche se xargs possono fare il trucco). Aggiornerà con l'esempio in un secondo.
#!/usr/bin/python
import os, shutil
src_dir = '/src/dir'
dest_dir = '/dest/dir'
for fn in os.listdir(src_dir):
os.makedirs(dest_dir+'/'+fn[0]+'/'+fn[1]+'/'+fn[2]+'/')
shutil.copyfile(src_dir+'/'+fn, dest_dir+'/'+fn[0]+'/'+fn[1]+'/'+fn[2]+'/'+fn)
Qualsiasi delle soluzioni proposte che utilizzano una sintassi jolly nella shell probabilmente non riuscire a causa del gran numero di file che avete. Tra le soluzioni proposte attuali, il perl è probabilmente il migliore.
Tuttavia, si può facilmente adattare qualsiasi dei metodi di script di shell a che fare con qualsiasi numero di file in tal modo:
ls -1 | \
while read filename
do
# insert the loop body of your preference here, operating on "filename"
done
Vorrei ancora usare perl, ma se si è limitato ad avere solo semplici strumenti Unix intorno, poi combina una delle soluzioni guscio di cui sopra con un ciclo come ho mostrato dovrebbe arrivare lì. Sarà lento, però.