Batch mv or rename in bash script - append date as a suffix - bash

After much searching and trial and error, I'm unable to do a batch mv or rename on a directory of files. What I'd like to do is move or rename all files in a directory so that the mv'd or renamed file has $date (+ '%Y%d%m') added to the original suffix.
All the original files have unique prefixes but are either .xml or .txt so I'd like to go from org_prefix.org_suffix -> org_prefix.org_suffix.DATE
I've tried this:
$ mv /directory/* /directory/*$(date (+ '%Y%m%d')
but always get /directory/*.actualdate' is not a directory error.
I've tried this:
$ for f in *; do mv $ $f.$(date +'_%m%d%y'); done
but I get mv: cannot stat '$'; No such file or directory
Lastly, I've even tried this:
$ rename 's/*/.test/' *
just to see if I could change all the files to org_prefix.test but nothing happens (no errors, nada, zip)
Any help greatly appreciated.

The proper way to loop through files (and e.g., print their name) in the current directory is:
for file in *; do
echo "$file"
done
How will you append the date? like so, of course:
for file in *; do
echo "$file.$(date +%Y%m%d)"
done
And how are you going to do the move? like so, of course:
for file in *; do
mv -nv -- "$file" "$file.$(date +%Y%m%d)"
done
I've added:
-v so that mv be verbose (I like to know what's happening and it always impresses my little sister to watch all these lines flowing on the screen).
-n so as to no overwrite an otherwise existing file. Safety first.
-- just in case a file name starts with a hyphen: without --, mv would confuse with an option. Safety first.
If you just want to look through the files with extension .banana, replace the for with:
for file in *.banana; do
of for files that contain the word banana:
for file in *banana*; do
and so on.
Keep up with the bananas!

$ mv /directory/* /directory/*$(date (+ '%Y%m%d')
This does not work, because the * is expanded to a list of all files, so after the expansion the command will be something like:
mv /directory/file1 /directory/file2 /directory/file3 /directory/file1_date /directory/file1_date ...
So you have specified many destinations, but the syntax for mv allows only one single destination.
for f in *; do mv $ $f.$(date +'_%m%d%y'); done
Here you forgot the f after the $, that's why you get the error message.
for f in *; do mv $f $f.$(date +'%m%d%y'); done
I think this should work now, but don't forget to quote all the variables!
Finally:
for f in *; do mv "$f" "$f.$(date +'%m%d%y')"; done
Edit: When there are characters directly after a variable, it's good practice to use {} to make clear that they are not part of the variable name:
for f in *; do mv "$f" "${f}.$(date +'%m%d%y')"; done

Related

Rename a file when I don't know its name - shell script

I am trying to rename a file which is the only file in a directory.
It is a podcast download which changes name each day so I don't know what it is called but it always ends in .MP3
I want to rename it to news.mp3
I have tried the following based on another solution on this site but it appends the news to the file
#!/bin/sh
for file in *.MP3; do
mv "$file" "${file/.MP3/news.mp3}"
done
If it's the only file in the directory you can just write the following command:
mv directory_name/* directory_name/news.mp3
In case there are few files or if dir is empty:
shopt -s nullglob
src="/path/to/dir/with/files"
dst="/destanation/folder"
i=1
cd "$src"
for f in *; do
mv "$f" "$dst/new_name_$((i++))"
done

How to rename all files in a directory from $filename into $Backupfilename

I have a folder called, evaluation, located in, /home/evaluation, which contains this :
Geography
Math
History
English
How do I rename every file inside of this folder, into something like this :
BackupGeography
BackupMath
BackupHistory
BackupEnglish
Here is what I have tried so far :
for file in /home/evaluation/*
do
mv "$file" "${file//Backup}"
done
But it doesn't work unfortunately.
Try this:
for file in /home/evaluation/*
do
mv -i "$file" "$(dirname "$file")/Backup$(basename "$file")"
done
Explanation: dirname gets the directory name (e.g. dirname /home/evaluation/Geography prints "/home/evaluation"), and basename gets the file name without the path (e.g. dirname /home/evaluation/Geography prints "Geography"). So
... "$(dirname "$file")/Backup$(basename "$file")"
--> "/home/evaluation" + "/Backup" + "Geography"
--> "/home/evaluation/BackupGeography"
(Note: + is not a shell operator. At least, not like this. I'm just illustrating how it's parsed.)
Oh, and mv -i will ask what to do if there's a name conflict. Without the -i, it'll silently and irreversibly delete one of the conflicting files. Using mv for bulk moves without -i (or -n) always makes me nervous.
You can do
for file in /home/evaluation/*
do
mv "$file" `dirname "$file"`/Backup`basename "$file"`
done

How to rename folders that contain consistent sub-string at the end using mv command?

I have many folders with the following pattern:
$ ls
a-master 123-master abc123-master
I want them to be:
$ ls
a 123 abc123
During my research, I found this answer. But when I ran mv "$f" "${f/-master/}", I got mv: rename to : No such file or directory error and I'm not sure why.
I found many answers recommending rename package but I don't prefer it. I think it's possible to be done just by using mv command but I'm not entirely sure how.
Does is possible? If possible, what is the correct command for this case?
I'm looking for one liner command, a short and sweet one.
I am fetching details from the shared link-
for f in opencv_*; do mv "$f" "${f/.so/}"; done
The mv command is working on a loop where f is defined as a temporary variable, whereas the query posted does not have a variable. (f == "")
Example,
mkdir abc123-master
export F=abc123-master
mv $F ${F/-master/}
ls -rlt
abc123

How to move files from subfolders to their parent directory (unix, terminal)

I have a folder structure like this:
A big parent folder named Photos. This folder contains 900+ subfolders named a_000, a_001, a_002 etc.
Each of those subfolders contain more subfolders, named dir_001, dir_002 etc. And each of those subfolders contain lots of pictures (with unique names).
I want to move all these pictures contained in the subdirectories of a_xxx inside a_xxx. (where xxx could be 001, 002 etc)
After looking in similar questions around, this is the closest solution I came up with:
for file in *; do
if [ -d $file ]; then
cd $file; mv * ./; cd ..;
fi
done
Another solution I got is doing a bash script:
#!/bin/bash
dir1="/path/to/photos/"
subs= `ls $dir1`
for i in $subs; do
mv $dir1/$i/*/* $dir1/$i/
done
Still, I'm missing something, can you help?
(Then it would be nice to discard the empty dir_yyy, but not much of a problem at the moment)
You could try the following bash script :
#!/bin/bash
#needed in case we have empty folders
shopt -s nullglob
#we must write the full path here (no ~ character)
target="/path/to/photos"
#we use a glob to list the folders. parsing the output of ls is baaaaaaaddd !!!!
#for every folder in our photo folder ...
for dir in "$target"/*/
do
#we list the subdirectories ...
for sub in "$dir"/*/
do
#and we move the content of the subdirectories to the parent
mv "$sub"/* "$dir"
#if you want to remove subdirectories once the copy is done, uncoment the next line
#rm -r "$sub"
done
done
Here is why you don't parse ls in bash
Make sure the directory where the files exist is correct (and complete) in the following script and try it:
#!/bin/bash
BigParentDir=Photos
for subdir in "$BigParentDir"/*/; do # Select the a_001, a_002 subdirs
for ssdir in "$subdir"/*/; do # Select dir_001, … sub-subdirs
for f in "$ssdir"/*; do # Select the files to move
if [[ -f $f ]]; do # if indeed are files
echo \
mv "$ssdir"/* "$subdir"/ # Move the files.
fi
done
done
done
No file will be moved, just printed. If you are sure the script does what you want, comment the echo line and run it "for real".
You can try this
#!/bin/bash
dir1="/path/to/photos/"
subs= `ls $dir1`
cp /dev/null /tmp/newscript.sh
for i in $subs; do
find $dir1/$i -type f -exec echo mv \'\{\}\' $dir1/$i \; >> /tmp/newscript.sh
done
then open /tmp/newscript.sh with an editor or less and see if looks like what you are trying to do.
if it does then execute it with sh -x /tmp/newscript.sh

prepend filename, not destination path, using mv

In the process of posting this question I've been experimenting with my code, I've come up with something that works, but my restless intellect wants to be a better programmer, not just solve the problem at hand, so I'm posting Script 1.0 and Script 1.1 to ask the community, Why does the change to line 3 make it work?
I'm copying files from a server and renaming them. The copying is going fine; the renaming isn't. These filenames have spaces. cp handles them competently; mv seems throws an error. I expected putting "" around the variable name to resolve the issue, but it still gives an error: mv: rename /src/foo.wav to zzz - /dest/foo.wav - copied 201403261800.wav: No such file or directory. I'm trying to prepend "zzz" to the filename, but it's getting prepended to the destination path. Here's Script 1.0:
cd /src/
DATE=$(date +"%Y%m%d%H%M")
find . -type f -iname "*.wav" | while read file ; do
if [[ "$file" != *zzz* ]]; then
echo "Tranferring...";
cp "$file" /dest/
mv "$file" \zzz\ \-\ "$file"\ \-\ copied\ $DATE.wav
echo "Transfer complete.";
fi
done
(The if condition ensures that I don't re-copy anything I've previously copied and renamed.)
Changing the 3rd line makes it work. But why? Here's Script 1.1:
cd /src/
DATE=$(date +"%Y%m%d%H%M")
find *.* -iname "*.wav" | while read file ; do
if [[ "$file" != *zzz* ]]; then
echo "Tranferring...";
cp "$file" /dest/
mv "$file" \zzz\ \-\ "$file"\ \-\ copied\ $DATE.wav
echo "Transfer complete.";
fi
done
If you look at the output of the different find commands you'll see that find . -type f will return lines like this: ./passwd.
Now you can't use that as the target, as renaming ./passwd to zzz - ./passwd would try to move it to a zzz - . folder as passwd.
By the way, you can surround the whole target name with quotes, to make it look better (also you should not escape the first z anyway):
mv "$file" "zzz - $file - copied $DATE.wav"
Also you probably want to get rid of the .wav part in the middle of the file:
mv "$file" "zzz - ${file%.wav} - copied $DATE.wav"
The ${var%string} returns the value of $var with string removed from the end of it if present. You can find more about this looking for bash string manipulation.

Resources