Переименование и движущиеся файлы в Bash или Perl

ПРИВЕТ, я абсолютно плохо знаком с Bash и StackOverflow.

Я должен переместить ряд файлов (все содержавшиеся в той же папке) к целевой папке, где файлы с тем же именем могли уже существовать.

В случае, если определенный файл существует, я должен переименовать файл прежде, чем переместить его путем добавления, например, возрастающего целого числа к имени файла.

Расширения должны быть сохранены (другими словами, что добавленное возрастающее целое число должно пойти перед расширением). Имена файлов могли содержать точки в середине.

Первоначально, я думал о сравнении этих двух папок иметь список существующих файлов (я сделал это с "коммуникацией"), но затем я вовлек немного. Я думаю, что просто пытаюсь сделать вещи самым сложным способом.

Какая-либо подсказка, чтобы сделать это в "ударе путь"? Хорошо, если это сделано в сценарии кроме сценария удара.

5
задан DVK 31 March 2010 в 17:09
поделиться

5 ответов

Согласно 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
3
ответ дан 18 December 2019 в 13:12
поделиться

Вот сценарий 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
4
ответ дан 18 December 2019 в 13:12
поделиться

Я чувствую себя неловко за то, что разместил это, не протестировав. Однако уже поздно, а утром у меня работа. Моя попытка выглядела бы примерно так:

## 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, потому что я трус. Удалите это на свой страх и риск.

0
ответ дан 18 December 2019 в 13:12
поделиться

Если вам не нужен инкрементный суффикс, 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
0
ответ дан 18 December 2019 в 13:12
поделиться

Если вы не против переименовать уже существующие файлы, GNU mv имеет - backup option:

mv --backup=numbered * /some/other/dir
7
ответ дан 18 December 2019 в 13:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: