Copy files with renaming
I have a huge file tree. Some files have same name but in different case, e.g., some_code.c and Some_Code.c . So when I’m trying to copy it to an NTFS/FAT filesystem, it asks me about whether I want it to replace the file or skip it. Is there any way to automatically rename such files, for example, by adding (1) to the name of conflict file (as Windows 7 does)?
Curious. I just wanted to see, what kind of error I get if I try to produce a file with the same name on a NTFS-partition (HPFS/NTFS, according to sudo fdisk -l /dev/sda ), and did touch foo; touch Foo and ended with 2 files foo and Foo . But I’m not curious enogh to reboot into Windows, to look how they look like over there. Migth it just be a FAT-problem? Ah — I have an USB-Stick with FAT, and could create a FAT-system inside a file, . — one moment please. 🙂
3 Answers 3
Many GNU tools such as cp , mv and tar support creating backup files when the target exists. That is, when copying foo to bar , if there is already a file called bar , the existing bar will be renamed, and after the copy bar will contain the contents of foo . By default, bar is renamed to bar~ , but the behavior can be modified:
# If a file foo exists in the target, then… cp -r --backup source target # rename foo → foo~ cp -r --backup=t source target # rename foo → foo.~1~ (or foo.~2~, etc)
There are other variants, such as creating numbered backups only when one already exists. See the coreutils manual for more details.
Brilliant. I didn’t know this option existed, and it just proved to be extremely useful. Thanks @Gilles.
to find possible candidates, and mcopy showed up.
shows a promising option -D clash-option isn’t that cool? But not so cool — it isn’t described. But there are some hints to mtools.dvi, which I searched on my system, without success, and via google, without success, but then, with google, I searched directly for mcopy clash-option and found this site.
to tests for autorename and targetdir a — instead of autorenaming it asked me for every file to ignore or override, that stupid s. .
My version is mtools-4.0.10 and the help page is from 1996 — 15 years old. Should we really lost some features, meanwhile?
I would split the work into two steps:
- Make a short function, which generates a unique name for a file, if that name is occupied.
- Run find , and execute that script for every file you wish to copy.
Shall we assist in this approach? 🙂
Here is a script, to autorename files:
#!/bin/bash name=$1 target=$2 autorename () < name=$1 target=$2 no=$3 test -e $/$.$no && autorename $ $ $((no+1)) || cp $ $/$.$no > test -e $/$ && autorename $ $ 0 || cp $ $
and this is my test invocation:
find -maxdepth 1 -name "fo*" -type f -exec ./autorename.sh <> /mnt/hidden/test/a ";"
Note: -maxdepth, -name and -type where used to restrict the number of affected files dramatically. I didn’t test the script for deeper file structures, nor for blanks in filenames and other, funky characters like linefeed, pagefeed and so on.
I used .1 because it doesn’t make trouble in most commands, while a ( and a ) often need masking.
How to find,copy and rename files in linux?
I am trying to find all files in a directory and sub-directories and then copy them to a different directory. However some of them have the same name, so I need to copy the files over and then if there are two files have the same name, rename one of those files. So far I have managed to copy all found files with a unique name over using:
#!/bin/bash if [ ! -e $2 ] ; then mkdir $2 echo "Directory created" fi if [ ! -e $1 ] ; then echo "image source does not exists" fi find $1 -name IMG_****.JPG -exec cp <> $2 \;
However, I now need some sort of if statement to figure out if a file has the same name as another file that has been copied.
One * matches all, you don’t need four of them. If you want to match exactly four characters, you can do . where each ? matches only one character. Also, you need to quote «IMG_. JPG» (like I did) to prevent shell from expanding it. You probably got lucky and there is no IMG_. JPG in the directory you are calling find from, so the pattern is kept intact.
Sure! I recommend reading this page: mywiki.wooledge.org/BashPitfalls it has good information where beginners (myself included) usually get wrong with bash.
4 Answers 4
Since you are on linux, you are probably using cp from coreutils. If that is the case, let it do the backup for you by using cp —backup=t
Try this approach: put the list of files in a variable and copy each file looking if the copy operation succeeds. If not, try a different name.
FILES=`find $1 -name IMG_****.JPG | xargs -r` for FILE in $FILES; do cp -n $FILE destination # Check return error of latest command (i.e. cp) # through the $? variable and, in case # choose a different name for the destination done
Inside the for statement, you can also put some incremental integer to try different names incrementally (e.g., name_1, name_2 and so on, until the cp command succeeds).
You are right, -n is —no-clobber . I confused it with the flag from other commands. The answer is fine now.
Sorry, I should have mentioned in the question that I am only learning linux for the first time, so could you explain what the parts of your code do please? sorry.
for file in $1/**/IMG_*.jpg ; do target=$2/$(basename "$file") SUFF=0 while [[ -f "$target$SUFF" ]] ; do (( SUFF++ )) done cp "$file" "$target$SUFF" done
in your script in place of the find command to append integer suffixes to identically-named files
You can use rsync with the following switches for more control
rsync —backup —backup-dir=DIR —suffix=SUFFIX -az
-b, —backup
With this option, preexisting destination files are renamed as each file is transferred or deleted. You can control where the backup file goes and what (if any) suffix gets appended using the —backup-dir and —suffix options.
—backup-dir=DIR
In combination with the —backup option, this tells rsync to store all backups in the specified directory on the receiving side. This can be used for incremental backups. You can additionally specify a backup suffix using the —suffix option (otherwise the files backed up in the specified directory will keep their original filenames).
—suffix=SUFFIX
This option allows you to override the default backup suffix used with the —backup (-b) option. The default suffix is a ~ if no —backup-dir was specified, otherwise it is an empty string.
You can use rsycn to either sync two folders on local file system or on a remote file system. You can even do syncing over ssh connection.
rsync is amazingly powerful. See the man page for all the options.
How to Copy File and Rename Linux
Sometimes you may need to copy files from one location to another while also renaming them. This can be useful for various reasons, such as creating a backup, duplicating a configuration file for editing, or generating a new version of a file with a different name. The easiest way to accomplish this task is by using the cp command in Linux.
It is fairly straightforward. By default, the cp command copies the file specified in the first argument to the location of the second argument. For instance, let’s copy the /etc/group file into your home directory and renaming it as group_back:
sudo cp /etc/group ~/group_back
In this case, the command includes sudo to grant administrative privileges. This may be necessary for some files, like the group configuration file in the /etc/ directory, which are protected and require elevated permissions for access or modification.
When executed, the command will copy the group configuration file from the /etc/ directory to your home folder, renaming it as group_back.
Found a mistake in the text? Let me know about that. Highlight the text with the mistake and press Ctrl+Enter.
How to copy multiple files and rename them at once by appending a string in between the file names in Unix?
I have a few files that I want to copy and rename with the new file names generated by adding a fixed string to each of them. E.g:
ls -ltr | tail -3 games.txt files.sh system.pl
games_my.txt files_my.sh system_my.pl
for i in `ls -ltr | tail -10`; do cp $i `echo $i\_my`;done
2 Answers 2
You can use the following:
for file in * do name="$" extension="$" cp $file $_my$ done
Note that $ returns the file name without extension, so that from hello.txt you get hello . By doing $_my.txt you then get from hello.txt -> hello_my.txt .
Regarding the extension, extension=»$» gets it. It is based on the question Extract filename and extension in bash.
Sorry, I forgot to mention that the file names are of different extensions and I want to retain the respective extension of each file with the rename.
Thanks. It works. But I don’t exactly get what file%.* and file##*. are doing? What does % and ## do in this case?
$ removes everything before last . , so that you get the extension. $ returns everything before last . , so that you get the file name without extension. You can check gnu.org/software/bash/manual/… for further reference.
If the shell variable expansion mechanisms provided by fedorqui’s answer look too unreadable to you, you also can use the unix tool basename with a second argument to strip off the suffix:
for file in *.txt do cp -i "$file" "$(basename "$file" .txt)_my.txt" done
Btw, in such cases I always propose to apply the -i option for cp to prevent any unwanted overwrites due to typing errors or similar.
It’s also possible to use a direct replacement with shell methods: