2010-03-30 176 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