Rename files with dynamic name in bash - bash

I am trying to rename all files which contains 'Name' with dynamic name.
So this file 'NameSomething' should look like this 'SearchSomething'. But script below just removes 'Name' from file name.
name='search'
Name='Search'
find ../../$name-module -name 'Name*' -type f -exec bash -c 'mv "$1" "${1/Name/$Name}"' -- {} \;

You have to export Name, because otherwise the command find will not inherit the variable $Name into its environment. So:
name='search'
export Name='Search'
find ../../$name-module -name 'Name*' -type f -exec bash -c 'mv "$1" "${1/Name/$Name}"' -- {} \;

\You can escape quotes, no need for an export:
find ../../$name-module -name 'Name*' -type f -exec bash -c "mv \"\$1\" \"\${1/Name/$Name}\"" -- {} \;

You might use find to find a list of the files you want to rename and pipe that to another program to do the renaming.
For example, you could do:
find ../../$name-module -print0 -name 'Name*' -type f | \
xargs -0 rename "s/Name/$Name/"
No need for exporting (ugly + might affect other commands)
Rename might get run only once (if all the found files fit on one command line) (with -exec, you're running two programs -- bash and mv -- per each file. That might be inefficient.
(You could get further performance increase with this approach by using Gnu parallel instead of xargs, if you're on a multicore computer)

You could use the Perl-based rename command:
Name=Search
rename "s/^Name/$Name/" *

Related

Change of directories names according to pattern in Bash

I need to change with one-liner (best option) directories name, more precise - to remove some prefix that has variable part at the beginning TO_BE_REMOVED_W._
I can find dirs to modification with:
find ./ -type d -iname 'TO_BE_REMOVED_W*' -exec mv {} _____ \;
How should I form execution part to rename directories to remove pattern from whole name?
Most distros come with rename which accepts substitution patterns, so I usually use -execdir with rename.
For example, to remove PREFIX_:
$ find . -execdir rename 's/^PREFIX_//' '{}' +
Note that for Arch Linux, this is called perl-rename (Arch's default rename is not the standard rename found on most distros).
You can use -exec bash -c '...' like this:
$ find -name 'foo*'
./foo-01
./foo-05
./foo-02
./foo-03
./foo-04
$ find -name 'foo*' -exec bash -c 'file=$0; echo Now you can do anything with $file ...' {} \;
Now you can do anything with ./foo-01 ...
Now you can do anything with ./foo-05 ...
Now you can do anything with ./foo-02 ...
Now you can do anything with ./foo-03 ...
Now you can do anything with ./foo-04 ...
$

Find and rename files by pattern works in Debian, but not in CentOS7

I need to find and rename files with question mark in names.
Example: "style.css?ver=111" should become "style.css"
I use this command
find . -type f -name "*\?*" -exec rename 's/\?.*//' '{}' \;
In Debian all works fine, but in CentOS7 I get and error that "rename: not enough arguments
"
Any ideas why?
For a reliable option that should work in any POSIX-compliant system, you may use
find . -type f -name "*\?*" -exec sh -c 'mv -- "$1" "${1%%\?*}"' findshell {} \;
$1 is the name of each file found and ${1%%\?*} is a construct that strips the substring starting from the question mark.
That should be enough if you have a few matching files. If you need it, a more efficient alternative is
find . -type f -name "*\?*" -exec sh -c '
for file in "$#"; do
mv -- "$file" "${file%%\?*}"
done
' findshell {} +

How do I rename files found with the find command

I have a series of music folders. Some of the file names contain an underscore which I would like to get rid of.
With
find /Users/Chris/CDs -type f -name "*_*"
I find all of the files with underscores.
it appears that I can add -execdir mv {} to the command but do not know what to add from there.
I think {} provides the full path and file name as a string of the file with underscores but I do not know how to use something like sed 's/_//g' to remove the _ on the new file name.
Any help would be greatly appreciated.
Try:
find /Users/Chris/CDs -type f -name "*_*" -execdir bash -c 'mv -i -- "$1" "${1//_/}"' Mover {} \;
How it works:
-execdir bash -c '...' Mover {} \;
This starts up bash and tells it to run the command in the single quotes with Mover assigned to $0 and the file name assigned to $1.
mv -i -- "$1" "${1//_/}"
This renames file $1. This uses bash's parameter expansion feature, ${1//_/}, to create the target name from $1 by removing all underlines.
The option -i tells mv to ask interactively before overwriting a file.
The option -- tells mv that there are no more options. This is needed so that files whose names begin with - will be processed correctly.
Example
Let's start with a directory with these files:
$ ls
1_2_3_4 a_b c_d
Next we run our command:
$ find . -type f -name "*_*" -execdir bash -c 'mv -i -- "$1" "${1//_}"' Mover {} \;
After the command completes, the files are:
$ ls
1234 ab cd
The purpose of $0
Observe this command where we have added an error:
$ find . -type f -name "*_*" -execdir bash -c 'foobar -i -- "$1" "${1//_}"' Mover {} \;
Mover: foobar: command not found
Note that Mover appears at the beginning of the error message. This signals that the error comes from within the bash -c command.
If we replace Mover with -, we would see:
$ find . -type f -name "*_*" -execdir bash -c 'foobar -i -- "$1" "${1//_}"' - {} \;
-: foobar: command not found
When running a single command in a terminal, the source of the error may still be obvious anyway. If this find command were buried inside a long script, however, the use of a more descriptive $0, like Mover or whatever, could be a big help.

How to copy files recursively, rename them but keep the same extension in Bash?

I have a folder with tens of thousands of different file types. Id like to copy them all to a new folder (Copy1) but also rename them all to $RANDOM but keep the extension intact. I realize I can write a line specifying which extension to find and how to name it, but there is got to be a way to do it dynamically, because there are at least 100 file types and may be more in the future.
I have the following so far:
find ./ -name '*.*' -type f -exec bash -c 'cp "$1" "${1/\/123_//_$RANDOM}"' -- {} \;
but that puts the random number after the extension, and also it puts the all in the same folder. I cant figure out how to do the following 2 things:
1 - Keep all paths intact, but in a new root folder (Copy1)
2 - How to have name be $RANDOM.extension, instead of .extension.$RANDOM
PS - by $RANDOM i mean actual randomly generated number. I am interested in keeping folder structure, so we are dealing with a few hundred files at most per directory, but all directories/files need to be renamed to $RANDOM. Another way to look at what I need to do. Copy all contents or Folder1 with all subdirectories and files to Folder2 (where Fodler2 is a $RANDOM name), then rename all folders and files to random names but keep all extensions.
EDIT: Ok i figured out how to rename and keep extension. But I have a problem where its dumping all of the files into the root directory where script is run from. How do I keep them in their respective folders? Command Im using is:
find ./ -name '*.*' -type f -exec bash -c 'mv "$1" $RANDOM.${1##*.}' -- {} \;
Thanks!
Ok i figured out how to rename and keep extension. But I have a
problem where its dumping all of the files into the root directory
where script is run from. How do I keep them in their respective
folders? Command Im using is:
find ./ -name '*.*' -type f -exec bash -c 'mv "$1" $RANDOM.${1##*.}' -- {} \;
Change your command to:
PATH=/bin:/usr/bin find . -name '*.*' -type f -execdir bash -c 'mv "$1" $RANDOM.${1##*.}' -- {} \;
Or alternatively using uuids instead of random numbers:
PATH=/bin:/usr/bin find . -name '*.*' -type f -execdir bash -c 'mv "$1" $(uuidgen).${1##*.}' -- {} \;
Here's what I came up with :
i=1
random="whatever"
find . -name "*.*" -type f | while read f
do
newbase=${f/*./$random$i.} //added counter to filename
cp $f /Path/Name/"$newbase"
((i++))
done
I had to add a counter to random (i), otherwise, if the extensions are similar, your files would overwrite themselves when copied.
In your new folder, your files should look like this :
whatever1.txt
whatever2.txt
etc etc
I hope this is what you were looking for.
Here is the command that worked for me.
find . -name '*.pdf' -type f -exec bash -c 'echo "{}" && cp "$1" ./$RANDOM.${1##*.}' -- {} \;

In unix , moving a file with xargs after finding and zipping it?

So in a bashscript i've the following very simple line , but how can i chain it further to move the file ?
find . -type f -ctime -$2 -name "mylog*.log" | xargs bzip2
This works fine but i'd also like to move the file to a new directory once I am done with the bzip2.
One standard trick is to use a new script that does whatever you need. Here, I assume that ${OTHER_DIRECTORY} is an environment variable that says where to put the compressed files, but there are plenty of other (better!) ways to get that information to the script (such as specifying the directory as the first argument — as the last argument is a bad idea).
#!/bin/bash
for file in "$#"
do
bzip2 "$file"
mv "$file.bz2" "${OTHER_DIRECTORY:-/tmp}"
done
You then run that script with find:
find . -type f ctime -$2 -name "mylog*.log" -exec tinyscript.sh {} +
This is pretty effective. If you only want one mv command, you can consider something along the lines of:
bzip2 "$#"
bz2=()
for file in "$#"; do bz2+=( "$file.bz2" ) done
mv "${bz3[#]}" "${OTHER_DIRECTORY:-/tmp}"
This code works even if the path names contain spaces and other untoward characters.
One option might be something like this:
find . -type f -ctime -$2 -name "mylog*.log" -exec bzip2 {} \; -exec mv {} /path/to/new_dir/ \;

Resources