Renaming and Moving Files in Bash or Perl
嗨,我对bash和stackoverflow完全不熟悉。
我需要将一组文件(都包含在同一文件夹中)移动到目标文件夹,在该文件夹中可能已经存在同名文件。
如果存在一个特定的文件,我需要在移动之前重命名该文件,方法是在文件名后面附加一个增量整数。
扩展应该被保留(换句话说,附加的增量整数应该在扩展之前)。文件名可以包含中间的点。
最初,我想比较这两个文件夹,得到一个现有文件的列表(我用"comm"做了这个操作),但后来有点卡住了。我想我只是想用最复杂的方式做事。
有没有"bash方式"的提示?如果它是在bash脚本之外的脚本中完成的,那么就可以了。
如果您不介意重命名已经存在的文件,gnu
1 | mv --backup=numbered * /some/other/dir |
下面是一个bash脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | source="/some/dir" dest="/another/dir" find"$source" -maxdepth 1 -type f -printf"%f " | 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")。
您可以将上面的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | source="/some/dir" dest="/another/dir" find"$source" -maxdepth 1 -type f -printf"%f " | 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 |
根据OP,这可以是Perl,而不仅仅是bash。我们走吧
新解决方案:(注意延伸)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ~/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: $! "; } }' ~/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 |
老办法:(不注意延伸)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ~/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: $! "; } }' ~/junk/a1> ls ../a2 f1 f1.1 f2 f2.1 f2.2 f2.3 f2.4 f3 |
如果不需要增量后缀,rsync可以执行以下操作:
1 | rsync --archive --backup --suffix=.sic src/ dst |
更新:
find/sed/sort用于管理版本化备份文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #!/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 |
我不想测试就把这个贴出来。但是已经晚了,我早上有工作。我的尝试看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ## 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 |
总的来说,策略是从源路径中获取每个名称,将其分解为多个部分,并且对于任何冲突,迭代形式为"base~xx.extension"的名称,直到找到一个不冲突的名称。
很明显,我已经用一个