Bulk rename directories with prefix Unix - shell

I am trying to bulk rename directories with a prefix in Unix. Prefix like abc-
So if current directory is 123, I want to make it abc-123, etc
I've tried
for d in $(find . -name '*' -type d) ; do
mv $d $(echo $d | sed 's/$d/abc-$d/g')
done
but that doesn't work. Do very little shell scripting so any help would be appreciated.
rename command is not available
Thank you!

If I understand your question, you could do it with one line and find -exec like so,
find . -type d -depth -execdir mv {} abc-{} \;

Try:
for d in $(find . -depth -type d); do
b=$(basename $d)
p=$(dirname $d)
mv -v $d $p/abc-$b
done
Note that the -depth argument is really important: it ensures that the directories are processed from bottom to top, so that you rename child directories before their parents. If you don't do that then you'll end up trying to rename paths that no longer exist.
Also I recommend replacing line 4 with
echo "mv -v $d $p/abc-$b"
and running that version of the loop first so you can see what it will do before trying it for real.

Related

Find file and rename it BASH

Any idea why this is not working? I checked many links and I can't figure it out why I think the syntax is correct.
I want to find the file maplist.txt.old and then rename it to maplist.txt in the same folder. I got no errors.
find ~ -type f -name csgo/maplist.txt.old -execdir mv csgo/maplist.txt.old maplist.txt \;
Lots of ways to handle this. Since you are looking in ~/csgo you can go directly to the directory in the find. The -execdir option will run the command in the directory. So without changing your example much:
find ~/csgo -type f -name maplist.txt.old -execdir mv maplist.txt.old maplist.txt \;
To automate this a bit further, you may want to handle this with a bash for loop, for example:
for file in $( find ~/csgo -type f -name maplist.txt.old ) ; do
mv $file $( echo $file | sed -e 's/\.old//' )
done

Rename all files in directory and subdirectory

How do I rename files in directory and subdirectory?
I found this program, but I need to go change files in subdirectory.
for file in *#me01
do
mv "$file" "${file/#me01/_me01}"
done
n#me01
to
n_me01
The following one-liner will likely work for you:
find . -type f -name '*#me01' -execdir rename '#me01' '_me01' {} \;
The following form is likely more correct as it will change only the last # to _ if there are multiple occurrences of #me01 in the file:
for f0 in $(find . -type f -name '*#me01')
do
f1=$(printf '%s' "$f0" | sed 's/#me01$/_me01/')
mv "$f0" "$f1"
done
This latter form is also more flexible and can be built upon more easily as the regex language in sed is much more powerful than rename expressions.
If rename of directories is also required the following can easily be added...
Either:
find . -type d -name '*#me01' -execdir rename '#me01' '_me01' {} \;
Or:
for d0 in $(find . -type d -name '*#me01')
do
d1=$(printf '%s' "$d0" | sed 's/#me01$/_me01/')
mv "$d0" "$d1"
done
Using bash:
shopt -s globstar
for name in **/*#me01; do
mv "$name" "${name%#me01}_me01"
done
This enables the globstar shell option in bash which makes ** match across path separators in pathnames.
It also uses a standard parameter substitution to delete the #me01 portion at the very end of the found pathname and replace it with _me01.

Rename files in several subdirectories

I want to rename a file present in several subdirectories using bash script.
my files are in folders:
./FolderA/ABCD/ABCD_Something.ctl
./FolderA/EFGH/EFGH_Something.ctl
./FolderA/WXYZ/WXYZ_Something.ctl
I want to rename all of the .ctl file with the same name (name.ctl).
I tried several command using mv or rename but didnt work.
Working from FolderA:
find . -name '*.ctl' -exec rename *.ctl name.ctl '{}' \;
or
for f in ./*/*.ctl; do mv "$f" "${f/*.ctl/name .ctl}"; done
or
for f in $(find . -type f -name '*.ctl'); do mv $f $(echo "$f" | sed 's/*.ctl/name.ctl/'); done
Can you help me using bash?
thanks
You can do this with one line with:
find . -name *.ctl -exec sh -c 'mv "$1" `dirname "$1"`/name.ctl' x {} \;
The x just allows the filename to be positional character 1 rather than 0 which (in my opinion) wrong to use as a parameter.
Try this:
find . -name '*.ctl' | while read f; do
dn=$(dirname "${f}")
# remove the echo after you sanity check the output
echo mv "${f}" "${dn}/name.ctl"
done
find should get all the files you want, dirname will get just the directory name, and mv will perform the rename. You can remove the quotes if you're sure that you'll never have spaces in the names.

Rename files depending on age

I have a script
for d in $(find /home/users/*/personal/*/docs/MY -type d); do
find $d -maxdepth 1 -type f -amin -10
done
It will list all files in MY directory created in period of 10 minutes in Linux server. I am looking for a way how to do that all these searched files matching criterias will be renamed by adding prefix old_. I mean if script finds files aaaa and bbbb, then renames them to old_aaa and old_bbbb.
Could someone help me?
Let find find all the files for you:
find /home/users/*/personal/*/docs/MY -type f -amin +10 -print0 |
while IFS= read -r -d $'\0' path; do
dir=$(dirname "$path")
name=$(basename "$path")
if [[ $name != old_* ]]; then
echo mv "$path" "$dir/old_$name"
fi
done
Remove the "echo" when you're satisfied it's working for you.
If you don't want old_old_old_old_... files you should try the following:
find "$d" -maxdepth 1 -type f -amin -10 -exec grep -q -v "old_" {} \; -exec mv "{}" "old_{}" \;
PS: I woul'd have commented the previous post but I am not allowed to.
Do like follows:
for d in $(find /home/users/*/personal/*/docs/MY -type d); do
find $d -maxdepth 1 -type f -amin -10 | { read bn; basename "$bn"; } | rename "s/()/old_\$1/"
done
Also take in mind that -amin -10 refers to files being accesed in the last 10 minutes, that includes files created in last 10 minutes but could contain other files as well.
Update:
An alternative way, which is probably faster than above one, because it uses mv command to rename and so it doesn't involve with regex, is the following:
for d in $(find /home/users/*/personal/*/docs/MY -type d); do
find $d -maxdepth 1 -type f -amin -10 | { read bn; basename "$bn"; } | { read fn; mv "$fn" "old_$fn"; }
done
Update 2:
I've updated my two commands above to fix the errors that OP noted in comments.
Use the option -exec:
find "$d" -maxdepth 1 -type f -amin -10 -exec mv "{}" "old_{}" \;
Comments:
Use quotes to make sure spaces don't matter. For the loop above, you should use
find "/home/users/"*"/personal/"*"/docs/MY" -type d
This isn't strictly necessary but it's good to turn this into a habit so you don't forget it when it matters.
Also note that the * must be outside the quotes.
The quotes around {} protect the special characters so the find command sees them (otherwise, the shell would try to interpret them). For the same reason, there is a \ before the ;
[EDIT] You may want to add a \! -name "old_*" to that which means "don't rename files that already start with _old" to the find.

How to copy the latest file modified with in a given date using unix shell commands?

I have to write a shell script that should copy the latest file in to a target directory. I use following shell command.
find . -type f -daystart -mtime -$dateoffset
It gives me the latest file set. But i need to get the latest file from that list and copy it to a target directory.
Thanks.
I can't think of a way to do this in Bourne shell, since you need to use a tool that actually reads datestamps and sorts them, and Bourne shell doesn't do that.
But here's a solution in PHP:
<?php
$fdate=array();
foreach(glob("*") as $filename)
$fdate[filemtime($filename)]=$filename;
krsort($fdate);
print "Newest item: " . reset($fdate) . "\n";'
?>
And if you hapen to be using bash instead of Bourne, he's a round-about way of getting what you want using an associative array:
#!/usr/local/bin/bash
declare -A fdate
highest=0
for file in *; do
timestamp=$(stat -f '%m' "$file")
fdate[$timestamp]="$file"
if [ "$timestamp" -gt "$highest" ]; then
highest=$timestamp
fi
done
printf "Newest file: %s\n" "${fdate[$highest]}"
Note that I'm using FreeBSD, so this solution will also work in OSX, but if you happen to be using Linux, you'll need to figure out how your implementation of the stat command differs from mine. (Hint: you may be able to use stat -c '%y', but man stat to be sure. Solaris, HP/UX, OSF/1, etc do not seem to include a stat binary that can be called from your shell.)
Update: #ghoti's neat solution is recommended over this one. The following has been proved nonrobust. It is left here only because, as a partial answer, it might point the way toward a better one-line solution.
ls -1dt $(find . -type f -daystart -mtime -$dateoffset) | head -n1
To copy the file to $TARGET_DIR,
A=$(ls -1dt $(find . -type f -daystart -mtime -$dateoffset) | head -n1)
if [ -n "$A" ] cp -u "$A" "$TARGET_DIR/$(basename $A)"
find . -name "*" -type f -daystart -mtime -$dateoffset | xargs -i mv {} /where/to/put/files
or
mv `find . -name "*" -type f -daystart -mtime -$dateoffset` /where/to/put/files
If you use ls command with -lt option then it will give you newest file at top.
So using this you can easily extract latest file name
You can use something like this:
find . -type f -name "*" -mtime +x_NUMBER_OF_DAYS|ls -lrt|awk -F' ' '{print $(COLUMN_NUMBER_IN_WHICH_FILE_NAME_APPEARS)}'|tail -1
This will give you the latest file till a given date.
As somebody suggested above daystart is only present in GNU flavor of find while -mtime is a more generic command.
P.S.: This again suffers from parsing problem if file name has space in it.But till we come-up with something more creative you can use this!

Resources