How to change file and directory names with find? - linux

I changed project name and now I have many files an directories with old name. How to replace these names with find?
find . -name "*old_name*" -exec ???

This find should work for you:
find . -name "old_name" -execdir mv "{}" new_name +
This will find files with the name old_name from the current dir in all sub directories and rename them to new_name.

Below is what I have used in the past. The biggest gotcha is the RHEL rename (c) vs Debian rename (perl) - They take different options. The example below uses RHEL c based rename command. Remove the '-type f' to also rename the directories.
find . -type f -name "*old_name*" -print0 | xargs -0 -I {} /usr/bin/rename "old_name" "new_name" {}

newname="myfile.sh"; for files in $(find Doc2/scripts/ -name gw_watch_err.sh); do echo $files; dir=${files%/*}; cfile=${files##*/}; echo "$dir -- $cfile"; echo "mv $cfile $newname"; done
Doc2/scripts/gateway/gw_watch_err.sh
Doc2/scripts/gateway -- gw_watch_err.sh
mv gw_watch_err.sh myfile.sh
you could also add:
find . -maxdepth 1 -iname file
where maxdepth will ensure you dont need to worry about sub folders and iname means case sensitive

Ok, my solution:
find . -name "*old_name*" -exec rename 's/old_name/new_name/g' {} \;
But this works for directories which name not contain "old_name", otherwise find say for example:
find: `./old_name': No such file or directory
Because it trying search in "old_name" directory, and the directory is already a "new_name"

renaming multiple directories with find: how to catch the catches
here's a little story on how to rename multiple files with find, but for the impatient I first put the proper command for your specific problem here:
find -depth -name "old name" -execdir mv -iv {} "new name" \;
and for the patient ones, a few misadventures with find, restricting changes to directories only:
we want to rename all directories named old dir into new dir recursively inside a current directory
we create an empty directory inside which we create a simple directory hierarchy:
$ cd $(mktemp -d) && mkdir -p "old dir" "subdir/old dir/old dir"
$ find | sort
.
./old dir
./subdir
./subdir/old dir
./subdir/old dir/old dir
the use of of the -exec action works only for a rename directly under a current directory, which you can see by dry-running a rename command (note that the shell doesn't output the quotes, which is not an error):
$ find -type d -name "old dir" -exec echo mv -iv {} "new dir" \;
mv -iv ./subdir/old dir new dir
mv -iv ./subdir/old dir/old dir new dir
mv -iv ./old dir new dir
note that only the first rename command would work as expected
the use of the -execdir action runs the command from the subdirectory containing the matched directory:
$ find -type d -name "old dir" -execdir echo mv -iv {} "new dir" \;
mv -iv ./old dir new dir
mv -iv ./old dir new dir
mv -iv ./old dir new dir
which seems fine as the rename commands are run in the matching directories, so we no longer dry-run:
$ find -type d -name "old dir" -execdir mv -iv {} "new dir" \;
'./old dir' -> 'new dir'
find: ‘./subdir/old dir’: No such file or directory
'./old dir' -> 'new dir'
find: ‘./old dir’: No such file or directory
$ find | sort
.
./new dir
./subdir
./subdir/new dir
./subdir/new dir/old dir
the problem is that the old dir is renamed new dir and find cannot descend further inside a renamed directory
a solution is to process each directory's contents before the directory itself, which is precisely what the -depth option does:
$ cd $(mktemp -d) && mkdir -p "old dir" "subdir/old dir/old dir"
$ find -depth -type d -name "old dir" -execdir mv -iv {} "new dir" \;
'./old dir' -> 'new dir'
'./old dir' -> 'new dir'
'./old dir' -> 'new dir'
$ find | sort
.
./new dir
./subdir
./subdir/new dir
./subdir/new dir/new dir

1. First, backup your directories and files
The following Bash code run on my OS X and Ubuntu boxes.
2. Rename the directories from old_dir to new_dir:
for d in $(find . -maxdepath X -type d -name 'old_dir'); do mv $d "$(dirname $d)/new_dir"; done
X is a number used to specify the depth of replacing old_dir
3. Rename the files from old_file to new_file:
for f in $(find . -type f -name 'old_file'); do mv $f "$(dirname $f)/new_file"; done
Don't care about #Benjamin W. and #ghoti them one for his ForMatted code and #ghoti try to orient the question to his Pitfalls.
Hi, #Benjamin W. what about this new post? and #ghoti did you run the above code incorrectly on your machine? If the code can't work just let me know or post a question pls, and if you had a better one pls post here let we know.

Related

Moving files with a pattern in their name to a folder with the same pattern as its name

My directory contains mix of hundreds of files and directories similar to this:
508471/
ae_lstm__ts_ 508471_detected_anomalies.pdf
ae_lstm__508471_prediction_result.pdf
mlp_508471_prediction_result.pdf
mlp__ts_508471_detected_anomalies.pdf
vanilla_lstm_508471_prediction_result.pdf
vanilla_lstm_ts_508471_detected_anomalies.pdf
598690/
ae_lstm__ts_598690_detected_anomalies.pdf
ae_lstm__598690_prediction_result.pdf
mlp_598690_prediction_result.pdf
mlp__ts_598690_detected_anomalies.pdf
vanilla_lstm_598690_prediction_result.pdf
vanilla_lstm_ts_598690_detected_anomalies.pdf
There are folders with an ID number as their names, like 508471 and 598690.
In the same path as these folders, there are pdf files that have this ID number as part of their name. I need to move all the pdf files with the same ID in their name, to their related directories.
I tried the following shell script but it doesn't do anything. What am I doing wrong?
I'm trying to loop over all the directories, find the files that have id in their name, and move them to the same dir:
for f in ls -d */; do
id=${f%?} # f value is '598690/', I'm removing the last character, `\`, to get only the id part
find . -maxdepth 1 -type f -iname *.pdf -exec grep $id {} \; -exec mv -i {} $f \;
done
#!/bin/sh
find . -mindepth 1 -maxdepth 1 -type d -exec sh -c '
for d in "$#"; do
id=${d#./}
for file in *"$id"*.pdf; do
[ -f "$file" ] && mv -- "$file" "$d"
done
done
' findshell {} +
This finds every directory inside the current one (finding, for example, ./598690). Then, it removes ./ from the relative path and selects each file that contains the resulting id (598690), moving it to the corresponding directory.
If you are unsure of what this will do, put an echo between && and mv, it will list the mv actions the script would make.
And remember, do not parse ls.
The below code should do the required job.
for dir in */; do find . -mindepth 1 -maxdepth 1 -type f -name "*${dir%*/}*.pdf" -exec mv {} ${dir}/ \;; done
where */ will consider only the directories present in the given directory, find will search only files in the given directory which matches *${dir%*/}*.pdf i.e file name containing the directory name as its sub-string and finally mv will copy the matching files to the directory.
in Unix please use below command
find . -name '*508471*' -exec bash -c 'echo mv $0 ${0/508471/598690}' {} \;
You may use this for loop from the parent directory of these pdf files and directories:
for d in */; do
compgen -G "*${d%/}*.pdf" >/dev/null && mv *"${d%/}"*.pdf "$d"
done
compgen -G is used to check if there is a match for given glob or not.

Run an Executable Program File in Multiple Subdirectories Using Shell

I have a main directory with 361 subdirectories. Within the each subdirectory, there is a parameter file and one executable program file. The executable file is coded to look for the parameter file in the directory where the executable is located. (The same executable file is in all subdirectories. The parameter files all have the same file name in all subdirectories)
Instead of executing the program file individually, is there a cshell command for terminal to run them all at once?
UPDATED
If your Linux is so old it doesn't have -execdir, you could try this:
find $(pwd) -name YourProgram -exec dirname {} \; | while read d; do cd "$d" && pwd; done
If that correctly prints the names of the directories where your program needs to be run, just remove the pwd and replace with whatever you want done in tha directory - presumably something like this:
find $(pwd) -name YourProgram -exec dirname {} \; | while read d; do cd "$d" && ./YourPrgram; done
ORIGINAL ANSWER
Like this maybe:
find . -type f -name YourProgramName -execdir ./YourProgramName YourParameterFile \;
But backup first and check it looks right before using.
The -execdir causes find to change to the directory it has found before running the commands there.
If your command is more complicated, you can do this:
find . -type f -name YourProgramName -execdir sh -c "command1; command2; command3" \;
Check it does what you want like this:
find . -type f -name YourProgramName -execdir pwd \;
Maybe this will help. Suppose you have in each folder a file named params_file and an executable named exec_file, then:
for dir in `find . -maxdepth 1 -mindepth 1 -type d` ; do
cd $dir
cat params_file | xargs ./exec_file
cd ..
done

Include folder name in renaming a file in linux

I've already used that command to rename the files in multiple directories and change JPG to jpg, so I have consistency.
find . -name '*.jpg' -exec sh -c 'mv "$0" "${0%.JPG}$.jpg"' {} \;
Do you have any idea how to change that to include the folder name in the name of the file
I am executing that in a folder that contains about 2000 folders (SKU's) or products ... and inside every SKU folder, there are 9 images. 1.jpg 2.jpg .... 9.jpg.
So the bottom-line is I have 2000 images with name 1.jpg, 2.jpg ... 9.jpg. I need those files to be unique, for example:
folder-name-1.jpg ... folder-name.2.jpg ... so on, in every folder.
Any help will be appreciated.
For example I can do as follows:
$ find . -iname '*.jpg' | while read fn; do name=$(basename "$fn") ; dir=$(dirname "$fn") ; mv "$fn" "$dir/$(basename "$dir")-$name" ;done
./lib/bukovina/version.jpg ./lib/bukovina/bukovina-version.jpg
./lib/bukovina.jpg ./lib/lib-bukovina.jpg
You can use fine one-liner:
find . -name '*.jpg' -execdir \
bash -c 'd="${PWD##*/}"; [[ "$1" != "$d-"* ]] && mv "$1" "./$d-$1"' - '{}' \;
This command uses safe approach to check whether image name is already not prefixed by the current directory name. You can run it multiple times also and image name won't be renamed after first run.
To get the folder name of a file you can do $(basename $(dirname ${FILE})), where ${FILE} is a path that may be relative but must contain at least one folder before the file name in it. This should not be a problem with find. If it is, just run it from one directory up.
find . -name '*.jpg' -exec sh -c 'mv "$0" "$(basename $(dirname $0))-${0%.JPG}$.jpg"' {} \;
Or, if you have JPEGs in your current directory:
find ../<dirname> -name '*.jpg' -exec sh -c 'mv "$0" "$(basename $(dirname $0))-${0%.JPG}$.jpg"' {} \;

git find and rename a string in multiple filenames and folder names

So Basically I need to find all files and folders in my github project containing the string 'persons'
find . -type f -print | grep "persons"
find . -type d -print | grep "persons"
The above works for me.
But I also need to rename all the above files and folders with 'members'
Can I do the above with a couple of commands? Instead of manually replacing them one by one
i dont know how to do a git mv oldfilename newfilename rescursively to the above
for dir in `find /DIR -type d -iname '*persons*'` ; do
git mv "${dir}" "${dir/persons/members}"
done
Will do. For the files do it with -type f.
find . -depth -name persons | while read F; do mv $F $(dirname $F)/members; done

Bash script unrar then rename with dir name

I want to rename files in a dir then change the file name base on the dir name this is what I had but it's not quite working
find . -type d | while read dir
do
cd "$dir"
find . -name \*.rar -exec unrar x '{}' \;
find . -name \*.avi -exec mv '{}' "$dir.avi" \;
cd ..
done
root/directory.1.with.rar.in/
root/directory.2.with.rar.in/
root/directory.2.with.rar.in/
root/directory.2.with.rar.in/
root/directory.2.with.rar.in/
I want all the rar in the Directory to be extracted in root then renamed with the name of the directory it was extracted from.

Resources