2010-03-30 13 views
5

HI, sono completamente nuovo su Bash e StackOverflow.Rinominare e spostare file in Bash o Perl

Ho bisogno di spostare un set di file (tutti contenuti nella stessa cartella) in una cartella di destinazione in cui potrebbero già esistere file con lo stesso nome.

Nel caso esista un file specifico, è necessario rinominare il file prima di spostarlo, aggiungendo ad esempio un intero incrementale al nome del file.

Le estensioni devono essere conservate (in altre parole, tale numero incrementale aggiunto dovrebbe passare prima dell'estensione). I nomi dei file potrebbero contenere punti nel mezzo.

Inizialmente, stavo pensando di confrontare le due cartelle per avere un elenco dei file esistenti (l'ho fatto con "comm"), ma poi mi sono un po 'bloccato. Penso che sto solo cercando di fare le cose nel modo più complicato possibile.

Qualche suggerimento per farlo nel "modo bash"? Va bene se è fatto in uno script diverso da quello di bash.

+0

Benvenuti in SO! :) –

+0

@ Katie - Mi sono preso la libertà di modificare la tua domanda e inserire entrambe le cose che sono state chiarite da te nei commenti. – DVK

risposta

3

Come per OP, questo può essere Perl, non solo bash. Qui andiamo

NUOVA SOLUZIONE: (facendo attenzione alla estensione)

~/junk/a1$ ls 
f1.txt f2.txt f3.txt z1  z2 


~/junk/a1$ ls ../a2 
f1.txt  f2.1.txt f2.2.txt f2.3.txt f2.txt  z1 

# I split the one-liner into multiple lines for readability 
$ perl5.8 -e 
    '{use strict; use warnings; use File::Copy; use File::Basename; 
     my @files = glob("*"); # assume current directory 
     foreach my $file (@files) { 
      my $file_base2 = basename($file); 
      my ($file_base, $ext) = ($file_base2 =~ /(.+?)([.][^.]+$)?$/); 
      my $new_file_base = "../a2/$file_base"; 
      my $new_file = $new_file_base . $ext; 
      my $counter = 1; 
      while (-e $new_file) { 
       $new_file = "$new_file_base." . $counter++ . $ext; 
      } 
      copy($file, $new_file) 
       || die "could not copy $file to $new_file: $!\n"; 
     } }' 

~/junk/a1> ls ../a2 
f1.1.txt f1.txt f2.1.txt f2.2.txt f2.3.txt f2.4.txt f2.txt f3.txt 
z1   z1.1  z2 

SOLUZIONE VECCHIO: (non prestando attenzione a estensione)

~/junk/a1$ ls 
f1 f2 f3 

~/junk/a1$ ls ../a2 
f1  f2  f2.1 f2.2 f2.3 

# I split the one-liner into multiple lines for readability 
$ perl5.8 -e 
    '{use strict; use warnings; use File::Copy; use File::Basename; 
     my @files = glob("*"); # assume current directory 
     foreach my $file (@files) { 
      my $file_base = basename($file); 
      my $new_file_base = "../a2/$file_base"; 
      my $new_file = $new_file_base; 
      my $counter = 1; 
      while (-e $new_file) { $new_file = "$new_file_base." . $counter++; } 
      copy($file,$new_file) 
       || die "could not copy $file to $new_file: $!\n"; 
     } }' 

~/junk/a1> ls ../a2 
f1  f1.1 f2  f2.1 f2.2 f2.3 f2.4 f3 
+0

Questo è ottimo (non mi interessa perl o bash), ma dovrei applicare il nuovo nome conservando l'estensione (ottenendo qualcosa come foo_1.txt per foo.txt, invece di foo.text.1). Sto cercando di capire come fare questo ... – Katie

+0

@ Katie - Vedi gli aggiornamenti – DVK

+0

@Katie - e buona attitudine a "non mi interessa finché si ottiene il lavoro" :) – DVK

7

Se non ti dispiace rinominare i file già esistenti, GNU mv ha la possibilità --backup:

mv --backup=numbered * /some/other/dir 
+0

In realtà, ho bisogno di conservare i nomi dei file della directory di destinazione. Inoltre, dovrei conservare le estensioni. Ma grazie per questo suggerimento, potrebbe essere utile in altre situazioni ... – Katie

4

Ecco uno script Bash:

source="/some/dir" 
dest="/another/dir" 
find "$source" -maxdepth 1 -type f -printf "%f\n" | while read -r file 
do 
    suffix= 
    if [[ -a "$dest/$file" ]] 
    then 
     suffix=".new" 
    fi 
    # to make active, comment out the next line and uncomment the line below it 
    echo 'mv' "\"$source/$file\"" "\"$dest/$file$suffix\"" 
    # mv "source/$file" "$dest/$file$suffix" 
done 

Il suffisso viene aggiunto alla cieca. Se in entrambe le directory sono presenti file denominati "foo.new", il risultato sarà un file denominato "foo.new" e il secondo denominato "foo.new.new" che potrebbe sembrare sciocco, ma è corretto in quanto non non sovrascrivere il file. Tuttavia, se la destinazione contiene già "foo.new.new" (e "foo.new" è sia nell'origine che nella destinazione), quindi "foo.new.new" verrà sovrascritto).

È possibile modificare lo if in un loop per gestire questa situazione. Questa versione anche conserva estensioni:

source="/some/dir" 
dest="/another/dir" 
find "$source" -maxdepth 1 -type f -printf "%f\n" | while read -r file 
do 
    suffix= 
    count= 
    ext= 
    base="${file%.*}" 
    if [[ $file =~ \. ]] 
    then 
     ext=".${file##*.}" 
    fi 
    while [[ -a "$dest/$base$suffix$count$ext" ]] 
    do 
     ((count+=1)) 
     suffix="." 
    done 
    # to make active, comment out the next line and uncomment the line below it 
    echo 'mv' "\"$source/$file\"" "\"$dest/$file$suffix$count$ext\"" 
    # mv "$source/$file" "$dest/$file$suffix$count$ext" 
done 
+0

@Dennis - nice touch re: commentando 'mv' :) – DVK

0

mi sento male per questo distacco senza prove. Comunque è tardi e ho un lavoro al mattino. Il mio tentativo sarebbe simile a questa:

## copy files from src to dst  
## inserting ~XX into any name between base and extension 
## where a name collision would occur 
src="$1" 
dst="$2" 

case "$dst" in 
    /*) :;;    # absolute dest is fine 
    *) dst=$(pwd)/$dst;; # relative needs to be fixed up 
    esac 

cd "$src" 
find . -type f | while read x; do 
    x=${x#./}   # trim off the ./ 
    t=$x;    # initial target 
    d=$(dirname $x); # relative directory 
    b=$(basename $x); # initial basename 
    ext=${b%%.*};  # extension 
    b=${b##*.};   # basename with ext. stripped off 
    let zz=0;   # initial numeric 
    while [ -e "$dst/$t" ]; do 
     # target exists, so try constructing a new target name 
     t="$d/$bb~$zz.$ext" 
     let zz+=1; 
    done 
    echo mv "./$x" "$dst/$t" 
done 

Nel complesso la strategia è quello di ottenere ogni nome dal percorso di origine, romperlo in parti, e, per qualsiasi collisione, iterare nomi del modulo "base ~ XX. estensione "finché non ne troviamo uno che non collide.

Ovviamente ho anteposto il comando mv a un echo perché sono un vigliacco. Rimuovilo dal tuo pericolo (file).

0

Se non avete bisogno suffisso incrementale, rsync può fare il lavoro:

rsync --archive --backup --suffix=.sic src/ dst 

Aggiornamento:

find/sed/Ordinare è usato per gestire di versione dei file di backup:

#!/bin/bash                           

src="${1}" 
dst="${2}" 

if test ! -d "${src}" -o ! -d "${dst}" ;then 
    echo Usage: $0 SRC_DIR DST_DIR >&2 
    exit 1 
fi 

rsync --archive --backup "${src}/" "${dst}/" 
new_name() { 
    local dst=$1 
    local prefix=$2 
    local suffix=$3 
    local max=$(find ${dst} -type f -regex ".*${prefix}.[0-9]*.${suffix}\$" \ 
     | sed 's/.*\.\([0-9]*\)\..*/\1/'|sort -n|tail -n 1) 
    let max++ 
    echo ${prefix}.${max}.${suffix} 
} 

# swap BACKUP-extension/real-extension                    
for backup_file in $(find $dst -name "*~"); do 
    file=${backup_file%~} 
    prefix=${file%.*} 
    suffix=${file##*.} 
    suffix=${suffix%\~} 
    mv ${backup_file} $(new_name $dst $prefix $suffix) 
done 
Problemi correlati