【问题标题】:Renaming and Moving Files in Bash or Perl在 Bash 或 Perl 中重命名和移动文件
【发布时间】:2011-02-02 16:17:46
【问题描述】:

您好,我对 Bash 和 StackOverflow 完全陌生。

我需要将一组文件(全部包含在同一个文件夹中)移动到一个目标文件夹,其中可能已经存在同名文件。

如果存在特定文件,我需要在移动它之前重命名该文件,例如在文件名中附加一个增量整数。

应该保留扩展名(换句话说,附加的增量整数应该放在扩展名之前)。文件名可以在中间包含点。

最初,我正在考虑比较两个文件夹以获得现有文件的列表(我使用“comm”进行此操作),但后来我有点卡住了。我想我只是想以最复杂的方式做事。

任何提示以“bash 方式”执行此操作?如果在bash脚本以外的脚本中完成也可以。

【问题讨论】:

  • @Katie - 我冒昧地编辑了你的问题,并将你在 cmets 中澄清的两件事都放进去了。

标签: perl bash unix scripting


【解决方案1】:

如果您不介意重命名已存在的文件,GNU mv--backup 选项:

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

【讨论】:

  • 其实我需要保留目标目录的文件名。另外,我应该保留扩展名。但是感谢这个提示,它在其他情况下可能很有用......
【解决方案2】:

根据 OP,这可以是 Perl,而不仅仅是 bash。来了

新解决方案:(注意扩展)

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

【讨论】:

  • 这很好(我不关心 perl 或 bash),但我应该通过保留扩展名来应用新名称(为 foo.txt 获取类似 foo_1.txt 的内容,而不是 foo.txt )。文本1)。我正在尝试了解如何执行此操作...
  • @Katie - 以及“只要能完成工作就不管”的良好态度:)
  • @Katie - 将末尾的“copy()”调用替换为“move()”以移动而不是复制
  • 不知道该说什么。这似乎是完美的。非常感谢。
【解决方案3】:

这是一个 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

【讨论】:

  • @Dennis - 很好的接触:评论 'mv' :)
【解决方案4】:

没有测试就发布这个我感觉很糟糕。然而,已经很晚了,我早上有工作。我的尝试看起来像这样:

## 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”形式的名称,直到找到不冲突的名称。

显然我在mv 命令前面加上了echo,因为我是个懦夫。删除它,后果自负(文件)。

【讨论】:

    【解决方案5】:

    如果你不需要增量后缀,rsync 可以完成这项工作:

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

    更新

    find/sed/sort 用于管理版本化备份文件:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-05
      • 2016-08-10
      • 2012-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多