Move files according to a specific pattern in the file name - unix

I have to copy a list of files having the following pattern in the name: "_my_file" from sub-folders into one main folder. File names appear as the following:
A_my_file.txt
B_my_file.txt
C_my_file.txt
I used the following string but it does not work:
find . -name "*_my_file*" -exec mv "{}"./dest_fld \;

Add space between "{}" and ./dest_fld:
find . -name "*_my_file*" -exec mv "{}" ./dest_fld \;
# ^
It would be better dest_fld to be outside the currently directory. Unless, dest_fld will be scaned too.

Related

BASH: performing a regex replace on a path from find command

AIM: to find all JS|TS excluding *.spec.js files in a directory but replace the base path with ./
I have this command
find src/app/directives -name '*.[j|t]s' ! -name '*.spec.js' -exec printf "import \"%s\";\n" {} \;
which in said directory prints the marked JS files. However I want to replace the src/app with ./
I've tried playing with [[]] and this command but they don't work.
find src/app/components -name '*.[j|t]s' ! -name '*.spec.js' -exec printf "import \"%s\";\n" ${{}/src
/hi} \;
zsh: bad substitution
Given your "AIM", all you really need is:
find src/app/directives -type f -name "*.[jt]s" ! -name "*.spec.js" -printf "./%f\n"
The reason being is the '|' in your character-class isn't matching anything, but isn't hurting anything for that matter. Your second ! -name "*.spec.js" is fine. You don't need -exec and can simply use -printf "./%f\n" (where "%f" provides the filename only for the current file). You simply prepend the "./" as part of the -printf format-string.
Let me know if I misunderstood your AIM or if you have further questions.
Removing src/app/directives While Preserving Remaining Path
If you want to preserve the remainder of the path after src/app/directives (essentially just replacing it with '.'), you can use a short helper-script with the POSIX parameter expansion to trim src/app/directives from the front of the string replacing it with '.' using printf in the helper script. For example the helper could be:
#!/bin/zsh
printf ".%s" "${1#./src/app/directives}"
(note: the leading "./" being removed along with src/app/directives is prepended by find, the '.' added by the printf format-string will result in the returned filename being ./rest/of/path/to/filename)
Call the script whatever you like, helper.sh below. Make it executable chmod +x helper.sh.
The find call would then be:
find src/app/directives -type f -name "*.[jt]s" ! -name "*.spec.js" -exec path/to/helper.sh '{}' \;
Give that a go and let me know if it does what you are needing.

Recursively remove portion of filename that matches a pattern

I'm on a UNIX system. Within a directory (and any of its subdirectories), I'm trying to rename all files that match a certain pattern:
change hello (1).pdf
to hello.pdf
Based on the top response from this question, I wrote the following command:
find . -name '* (1)*' -exec rename -ns 's/ (1)//' {} \;
The find works on its own and the rename also works on its own, but the above command only outputs Reading filenames from STDIN and does nothing. How can I make this work?
Figured this out! For whatever reason, it only works when you use the Perl version of rename like this:
find . -name '* (1)*' -exec rename -f -s ' (1)' '' {} \;

Rename all subdirectories in csh shell

I'm using csh and I have a directory structure containing multiple sub-directories. I'm trying to rename all the directories and sub-directories but not the files inside these directories. So something like
From
topdir1
--dir11
--dir12
topdir2
--dir21
----dir211
--dir22
to
topdir1.test
--dir11.test
--dir12.test
topdir2.test
--dir21.test
----dir211.test
--dir22.test
I can list the directories with find . -maxdepth 3 -type d. I'm trying to use a foreach loop to rename them. So
foreach i (`find . -maxdepth 3 -type d`)
mv $i $i.test
end
But this doesn't work as once the top level directory is renamed, it cannot find the sub-directories, so it only renames the top level directories.
Any idea on how to go about this?
Thanks
How about reversing the find results so that the subdirectories are listed first?
foreach i (`find ./* -maxdepth 3 -type d | sort -r`)
mv $i $i.test
end
Sort will output the longest directory names last, using the -r (reverse) flag changes it so that the lowest directories will be listed first, and be renamed before their parent directories do.
Use the -depth option to find.
From the solaris man find page:
-depth Always true. Causes descent of the
directory hierarchy to be done so that
all entries in a directory are acted on
before the directory itself. This can
be useful when find is used with cpio(1)
to transfer files that are contained in
directories without write permission.
Why use a loop? Just let find do the work:
find . -depth -maxdepth 3 -type d -exec mv {} {}.test \;
That is not strictly portable (some implementations of find may legally not expand {}.test to the string you want, so you might prefer:
find . -depth -maxdepth 3 -type d -exec sh -c 'mv $0 $0.test' {} \;

Using find to delete all files with a given name except the one with a given extension

I want to delete all files in a directory with a given name, except for one with a given extension. I.e. we have a directory with:
foo.txt foo.exe foo.jpg foo.png foo.something foo.somethingelse bar.jpg bar.exe
I want to get rid of foo.txt foo.jpg foo.png foo.something foo.somethingelse
BUT crucially I don't want to get rid of foo.exe
Is there an easy one liner to do this?
Thank you
You can use ! inside a find command to exclude things, so something like:
find . -maxdepth 1 -type f -name "foo.*" ! -name foo.exe -exec rm '{}' \;
----------- ------- ------------- --------------- ----------------
in this dir files named foo.* but not foo.exe ...destroy them.
That should delete files matching foo.* in the current directory but leave foo.exe alone.

Unix Find Replace Special Characters in Multiple Files

I've got a set of files in a web root that all contain special characters that I'd like to remove (Â,€,â,etc).
My command
find . -type f -name '*.*' -exec grep -il "Â" {} \;
finds & lists out the files just fine, but my command
find . -type f -name '*.*' -exec tr -d 'Â' '' \;
doesn't produce the results I'm looking for.
Any thoughts?
to replace all non-ascii characters in all files inside the current directory you could use:
find . -type f | xargs perl -pi.bak -e 's,[^[:ascii:]],,g'
afterwards you will have to find and remove all the '.bak' files:
find . -type f -a -name \*.bak | xargs rm
I would recommend looking into sed. It can be used to replace the contents of the file.
So you could use the command:
find . -type f -name '*.*' -exec sed -i "s/Â//" {} \;
I have tested this with a simple example and it seems to work. The -exec should handle files with whitespace in their name, but there may be other vulnerabilities I'm not aware of.
Use
tr -d 'Â'
What does the ' ' stands for? On my system using your command produces this error:
tr: extra operand `'
Only one string may be given when deleting without squeezing repeats.
Try `tr --help' for more information.
sed 's/ø//' file.txt
That should do the trick for replacing a special char with an empty string.
find . -name "*.*" -exec sed 's/ø//' {} \
It would be helpful to know what "doesn't produce the results I'm looking for" means. However, in your command tr is not provided with the filenames to process. You could change it to this:
find . -type f -name '*.*' -exec tr -d 'Â' {} \;
Which is going to output everything to stdout. You probably want to modify the files instead. You can use Grundlefleck's answer, but one of the issues alluded to in that answer is if there are large numbers of files. You can do this:
find . -type f -name '*.*' -print0 | xargs -0 -I{} sed -i "s/Â//" \{\}
which should handle files with spaces in their names as well as large numbers of files.
with bash shell
for file in *.*
do
case "$file" in
*[^[:ascii:]]* )
mv "$file" "${file//[^[:ascii:]]/}"
;;
esac
done
I would use something like this.
for file in `find . -type f`
do
# Search for char end remove it. Save file as file.new
sed -e 's/[ۉ]//g' $file > $file.new
# mv file.new to file DON'T RUN IF YOU WILL NOT OVERITE ORIGINAL FILE
mv $file.new $file
done
The above script will fail as levislevis85 has mentioned it with spaces in filenames. This would not be the case if you use the following code.
find . -type f | while read file
do
# Search for char end remove it. Save file as file.new
sed -e 's/[ۉ]//g' "$file" > "$file".new
# mv file.new to file DON'T RUN IF YOU WILL NOT OVERITE ORIGINAL FILE
mv "$file".new "$file"
done

Resources