How do I recursively visit and delete all files in a particular folder which has spaces in sh? - linux

I am currently using
p=` ls -l -p $MYDIR | egrep '^d' | awk '{print $9}'
for getting all the folders and then
for dirs in ${p}
do
for recursively opening the folders. It works fine for folder name without spaces, but for folder names with spaces, the second part of the folder name is selected as a seperate folder.

To iterate over all directories under $MYDIR,
find "$MYDIR" -type d |
while read dir; do
printf '%s\n' "Deleting files in <$dir>"
rm -f "$dir"/*
done
Note that you must double quote the dir variable when using it to prevent the shell from performing word-splitting at spaces.
Skipping $MYDIR if you don't need it left as an exercise.

You can use:-
find /opt/test -type d ! -name "test" -exec echo rm -rf \"{}\" \; | sh
or
find -type d ! -name "." -exec echo rm -rf \"{}\" \; | sh

Related

Linux Command Line - list all directories containing .js files, and copy the directories and their contents to a new folder

Here is the code I already have that finds and lists all directories containing .js files (excluding the node_modules directory).
find . -name '*.js*' -printf "%h\n" | sort -u | grep -v node_modules
As you can see, listing those directories is no problem. However, rather than list the directories, I would like to copy them (and their contents) to a new folder, preferably all in one line without running any kind of script.
Any help would be much appreciated!
The safest way to do this is to process the list of directories using NULL as the delimiter so that directories with spaces (and other odd characters) are handled correctly.
Remove the echo if the output looks correct.
"1-liner"
find "/path/to/tld" -path "*node_modules*" -prune -o -name "*.js" -printf "%h\0" | \
sort -uz | xargs -0 -I _ echo cp -a _ "/path/to/new/dir"
Bash Script
This requires Bash 4 for the associative array which will filter out duplicates.
#!/bin/bash
tld="/path/to/top/level/dir"
newdir="/path/to/new/dir"
unset dirHash;
declare -A dirHash
while read -r -d $'\0' dir; do
(( ! dirHash["$dir"]++ )) && echo cp -a "$dir" "$newdir"
done < <(find "$tld" -path "*node_modules*" -prune -o -name "*.js" -printf "%h\0")

Find pattern of the file, create a folder with that pattern and copy the files to that folder - Bash script

I have a task, to find the pattern of the file, create a folder with the pattern name and copy the file to that folder. I am able to create the folders.
folders=`find /Location -type f -name "*.pdf" -printf "%f\n" | cut -f 1 -d '_' | sort -u`
for i in $folders
do
mkdir -p /LocationToCreateTheFolder/$i
done
Not able to go further on how to copy the files.
maybe try?
for i in $folders do mkdir -p /LocationToCreateTheFolder/$i && cp ./$i.pdf ./$i/
This will do the finding and the copying:
find Location -type f -name '*.pdf' -exec bash -c 'f=${1##*/}; d="LocationToCreateTheFolder/${f%%_*}"; mkdir -p "$d" && cp "$1" "$d"' None {} \;
This is safe for difficult file names even ones that contain spaces, tabs, or newlines in their names.
How it works
find Location -type f -name '*.pdf' -exec bash -c '...' None {} \;
This will find the pdf files under directory Location and, for each one found, the bash commands inside '...' will be executed with $1 set to the name of the file found. ($0 is set to None. We don't use $0.)
f=${1##*/}
This removes the directory names from the name of the file. This is an example of prefix removal: everything in $1 up to and including the last / is removed.
d="LocationToCreateTheFolder/${f%%_*}"
This creates the name of the directory to which we want to send the file.
${f%%_*}" is an example of suffix removal. Everything in $f from the first _ and after is removed.
mkdir -p "$d" && cp "$1" "$d"
This makes sure that the directory exists and then copies the file to it.

Bash script to find files in a list, copy them to dest, print files not found

I would like to build on the answer I found here: Bash script to find specific files in a hierarchy of files
find $dir -name $name -exec scp {} $destination \;
I have a file with a list of file names and I need to find those files on a backup disk, then copy those files found to a destination folder, and lastly print the files that could not be found to a new file.
the last step would be helpful so that I wouldn't need to make another list of files copied and then do a compare with original list.
If the script can then make a list of the copied files, and do a compare, then print the differences, then that's exactly what's required. Unless the shell process find can print to file each time it "Can't find" a file.
Assuming that your list is separated by newlines; something like this should work
#!/bin/bash
dir=someWhere
dest=someWhereElse
toCopyList=filesomewhere
notCopied=filesomewhereElse
while read line; do
find "$dir" -name "$line" -exec cp '{}' $dest \; -printf "%f\n"
done < "$toCopyList" > cpList
#sed -i 's#'$dir'/##' cpList
# I used # instead of / in sed to not confuse sed with / in $dir
# Also, I assumed the string in $dir doesnot end with a /
cat cpList "$toCopyList" | sort | uniq -c | sed -nr '/^ +1/s/^ +1 +(.*)/\1/p' > "$notCopied"
# Will not work if you give wild cards in your "toCopyList"
Hope it helps
while read fname ; do
find /FROM/WHERE/TO/COPY/ \
-type f \
-name "$fname" \
-exec cp \{\} /DESTINATION/DIR/ \; 2>/dev/null
find /DESTINATION/DIR/ \
-type f \
-name "$fname" &>/dev/null || \
echo $fname
done < FILESTOCOPY > MISSEDFILES
Will do.

Bash Script: Find and Remove Folders Older Than 7 Days, With File That Does NOT Contain a Certain String

I need to come up with a Bash script that will remove any folders within a directory if they meet both of the following criteria:
Older than 7 days.
Have an xml file in them that does not contain a certain string.
I know that this command works for removing all folders in the directory that are older than n days:
find ./ -type d -mtime +7 -exec rm -rf {}\;
And this command removes all of the files named kittens.xml that don't have the string <claws>18</claws>:
find ./* -name "kittens.xml" -type f\! -exec grep -L "<claws>18</claws>" {} \;| xargs rm -fv
But how do I remove all the folders that are more than one week old and don't contain that string?
FYI, I have very little prior experience with Bash.
for dir in `find /WHERE/ARE/THOSE/DIRS -type d -mtime +7 2>/dev/null` ; do
fgrep '<claws>18</claws>' "${dir}/kittens.xml" &>/dev/null || rm -fv "${dir}"
done
It loops through the found directories, then checks via fgrep for the needed string in the file, and if it's not found (e.g. not in the file OR the file is missing), removes the dir.
Note: it might cause damage, so think before run it. I'd run it first like this so it shows what it would remove...
fgrep '<claws>18</claws>' "${dir}/kittens.xml" &>/dev/null || echo "rm -fv ${dir}"
Also if there are subdirs, it might cause problems.
Try using the grep v or --invert-match tags:
find ./* -name "kittens.xml" -type f\! -exec grep -L -v "<claws>18</claws>" {} \;| xargs rm -fv
You can find more about grep invert by using this in terminal:
grep --help | grep invert

List all SVN repository URLs from a folder in recursive mode

We are looking for a script that will traverse in recursive mode all subfolders and list all SVN repository URLs and the path where it was found.
It will be used on /home folder of a user.
Recursively find directories, and for each of them try to get the SVN info. If it is successfull, then don't descend into the directory and print the directory name.
find -type d -exec bash -c "svn info {} > /dev/null 2> /dev/null" \; -prune -print
This will list the directories.
If you want the repository info, you can add it in the middle of the find exec command.
find -type d -exec bash -c "svn info {} 2> /dev/null | grep URL" \; -prune -print
Edit:
I found much better results by only testing for the presence of an .svn subdirectory. Then, svn info is called once at the end and grepped for Path and URL. (Plus using -0 to prevent from spaces in filenames.)
find -type d -exec test -d "{}/.svn" \; -prune -print0 | xargs -0 svn info | grep -e '\(Path\|URL\)'
Depending on what kind of limitations you have there could be different ways. The easiest way would be to do svn ls -R | grep -v "\." to grab all the sub-folders from the repository location you're at and feed that in to a for loop that adds the URI to the root of the ls to the front of each line. This will however not be adequate if you have files that do not contain a "." as they will be detected as folders. Unfortunately svn ls doesn't allow you to filter by file/folder, so if you need to deal with filenames without extensions then you'd have to do something different such as checking out the source and using find to get the folder names.
user_home=... # fill in the user's home dir
old_dir=/../
find $user_home -name .svn | rev | cut -f2- -d/ | rev | while read line ; do
echo -n "$line"$'\t'
wc -c <<<"$line"
done | sort -t$'\t' -k1,1 -k2,2n | while read dir size ; do
if [[ $dir != $old_dir* ]] ; then
old_dir=$dir
svn info $dir | grep URL
echo PATH: $dir
echo
fi
done
Just hope users do not store SVN under directories with spaces in names.

Resources