Remove files in directory and ignore if empty-BASH - bash

By using the command :
rm /file_path/*.csv
I can delete all csv files in the required folder.However if the directory is empty or there are no csv files I get the following error:
No such file or directory
How do I avoid this error?I have this logic in a script with certain downstream dependancies so throwing this error will cause the rest of my code to stop.Whats the best way in bash to delete files only if they exist in the directory?

Another variant is to check if your folder is empty before to run your script:
find file_path/ -type d -empty
It returns the name of your folder if it is empty.
Or use the "-f" option with rm command if you want only avoid the error message:
Without:
rm -r file_path/*.csv
rm: cannot remove ‘file_path/*.csv’: No such file or directory
With:
rm -rf file_path/*.csv

See Test whether a glob has any matches in bash for ways to check if /file_path/*.csv matches anything. However, even if you do such a test before running the rm command it may fail if the directory has a very large number of CSV files. See Argument list too long error for rm, cp, mv commands.
If you have a modern version of find, this is a reliable and efficient option:
find /file_path -maxdepth 1 -type f -name '*.csv' -delete

You can do: [ -f /file_path/*.csv ] && rm /file_path/*.csv

Related

Copying files in multiple directories in terminal

I am new to using the command line for operations, so forgive me if this question is obvious. I would like to move all files of a certain type (.aiff) from one directory that contains many sub-directories. The file structure looks like this:
directory
- subdir1
-- sound.aiff
-- other.txt
- subdir2
-- sound2.aiff
-- other2.txt
I've tried using something like cp -R /Users/me/directory/*.aiff /Users/me/newdirectory but I get a "no such file or directory" error. I don't know how to specify that the files I want copied in the subdirectories must be .aiff files.
Try this:
cp -R /Users/me/directory/*/*.aiff /Users/me/newdirectory
But probably the destination /Users/me/newdirectory is missing.
You could verify this by doing:
file /Users/me/newdirectory
If the directory doesn't exist will print an error like:
Users/me/newdirectory: cannot open `/Users/me/newdirectory' (No such file or directory)
Create the directory with:
mkdir /Users/me/newdirectory
Next, try to copy the files again, if you want to move them use mv instead of cp
Another way is to use the command find, for example:
find /Users/me/directory -type f -iname "*.aiff" -exec mv {} /Users/me/newdirectory \;
In this example, the command find is going to search for in directory /Users/me/directory/ only for files -type f that end (case insensitive) in *.aiff for each file found it will execute the command mv exec mv {} /Users/me/newdirectory. The {} is a placeholder.
Before moving you could test the command by just finding the desired types:
find . -iname "*.aiff"
This will search for files within the directory the command is executed, notice the . instead of a /Users/me/directory/

bash script to remove files matching those in another directory

I'm trying to create a script that retrieves files (including subfolders) from CVS and stores them into a temporary directory /tmp/projectdir/ (OK), then removes copies of those files from my project directory /home/projectdir/ (not OK) without touching any other files in the project directory or the folder structure itself.
I've been attempting two methods, but I'm running into problems with both. Here's my script so far:
#!/usr/bin/bash
cd /tmp/
echo "removing /tmp/projectdir/"
rm -rf /tmp/projectdir
# CVS login goes here, code redacted
# export files to /tmp/projectdir/dir_1/file_1 etc
cvs export -kv -r $1 projectdir
# method 1
for file in /tmp/projectdir/*
do
# check for zero-length string
if [-n "$file"]; then
echo "removing $file"
rm /home/projectdir/"$file"
fi
done
# method 2
find /tmp/projectdir/ -exec rm -i /home/projectdir/{} \;
Neither method works as intended, because I need some way of stripping /tmp/projectdir/ from the filename (to be replaced with /home/projectdir/) and to prevent them from executing rm /home/projectdir/dir_1 (i.e. the directory and not a specific file), but I'm not sure how to achieve this.
(In case anybody is wondering, the zero-length string bit was an attempt to avoid rm'ing the directory, before I realised /tmp/projectdir/ would also be a part of the string)
You can use:
cd /tmp/projectdir/
find . -type f -exec rm -i /home/projectdir/{} \;

Makefile: removing files

On Windows I am trying to add a Makefile target to remove all files from a specific directory (NOT including subdirectories)...
clean_files:
rm -f Build/*.*
But I get the error: /bin/sh: rm: command not found
Running it from the command line works and running it without the *'s works.
clean_files:
- rm -f Build/*
putting a '-' before a make command will ignore any errors from that command, like
rm: cannot remove `Build/subdir': Is a directory
For removing all files from directory (NOT including sub-directories) consider:
clean_files:
find Build/ -type f -maxdepth 1 -delete

Copy all files with a certain extension from all subdirectories

Under unix, I want to copy all files with a certain extension (all excel files) from all subdirectories to another directory. I have the following command:
cp --parents `find -name \*.xls*` /target_directory/
The problems with this command are:
It copies the directory structure as well, and I only want the files (so all files should end up in /target_directory/)
It does not copy files with spaces in the filenames (which are quite a few)
Any solutions for these problems?
--parents is copying the directory structure, so you should get rid of that.
The way you've written this, the find executes, and the output is put onto the command line such that cp can't distinguish between the spaces separating the filenames, and the spaces within the filename. It's better to do something like
$ find . -name \*.xls -exec cp {} newDir \;
in which cp is executed for each filename that find finds, and passed the filename correctly. Here's more info on this technique.
Instead of all the above, you could use zsh and simply type
$ cp **/*.xls target_directory
zsh can expand wildcards to include subdirectories and makes this sort of thing very easy.
From all of the above, I came up with this version.
This version also works for me in the mac recovery terminal.
find ./ -name '*.xsl' -exec cp -prv '{}' '/path/to/targetDir/' ';'
It will look in the current directory and recursively in all of the sub directories for files with the xsl extension. It will copy them all to the target directory.
cp flags are:
p - preserve attributes of the file
r - recursive
v - verbose (shows you whats
being copied)
I had a similar problem. I solved it using:
find dir_name '*.mp3' -exec cp -vuni '{}' "../dest_dir" ";"
The '{}' and ";" executes the copy on each file.
I also had to do this myself. I did it via the --parents argument for cp:
find SOURCEPATH -name filename*.txt -exec cp --parents {} DESTPATH \;
In 2022 the zsh solution also works in Linux Bash:
cp **/*.extension /dest/dir
works as expected.
find [SOURCEPATH] -type f -name '[PATTERN]' |
while read P; do cp --parents "$P" [DEST]; done
you may remove the --parents but there is a risk of collision if multiple files bear the same name.
On macOS Ventura 13.1, on zsh, I saw the following error when there were too many files to copy, saw the following error:
zsh: argument list too long: cp
Had to use find command along with cp to get the files copied to my destination:
find ./module/*/src -name \*.java -print | while read filelocation; do cp $filelocation mydestinationlocation; done

How to remove files starting with double hyphen?

I have some files on my Unix machine that start with
--
e.g. --testings.html
If I try to remove it I get the following error:
cb0$ rm --testings.html
rm: illegal option -- -
usage: rm [-f | -i] [-dPRrvW] file ...
unlink file
I tried
rm "--testings.html" || rm '--testings.html'
but nothing works.
How can I remove such files on terminal?
rm -- --testings.html
The -- option tells rm to treat all further arguments as file names, not as options, even if they start with -.
This isn't particular to the rm command. The getopt function implements it, and many (all?) UNIX-style commands treat it the same way: -- terminates option processing, and anything after it is a regular argument.
http://www.gnu.org/software/hello/manual/libc/Using-Getopt.html#Using-Getopt
rm -- --somefile
While that works, it's a solution that relies on rm using getopts for parsing its options. There are applications out there that do their own parsing and will puke on that too (because they might not necessarily implement the "-- means end of options" logic).
Because of that, the solution you should drive through your skull is this one:
rm ./--somefile
It will always work, because this way your arguments never begin with a -.
Moreover, if you're trying to make really decent shell scripts; you should technically be putting ./ in front of all your filename parameter expansions to prevent your scripts from breaking due to funky filename input (or to prevent them being abused/exploited to do things they're not supposed to do: for instance, rm will delete files but skip over directories; while rm -rf * will delete everything. Passing a filename of "-rf" to a script or somebody touch ~victim/-rf'ing could in this way be used to change its behaviour with really bad consequences).
Either rm -- --testings.html or rm ./--testings.html.
rm -- --testings.html
Yet another way to do it is to use find ... -name "--*" -delete
touch -- --file
find -x . -mindepth 1 -maxdepth 1 -name "--*" -delete
For a more generalised approach for deleting files with impossible characters in the filename, one option is to use the inode of the file.
It can be obtained via ls -i.
e.g.
$ ls -lai | grep -i test
452998712 -rw-r--r-- 1 dim dim 6 2009-05-22 21:50 --testings.html
And to erase it, with the help of find:
$ find ./ -inum 452998712 -exec rm \{\} \;
This process can be beneficial when dealing with lots of files with filename peculiarities, as it can be easily scripted.
rm ./--testings.html
or
rm -- --testings.html

Resources