Rename and move files in Bash or Perl

HI, I am completely new to Bash and StackOverflow.

I need to transfer a set of files (all of them are contained in one folder) to the target folder, where files with the same name may already exist.

If a specific file exists, I need to rename the file before moving it, adding, for example, an incremental integer to the file name.

Extensions must be preserved (in other words, the added incremental integer must be before ). File names may contain dots in the middle.

I initially thought about comparing two folders to have a list of existing files (I did this with "comm"), but then I got a little stuck. I think I'm just trying to do something in the most difficult way.

Any hint of this in bash style? This is normal if done in a script other than a Bash script.

+5
source share
5 answers

According to the OP, it could be Perl, not just bash. Here we go

NEW SOLUTION: (paying attention to the extension)

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

OLD SOLUTION: (ignoring the extension)

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

If you don't mind renaming files that already exist, GNU mvhas the option --backup:

mv --backup=numbered * /some/other/dir
+7
source

Bash script:

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

, . , . :

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

, rsync :

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

find / sed / sort is used to control backup file versions:

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

All Articles