Renaming multiple files in Linux/Unix - bash

I have over a thousand files of similar names in a directory and wish to do a rename. The files are of this format
GW_LGMS01-50160306185154-01375272.CDR
GW_LGMS01-50160306237154-01375272.CDR.00001
GW_LGMS02-50160306133554-02308872.CDR
GW_LGMS02-50160306137554-02308872.CDR.00014
GW_LGMS03-50160306221836-02217475.CDR.00001
GW_LGMS03-50160306235132-02217475.CDR
I want to do a rename on all of them at once to append a 0- before 50160306 on all of them. That is,
GW_LGMS01-0-50160306185154-01375272.CDR
GW_LGMS01-0-50160306237154-01375272.CDR.00001
GW_LGMS02-0-50160306133554-02308872.CDR
GW_LGMS02-0-50160306137554-02308872.CDR.00014
GW_LGMS03-0-50160306221836-02217475.CDR.00001
GW_LGMS03-0-50160306235132-02217475.CDR
50160306 is what all the files have in common.

Assuming that -50160306 is unique in the file names, and that you are using a shell that understands ${parameter/pattern/string} (Bash, KornShell, etc.):
for f in *.CDR*; do
echo mv "$f" "${f/-50160306/-0-50160306}"
done
Do this with the echo in place to see what would happen, then remove the echo when you are sure it does the right thing.
If you are afraid to mess up, just put the files with the new names in a new folder:
mkdir renamed
for f in *.CDR*; do
cp "$f" renamed/"${f/-50160306/-0-50160306}"
done

If you don't use bash:
#!/bin/sh
for i in * ; do
mv "$i" "$(printf '%s' "$i" | sed 's/\(50160306.*\)/0-\1/')"
done

There are two rename tools floating around: one is part of the util-linux package, the other is Perl based (see this answer for details). To find out which one you have, check at the end of the man page (man rename).
With the util-linux version, you can rename your files as follows:
rename 50160306 0-50160306 *
and for the Perl based version, it would be (untested!)
rename 's/50160306/0-$&/' *
Be aware that there are no safeguards with these commands – test them on a small sample before you use them.

Related

Rename several gopro files

I have video files with this structure : GX**#### where #### is the number of the video.
But sometimes videos are splitted in 2 or 3 files, for instance : GX01#### and GX02####
My problem is that to organise my folders I prefer rename them like this : GX####-1 and GX####-2.
So i ask you the question if I can create a script/command to rename automatically my files to do this ? I can use .bat windows files or .sh linux files. My main problem is that i don't know a command to rename files simply (i saw many solutions which rewrite the entire file). May be if you know just this I will can do my script.
Thanks you in advance.
You can loop through the files and use PE parameter expansion to slice and jumble the strings and mv to rename the files.
Here is an example, first let us create an empty files using touch, but first we need to create a new directory (folder as you call it) and go into that newly created directory, using mkdir and cd.
mkdir newdirectory && cd newdirectory
Now create the empty files using touch.
touch GX01#### GX02#### GX03#### GX04####
Now check the empty files using ls
ls *
The output should be like
GX01#### GX02#### GX03#### GX04####
Now that we have created empty files, we can now do the renaming part.
for file in GX*; do
file1="${file#*??}"
file2=${file1#*??}
file3=${file1%*$file2}
echo mv -v "$file" "${file%*$file1}${file2}"-"$file3"
done
The output should be on stdout (which is your screen) because of the echo.
mv -v GX01#### GX####-01
mv -v GX02#### GX####-02
mv -v GX03#### GX####-03
mv -v GX04#### GX####-04
If you're satisfied with what you see then remove the echo, so mv can actually rename the files. To show how the slicing is done here is an example.
file=GX01####
file1="${file#*??}"
file2=${file1#*??}
file3="${file1%*$file2}"
printf '%s\n' "$file1" "$file2" "$file3"
The output should be something like.
01####
####
01
The ? means a single string from the shell.
A word of caution and advice, make a backup of the files you want to edit just in case the shell made a mistake while you're renaming the files. :-)
Also you should probably use shell globing like nullglob see
http://mywiki.wooledge.org/glob#nullglob
See man bash and look for Parameter Expansion.
PAGER='less +/^[[:space:]]*parameter\ expansion' man bash
Some online resources, with detailed explanation and examples.
http://mywiki.wooledge.org/BashFAQ/073
https://wiki.bash-hackers.org/syntax/pe
You could echo the original and new file names:
for f in GX*; do
echo "$f" $(sed 's/^GX\(..\)\(.*\)/GX\2-\1/' <<< "$f")
done
which should output:
GX01#### GX####-01
GX02#### GX####-02
then use mv -n instead of echo to rename the files.

How to remove unknown file extensions from files using script

I can remove file extensions if I know the extensions, for example to remove .txt from files:
foreach file (`find . -type f`)
mv $file `basename $file .txt`
end
However if I don't know what kind of file extension to begin with, how would I do this?
I tried:
foreach file (`find . -type f`)
mv $file `basename $file .*`
end
but it wouldn't work.
What shell is this? At least in bash you can do:
find . -type f | while read -r; do
mv -- "$REPLY" "${REPLY%.*}"
done
(The usual caveats apply: This doesn't handle files whose name contains newlines.)
You can use sed to compute base file name.
foreach file (`find . -type f`)
mv $file `echo $file | sed -e 's/^\(.*\)\.[^.]\+$/\1/'`
end
Be cautious: The command you seek to run could cause loss of data!
If you don't think your file names contain newlines or double quotes, then you could use:
find . -type f -name '?*.*' |
sed 's/\(.*\)\.[^.]*$/mv "&" "\1"/' |
sh
This generates your list of files (making sure that the names contain at least one character plus a .), runs each file name through the sed script to convert it into an mv command by effectively removing the material from the last . onwards, and then running the stream of commands through a shell.
Clearly, you test this first by omitting the | sh part. Consider running it with | sh -x to get a trace of what the shell's doing. Consider making sure you capture the output of the shell, standard output and standard error, into a log file so you've got a record of the damage that occurred.
Do make sure you've got a backup of the original set of files before you start playing with this. It need only be a tar file stored in a different part of the directory hierarchy, and you can remove it as soon as you're happy with the results.
You can choose any shell; this doesn't rely on any shell constructs except pipes and single quotes and double quotes (pretty much common to all shells), and the sed script is version neutral too.
Note that if you have files xyz.c and xyz.h before you run this, you'll only have a file xyz afterwards (and what it contains depends on the order in which the files are processed, which needn't be alphabetic order).
If you think your file names might contain double quotes (but not single quotes), you can play with the changing the quotes in the sed script. If you might have to deal with both, you need a more complex sed script. If you need to deal with newlines in file names, then it is time to (a) tell your user(s) to stop being silly and (b) fix the names so they don't contain newlines. Then you can use the script above. If that isn't feasible, you have to work a lot harder to get the job done accurately — you probably need to make sure you've got a find that supports -print0, a sed that supports -z and an xargs that supports -0 (installing the most recent GNU versions if you don't already have the right support in place).
It's very simple:
$ set filename=/home/foo/bar.dat
$ echo ${filename:r}
/home/foo/bar
See more in man tcsh, in "History substitution":
r
Remove a filename extension '.xxx', leaving the root name.

How to add leading zero's to sequential file names

I have images files that when they are created have these kind of file names:
Name of file-1.jpg
Name of file-2.jpg
Name of file-3.jpg
Name of file-4.jpg
..etc
This causes problems for sorting between Windows and Cygwin Bash. When I process these files in Cygwin Bash, they get processed out of order because of the differences in sorting between Windows file system and Cygwin Bash sees them. However, if the files get manually renamed and numbered with leading zeroes, this issue isn't a problem. How can I use Bash to rename these files automatically so I don't have to manually process them. I'd like to add a few lines of code to my Bash script to rename them and add the leading zeroes before they are processed by the rest of the script.
Since I use this Bash script interchangeably between Windows Cygwin and Mac, I would like something that works in both environments, if possible. Also all files will have names with spaces.
You could use something like this:
files="*.jpg"
regex="(.*-)(.*)(\.jpg)"
for f in $files
do
if [[ "$f" =~ $regex ]]
then
number=`printf %03d ${BASH_REMATCH[2]}`
name="${BASH_REMATCH[1]}${number}${BASH_REMATCH[3]}"
mv "$f" "${name}"
fi
done
Put that in a script, like rename.sh and run that in the folder where you want to covert the files. Modify as necessary...
Shamelessly ripped from here:
Capturing Groups From a Grep RegEx
and here:
How to Add Leading Zeros to Sequential File Names
#!/bin/bash
#cygcheck (cygwin) 2.3.1
#GNU bash, version 4.3.42(4)-release (i686-pc-cygwin)
namemodify()
{
bname="${1##*/}"
dname="${1%/*}"
mv "$1" "${dname}/00${bname}" # Add any number of leading zeroes.
}
export -f namemodify
find . -type f -iname "*jpg" -exec bash -c 'namemodify "$1"' _ {} \;
I hope this won't break on Mac too :) good luck

automatically renaming files

I have a bunch of files (more than 1000) on this like the followings
$ ls
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm-dev.lc
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm-dev.lex
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm-train.lc
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm-train.lex
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm.lc
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm.lex
org.allenai.ari.solvers.termselector.ExpandedLearner.lc
org.allenai.ari.solvers.termselector.ExpandedLearner.lex
org.allenai.ari.solvers.termselector.ExpandedLearnerSVM.lc
org.allenai.ari.solvers.termselector.ExpandedLearnerSVM.lex
....
I have to rename these files files by adding a learners right before the capitalized name. For example
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm.lex
would change to
org.allenai.ari.solvers.termselector.learners.BaselineLearnersurfaceForm.lex
and this one
org.allenai.ari.solvers.termselector.ExpandedLearner.lc
would change to
org.allenai.ari.solvers.termselector.learners.ExpandedLearner.lc
Any ideas how to do this automatically?
for f in org.*; do
echo mv "$f" "$( sed 's/\.\([A-Z]\)/.learner.\1/' <<< "$f" )"
done
This short loop outputs an mv command that renames the files in the manner that you wanted. Run it as-is first, and when you are certain it's doing what you want, remove the echo and run again.
The sed bit in the middle takes a filename ($f, via a here-string, so this requires bash) and replaces the first occurrence of a capital letter after a dot with .learner. followed by that same capital letter.
There is a tool called perl-rename, sometimes rename. Not to be confused with rename from util-linux.
It's very good for tasks like this as it takes a perl expression and renames accordingly:
perl-rename 's/(?=\.[A-Z])/.learners/' *
You can play with the regex online
Alternative you can a for loop and $BASH_REMATCH:
for file in *; do
[ -e "$file" ] || continue
[[ "$file" =~ ^([^A-Z]*)(.*)$ ]]
mv -- "$file" "${BASH_REMATCH[1]}learners.${BASH_REMATCH[2]}"
done
A very simple approach (useful if you only need to do this one time) is to ls >dummy them into a text file dummy, and then use find/replace in a text editor to make lines of the form mv xxx.yyy xxx.learners.yyy. Then you can simple execute the resulting file with ./dummy.
The exact find/replace commands depend on the text editor you use, but something like
replace org. with mv org.. That gets you the mv in the beginning.
replace mv org.allenai.ari.solvers.termselector.$1 with mv org.allenai.ari.solvers.termselector.$1 org.allenai.ari.solvers.termselector.learner.$1 to duplicate the filename and insert the learner.
There is also syntax with a for, which can do it probably in one line, (long) but I cannot explain it - try help for if you want to learn about it.

Copying unique files to a new location

I am a total newbie to Linux and bash scripting and am currently stumped with this problem!
I have a directory containing many images from which I need to copy the unique images to a new location. I know there are numerous options for how to go about doing this but have very limited knowledge at the moment so appreciate I may be going about this the wrong way.
I used find and cat to create this list and have attempted to copy the files across with the intention of comparing them (using md5 and checking file names) when they are there.
However, the text file has 30 files on it but only 18 have been copied over. Can anyone advise?
My code to find files is -
find $1 -name "IMG_****.JPG" | cat > list.txt
and my code to copy from the list is
for image in $(cat list.txt);
do
cp $image $2
done
You're doing this much too complicated. Do not pipe find output to cat to pipe it into a list. This is an unnecessary use of cat. If you must, you can redirect the output of every program directly:
find "$1" -name "IMG_*.JPG" > list.txt
Also, do not use for to read lines from a file. Better use while with read:
while read -r filename; do
cp "$filename" "$2"
done < list.txt
But it's even easier. You can just work with the files directly from find:
find "$1" -name "IMG_*.JPG" -exec cp {} "$2" \;
Here, {} will be replaced by each filename that find finds. Don't forget to quote your variables, so that spaces in file paths are no problem.
Another much simpler method with Bash options:
shopt -s nullglob globstar
cp -t "$2" -- "$1"/**/IMG_*.JPG
Here, globstar enables recursive matching of directories through **. The -t option to cp specifies the target of the copy operation.* The command will be expanded to cp -t target -- source1/IMG_foo.JPG source2/IMG_bar.JPG et cetera.
Now, as to your original issue, it could have been that some images have a space in their name. This would have broken your original script. If your image files contained a newline in their name, it also wouldn't have worked with while read … – but you would have gotten an error in that case of a file not being found.
Also note that cp overwrites files with the same name. Without asking for confirmation. So if in your subdirectories there are images with the same filename, you'd only get one result, with the latest overwriting the existing one.
* The -- isn't strictly necessary, but it's a good habit to include it to tell the command when the options arguments are over.

Resources