ПРИВЕТ, я абсолютно плохо знаком с Bash и StackOverflow.
Я должен переместить ряд файлов (все содержавшиеся в той же папке) к целевой папке, где файлы с тем же именем могли уже существовать.
В случае, если определенный файл существует, я должен переименовать файл прежде, чем переместить его путем добавления, например, возрастающего целого числа к имени файла.
Расширения должны быть сохранены (другими словами, что добавленное возрастающее целое число должно пойти перед расширением). Имена файлов могли содержать точки в середине.
Первоначально, я думал о сравнении этих двух папок иметь список существующих файлов (я сделал это с "коммуникацией"), но затем я вовлек немного. Я думаю, что просто пытаюсь сделать вещи самым сложным способом.
Какая-либо подсказка, чтобы сделать это в "ударе путь"? Хорошо, если это сделано в сценарии кроме сценария удара.
Согласно 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
Вот сценарий 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
Я чувствую себя неловко за то, что разместил это, не протестировав. Однако уже поздно, а утром у меня работа. Моя попытка выглядела бы примерно так:
## 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
, потому что я трус. Удалите это на свой страх и риск.
Если вам не нужен инкрементный суффикс, 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
Если вы не против переименовать уже существующие файлы, GNU mv
имеет - backup
option:
mv --backup=numbered * /some/other/dir