Basic Bash Function to find and copy files based on filetype - linux

I use the little bit of code below to find files in a folder with a given filetype and then copy them to a different folder.
find ./ -name '*.chk' -exec cp -prv '{}' "./" ';'
I tried to change this into a function in my .bash_profile so I could use it more quickly, but for some reason, it's not working. By not working, I mean nothing seems to happen when I execute "mymove .txt folder". Bash just goes to a new line, ready to accept more input.
mymove() {
find ./ -name "$1" -exec cp -prv '{}' "$2" ';';
}
Any advice?

You mention that you execute mymove with two parameters, .txt and folder:
$ mymove .txt folder
This will not work as .txt doesn't match any files, change it to:
$ mymove '*.txt' folder
Note the quotes, you do not want to shell to expand *.txt.

Related

Loop through a directory with any level of depth

I want to execute a command on all files present on all levels in the directory. It may have any number of files and sub directories. Even these sub directories may contain any number of files and subdirectories. I want to do this using shell script. As I am new to this field can any one suggest me a way out.
You can use the command "find" with "xargs" after "|"(pipe).
Example: Suppose that I want to remove all files that have ".txt" extension on "Documents" directory:
find Documents -iname *.txt |xargs rm -f
Helps?
You can use a recursive command that uses wildcard characters (*) like so:
for dir in ~/dev/myproject/*; do (cd "$dir" && git status); done
If you want to apply commands on the individual files you should use the find command and execute commands on it like so:
find yourdirectory -type f -exec echo "File found: '{}'" \;
What this does:
finds all the items in the directory yourdirectory
that have the type f - so are a file
runs an exec on each file
Use find:
find -type f -exec COMMAND {} \;
-f applies the command only to files, not to directories. The command is recursive by default.

Delete .DS_STORE files in current folder and all subfolders from command line on Mac

I understand I can use find . -name ".DS_Store" to find all the .DS_Store files in the current folder and all subfolders. But how could I delete them from command line simultaneously? I found it's really annoying to switch back and forth to all folders and delete it one by one.
find can do that. Just add -delete:
find . -name ".DS_Store" -delete
Extend it even further to also print their relative paths
find . -name ".DS_Store" -print -delete
For extra caution, you can exclude directories and filter only for files
find . -name ".DS_Store" -type f -delete
find . -name ".DS_Store" -print -delete
This will delete all the files named .DS_Store in the current path while also displaying their relative paths
Here is how to remove recursively the .DS_Store file
Open up Terminal
In the command line, go to the location of the folder where all files and folders are:
cd to/your/directory
Then finally, type in the below command:
find . -name '.DS_Store' -type f -delete
Press Enter
Cheers!!
You can also use extended globbing (**):
rm -v **/.DS_Store
in zsh, bash 4 and similar shells (if not enabled, activate by: shopt -s globstar).
The best way to do this cleanly is using:
find . -type f \( -name ".DS_Store" -o -name "._.DS_Store" \) -delete -print 2>&1 | grep -v "Permission denied"
This removes the files, hides "permission denied" errors (while keeping other errors), printing out a clean list of files removed.
All the answers above work but there is a bigger problem if one is using mac and still on mac. The described lines do delete all the DS_Store files but Finder recreates them immediately again because that is the default behaviour. You can read about how this all works here. To quote from there if you are on mac, you should remove them only if you really need:
If you don’t have a particular reason to delete these .DS_Store files (windows sharing might be a solid reason,) it’s best to leave them “as is.” There’s no performance benefit in deleting .DS_Store files. They are harmless files that don’t usually cause any problems. Remember that the .DS_Store file saves your personalized folder settings, such as your icon arrangement and column sortings. And that’s why you normally don’t want to delete them but rather HIDE them.
If you really do, there is one more way which was not mentioned here:
sudo find / -name “.DS_Store” -depth -exec rm {} \;
Make a new file with a text editor, copy and paste the following text into it, and save it with the ".sh" file extension, then open the file with Terminal. Make sure the text editor is actually saving the raw text and not saving the file as a Rich Text Format file or some other text file format with additional information in the file.
#!/bin/bash
echo -e "\nDrag a folder here and press the Enter or Return keys to delete all files whose names begin with a dot in its subfolders:\n"
read -p "" FOLDER
echo -e "\nThe following files will be deleted:\n"
find $FOLDER -name ".*"
echo -e "\nDelete these files? (y/n): "
read -p "" DECISION
while true
do
case $DECISION in
[yY]* ) find $FOLDER -name ".*" -delete
echo -e "\nThe files were deleted.\n"
break;;
[nN]* ) echo -e "\nAborting without file deletion.\n"
exit;;
* ) echo -e "\nAborting without file deletion.\n"
exit;;
esac
done
This wasn't exactly the question, but if you wanna actually zip the directory without them .DS_STORE files, this works a treat...
zip -r -X archive_name.zip folder_to_compress

bash on Linux, delete files with certain file extension

I want to delete all files with a specific extension - ".fal", in a folder and its subfolders, except the one named "*Original.fal". The problem is that I want to delete other files that have the same extension:
*Original.fal.ds
*Original.fal.ds.erg
*Original.fal.ds.erg.neu
There are other ".fal"s that I want to delete as well, that don't have "Original" in them.
Names vary all the time, so I can't delete specific names. The *Original.fal doesn't vary.
I can only get up to here:
$find /disk_2/people/z183464/DOE-Wellen -name "*.fal" \! -name "*Original.fal" -type f -exec echo rm {} \;
It would be great if the command can delete only in the folder (and it's subfolders) where it has been called (executed)
When I run the code it gives me an error:
/disk_2/people/z183464/DOE-Wellen: is a directory
If you do not want find to dive too deep, you can restrict it with -maxdepth.
You can use a simple for loop for that. This command shows all the files you might want to delete. Change echo with rm to delete them.
cd /disk_2/people/z183464/DOE-Wellen && for I in `find . -name "*.fal" ! -name "*Original.fal"`; do echo $I; done
With "find ... | grep ..." you can use regex too, if you need more flexibility.

Backup files with dir structure bash script

I'm making a bash script that should backup all files and dir structure to another dir.
I made the following code to do that:
find . -type f -exec cp {} $HOME/$bdir \; -o -type d -exec mkdir -p {} $HOME/$bdir \; ;
The problem is, is that this only copies the files and not the dir structure.
NOTE: I may not use cp -r, cp -R or something like it because this code is part of an assignment.
I hope somebody can put me in the right direction. ;)
Joeri
EDIT:
I changed it to:
find . -type d -exec mkdir -p $HOME/$bdir/{} \; ;
find . -type f -exec cp {} $HOME/$bdir/{} \; ;
And it works! Ty guys ;)
This sounds like a job for rsync.
You mention that this is an assignment. What are your restrictions? Are you limited to only using find? Does it have to be a single command?
One way to do this is to do it in two find calls. The first call only looks for directories. When a directory is found, mkdir the corresponding directory in the destination hierarchy. The second find call would look for files, and would use a cp command like you currently have.
You can also take each filename, transform the path manually, and use that with the cp command. Here's an example of how to generate the destination filename:
> find . -type f | sed -e "s|^\./|/new/dir/|"
/new/dir/file1.txt
/new/dir/file2.txt
/new/dir/dir1/file1_1.txt
/new/dir/dir1/file1_2.txt
For your purposes, you could write a short bash script that take the source file as input, uses sed to generate the destination filename, and then passes those two paths to cp. The dirname command will return the directory portion of a filename, so mkdir -p $(dirname $destination_path) will ensure that the destination directory exists before you call cp. Armed with a script like that, you can simply have find execute the script for every file it finds.
cd olddir; tar c . | (cd newdir; tar xp)
Can you do your find with "-type d" and exec a "mkdir -p" first, followed by your find that copies the files rather than having it all in one command? It should probably also be mkdir -p $HOME/$bdir/{}.

Copying files in multiple subdirectories in the Linux command line

Let's say I have the following subdirectories
./a/, ./b/, ./c/, ...
That is, in my current working directory are these subdirectories a/, b/ and c/, and in each of these subdirectories are files. In directory a/ is the file a.in, in directory b/ is the file b.in and so forth.
I now want to copy each .in file to a .out file, that is, a.in to a.out and b.in to b.out, and I want them to reside in the directories they were copied from. So a.out will be found in directory a/.
I've tried various different approaches, such as
find ./ -name '*.in'|cp * *.out
which doesn't work because it thinks *.out is a directory. Also tried
ls -d */ | cd; cp *.in *.out
but it that would list the subdirectories, go into each one of them, but won't let cp do it's work (which still doesn't work)
The
find ./ -name '*.in'
command works fine. Is there a way to pipe arguments to an assignment operator? E.g.
find ./ -name '*.in'| assign filename=|cp filename filename.out
where assign filename= gives filename the value of each .in file. In fact, it would be even better if the assignment could get rid of the .in file extension, then instead of getting a.in.out we would get the preferred a.out
Thank you for your time.
Let the shell help you out:
find . -name '*.in' | while read old; do
new=${old%.in}.out # strips the .in and adds .out
cp "$old" "$new"
done
I just took the find command you said works and let bash read its output one filename at a time. So the bash while loop gets the filenames one at a time, does a little substitution, and a straight copy. Nice and easy (but not tested!).
Try a for loop:
for f in */*.in; do
cp $f ${f%.in}.out;
done
The glob should catch all the files one directory down that have a .in extension. In the cp command, it strips off the .in suffix and then appends a .out (see Variable Mangling in Bash with String Operators)
Alternatively, if you want to recurse into every subdirectory (not just 1 level deep) replace the glob with a find:
for f in $(find . -name '*.in'); do
cp $f ${f%.in}.out;
done
This should do the trick!
for f in `find . -type f -name "*.in"`; do cp $f `echo $f | sed 's/in$/out/g'`; done

Resources