2010-03-30 199 views
5

嗨,我完全不熟悉Bash和StackOverflow。在Bash或Perl中重命名和移動文件

我需要將一組文件(全部包含在同一文件夾中)移動到目標文件夾,其中具有相同名稱的文件可能已經存在。

如果存在特定文件,我需要在移動它之前重命名該文件,方法是在文件名後附加一個增量整數。

應該保留擴展名(換句話說,附加的增量整數在之前應該爲)。文件名可以在中間包含點。

最初,我正在考慮比較兩個文件夾有一個現有的文件列表(我做了這個與「通信」),但後來我有點卡住了。我想我只是想以最複雜的方式做事。

任何暗示在「bash的方式」做到這一點?如果它是在bash腳本以外的腳本中完成的,那麼也可以。

+0

歡迎來到SO! :) – 2010-03-30 21:24:25

+0

@Katie - 我冒昧地編輯你的問題,並把你在評論中闡明的兩件事情都放進去。 – DVK 2010-03-30 23:03:56

回答

3

按OP,這可能是Perl的,不只是慶典。在這裏,我們去

新的解決方案:(注意擴展名)

~/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 

老辦法:(不重視擴展)

~/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

這是偉大的(我不在乎perl或bash),但我應該通過保留擴展名來應用新名稱(獲得類似foo_1.txt爲foo.txt,而不是foo.text.1)。我試圖瞭解如何做到這一點...... – Katie 2010-03-30 22:13:27

+0

@Katie - 查看更新 – DVK 2010-03-30 22:47:21

+0

@Katie - 對「只要完成任務就不在乎」的好態度:) – DVK 2010-03-30 22:48:16

7

如果你不介意重命名已存在的文件,GNU mv--backup選項:

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

實際上,我需要保留目標目錄的文件名稱。另外,我應該保留擴展名。但感謝這個提示,它可能會在其他情況下有用... – Katie 2010-03-30 21:51:52

4

這裏是一個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 

後綴是盲目添加的。如果在兩個目錄中都有名爲「foo.new」的文件,那麼結果將是一個名爲「foo.new」的文件,而第二個名爲「foo.new.new」的文件可能看起來很傻,但是正確的是它沒有覆蓋文件。但是,如果目的地已經包含「foo.new.new」(並且「foo.new」位於源和目的地中),則「foo.new.new」將被覆蓋)。

您可以將上面的if更改爲循環以處理這種情況。這個版本也保留擴展

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

@丹尼斯 - 很好的觸摸重新:評論'MV':) – DVK 2010-03-30 22:50:36

0

我覺得不好張貼這一點沒有測試它。不過,現在已經很晚了,我早上有工作。我的企圖都將是這個樣子:

## 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 

總體的策略是從源路徑獲取每個名稱,它分成部分,並且對於任何碰撞,遍歷窗體「基地〜XX的名字。延伸「,直到我們找到一個不會發生碰撞的地方。

很明顯,我已經預先配置了mv命令,因爲我是個膽小鬼,所以命令是echo。刪除自己(文件)的危險。

0

如果你不需要增加後綴,rsync的可以做的工作:

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

更新

查找/ SED /排序用於管理版本的備份文件:

#!/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