Count no.of .xlsx across all the directories and subdirectories where .tab is also exist - bash

I have a directroy which consists of 'n' number of sub directories. Following is the structure:
RootDir ->
SubDir1 ->
test.xlsx
test.tab
SubDir2 ->
test.xlsx
As shown above SubDir1 has both files .xlsx and .tab and SubDir2 have only .xlsx. Like this I have 'n' number of subdirectories, and willing to count only .xlsx from the folders where .tab file is also present.
I wanted to do it using shell scripting.
The present code returning me count of .xlsx files. But, it also includes the .xlsx files where .tab fiiles are not present.
find . -name '*.xlsx' -type f

The following code should work:
count=0
for file in `find . -name '*.xlsx' -type f`; do
if [ -f "${file%.xlsx}"".tab" ]; then
count=`expr $count + 1`
fi
done
echo $count

A slightly refined version of Ankush Pandit's answer:
#!/bin/bash
count=0
while IFS= read -r -d "" f; do
[[ -f ${f%.xlsx*}.tab ]] && (( count++ ))
done < <(find RootDir -type f -name "*.xlsx" -print0)
echo "$count"
The combination of -print0 and read -d "" options protects filenames
which contain special characters such as a space character.
The syntax <(find ..) is a process substitution and the output of
find is fed to the read command in the while loop via the redirect.

Related

Move all files in a folder to a new location if the same existing Folder name exists at remote location

Looking for a bash script:
Here's the situation:
I have 1000's folders and subfolders on my Backup Directory Drive
lets say.....
/backup
/backup/folderA
/backup/folderA/FolderAA
/backup/folderB
/backup/folderB/FolderBB
I have Dozens of similar folders in a secondary location (with files in them) and the Folder names will match one of the folders or subfolders in the main backup drive.
I would like to move all contents of specific extension types from my secondary location $FolderName to the Backup location + matching subfolder ONLY if the $FolderName matches exactly and remove the folders from my secondary location!
If there is no corrosponding folder or subfolder in the backup location then leave the source folders & files alone.
looking forward to getting some help/guidance.
Mike
Additional info requested.Expected input and ouput
Lets say i have the following:
Backup Folder
/backup/test/file.bak
And for my secondary folder location:
/secondarylocation/mike/test/hello/john.bak
/secondarylocation/mike/test/hello/backup.zip
i would like this as the end result
/backup/test/file.bak
/backup/test/john.bak
/backup/test/backup.zip
and /secondarylocation/mike/test *and sub folders and files removed
run this script with quoted folders and file types:
./merge.sh "backup" "secondarylocation/mike" "*.zip" "*.bak"
replace -iname with -name if you want to search for suffix case sensitive
replace mv -fv with mv -nv when you don't want to overwrite duplicate file names
add -mindepth 1 to last find if you want to keep empty folder test
merge.sh
#!/bin/bash
# read folders from positional parameters
[ -d "$1" ] && targetf="$1" && shift
[ -d "$1" ] && sourcef="$1" && shift
if [ -z "$targetf" ] || [ -z "$sourcef" ]
then
echo -e "usage: ./merge.sh <targetfolder> <sourcefolder> [PATTERN]..."
exit 1
fi
# add prefix -iname for each pattern
while [ ${pattern:-1} -le $# ]
do
set -- "$#" "-iname \"$1\""
shift
pattern=$((${pattern:-1}+1))
done
# concatenate all prefix+patterns with -o and wrap in parentheses ()
if (( $# > 1 ))
then
pattern="\( $1"
while (( $# > 1 ))
do
pattern="$pattern -o $2"
shift
done
pattern="$pattern \)"
else
pattern="$1"
fi
# move files from searchf to destf
find "$targetf" -mindepth 1 -type d -print0 | sort -z | while IFS=$'\0' read -r -d $'\0' destf
do
find "$sourcef" -mindepth 1 -type d -name "${destf##*/}" -print0 | sort -z | while IFS=$'\0' read -r -d $'\0' searchf
do
if (( $# ))
then
# search with pattern
eval find "\"$searchf\"" -depth -type f "$pattern" -exec mv -fv {} "\"$destf\"" \\\;
else
# all files
find "$searchf" -depth -type f -exec mv -fv {} "$destf" \;
fi
# delete empty folders
find "$searchf" -depth -type d -exec rmdir --ignore-fail-on-non-empty {} +
done
done
exit 0
this will merge hello into test (earn the fruits and cut the tree)

Renaming all files in a folder with a prefix and in ascending order

How does one rename
random_files.jpg
that\ may\ contain\ spaces.jpg
and_differ_in_extensions.mp4
to
PREFIX_1.jpg
PREFIX_2.jpg
PREFIX_3.mp4
via bash script? More formally, how do I rename all files in a directory into an ordered list of form PREFIX_N.ext where .ext is preserved from the original filename.
My attempt below
for f in *; do
[[ -f "$f" ]] && mv "$f" "PREFIX_$f"
done
changes only prefixes.
You can use this in a for loop using find:
while IFS= read -rd '' file; do
ext="${file##*.}"
echo mv "$file" "PREFIX_$((++i)).$ext"
done < <(find . -type f -name '*.*' -maxdepth 1 -print0)
Once satisfied with the output, remove echo before mv command.
You can loop over the files using *, and then access them with a quoted var to preserve all the special characters.
You can then use parameter expansion to remove the start of the file up to ., and append that to your new filename.
x=1;for i in *;do [[ -f "$i" ]] && mv "$i" "PREFIX_$((x++)).${i##*.}";done
If you know x isn't already set though you can remove the assignment at the start and change $((x++)) to $((++x))

How to print the number of locations of file found

Prompt the user for a file name, without the path (Ex: xyz.out)
- Using the find command, provide the full path to the file
- At the end, “print number of locations of that file found”
- If it’s not found, then display “not found
and this is my script
#! /bin /bash
echo "please enter your file name"
read filename
if [ -f $filename ];
then
echo "file $filename found"
find $PWD -type f | grep $filename
#find "$(cd ..; pwd)" -name $filename
else
echo "file $filename was not found"
fi
but the thing is At the end, i need to “print number of locations of that file found”
help me out with this
Something like this to get the count:
find $PWD -type f -name $filename 2>/dev/null | wc -l
This should work:
find "$PWD" -type f -name "$fname" |grep "" -c
In trying to keep it as short as possible, one approach with Posix shell would be to fill a temporary file with the file names returned by find, cat the file to provide your output, and use wc to provide the line count (note: you use your own pattern instead of "*cmpf*" shown below):
$ find . -name "*cmpf*" -printf "%P\n" >tmp; cat tmp; c=$(wc -l <tmp); \
rm tmp; printf "[%s] files found\n" $c
cmpf1f2.c
cmpf1f2_2.c
bin/cmpf1f2_2
bin/cmpf1f2
snip/cmpf1f2_notes.txt
cmpf1f2_for.c
[6] files found
If bash is available, another approach is to read the matching files into an array and then use the number of elements as your count. Example:
$ a=( $(find . -name "*cmpf*" -printf "%P\n") ); printf "%s\n" ${a[#]}; \
printf -- "--\n'%s' files found.\n" ${#a[#]}
cmpf1f2.c
cmpf1f2_2.c
bin/cmpf1f2_2
bin/cmpf1f2
snip/cmpf1f2_notes.txt
cmpf1f2_for.c
--
'6' files found.
Both approaches give you both the files and directories in which they reside as well as the count of the files returned.
Note: if you would like ./ before each file and directory names, use the %p format instead of %P above.

Bash script to list files not found

I have been looking for a way to list file that do not exist from a list of files that are required to exist. The files can exist in more than one location. What I have now:
#!/bin/bash
fileslist="$1"
while read fn
do
if [ ! -f `find . -type f -name $fn ` ];
then
echo $fn
fi
done < $fileslist
If a file does not exist the find command will not print anything and the test does not work. Removing the not and creating an if then else condition does not resolve the problem.
How can i print the filenames that are not found from a list of file names?
New script:
#!/bin/bash
fileslist="$1"
foundfiles="~/tmp/tmp`date +%Y%m%d%H%M%S`.txt"
touch $foundfiles
while read fn
do
`find . -type f -name $fn | sed 's:./.*/::' >> $foundfiles`
done < $fileslist
cat $fileslist $foundfiles | sort | uniq -u
rm $foundfiles
#!/bin/bash
fileslist="$1"
while read fn
do
FPATH=`find . -type f -name $fn`
if [ "$FPATH." = "." ]
then
echo $fn
fi
done < $fileslist
You were close!
Here is test.bash:
#!/bin/bash
fn=test.bash
exists=`find . -type f -name $fn`
if [ -n "$exists" ]
then
echo Found it
fi
It sets $exists = to the result of the find. the if -n checks if the result is not null.
Try replacing body with [[ -z "$(find . -type f -name $fn)" ]] && echo $fn. (note that this code is bound to have problems with filenames containing spaces).
More efficient bashism:
diff <(sort $fileslist|uniq) <(find . -type f -printf %f\\n|sort|uniq)
I think you can handle diff output.
Give this a try:
find -type f -print0 | grep -Fzxvf - requiredfiles.txt
The -print0 and -z protect against filenames which contain newlines. If your utilities don't have these options and your filenames don't contain newlines, you should be OK.
The repeated find to filter one file at a time is very expensive. If your file list is directly compatible with the output from find, run a single find and remove any matches from your list:
find . -type f |
fgrep -vxf - "$1"
If not, maybe you can massage the output from find in the pipeline before the fgrep so that it matches the format in your file; or, conversely, massage the data in your file into find-compatible.
I use this script and it works for me
#!/bin/bash
fileslist="$1"
found="Found:"
notfound="Not found:"
len=`cat $1 | wc -l`
n=0;
while read fn
do
# don't worry about this, i use it to display the file list progress
n=$((n + 1))
echo -en "\rLooking $(echo "scale=0; $n * 100 / $len" | bc)% "
if [ $(find / -name $fn | wc -l) -gt 0 ]
then
found=$(printf "$found\n\t$fn")
else
notfound=$(printf "$notfound\n\t$fn")
fi
done < $fileslist
printf "\n$found\n$notfound\n"
The line counts the number of lines and if its greater than 0 the find was a success. This searches everything on the hdd. You could replace / with . for just the current directory.
$(find / -name $fn | wc -l) -gt 0
Then i simply run it with the files in the files list being separated by newline
./search.sh files.list

bash delete directories based on contents

Currently I have multiple directories
Directory1 Directory2 Directory3 Directory4
each of these directories contain files (the files are somewhat cryptic)
what i wish to do is scan files within the folders to see if certain files are present, if they are then leave that folder alone, if the certain files are not present then just delete the entire directory. here is what i mean:
im searching for the files that have the word .pass. in the filename.
Say Directory 4 has that file that im looking for
Direcotry4:
file1.temp.pass.exmpl
file1.temp.exmpl
file1.tmp
and the rest of the Directories do not have that specific file:
file.temp
file.exmp
file.tmp.other
so i would like to delete Directory1,2 and3 But only keep Directory 4...
So far i have come up with this code
(arr is a array of all the directory names)
for x in ${arr[#]}
do
find $x -type f ! -name "*pass*" -exec rd {} $x\;
done
another way i have thought of doing this is like this:
for x in ${arr[#]}
do
cd $x find . -type f ! -name "*Pass*" | xargs -i rd {} $x/
done
SO far these don't seem to work, and im scared that i might do something wrong and have all my files deleted.....(i have backed up)
is there any way that i can do this? remember i want Directory 4 to be unchanged, everything in it i want to keep
To see if your directory contains a pass file:
if [ "" = "$(find directory -iname '*pass*' -type f | head -n 1)" ]
then
echo notfound
else
echo found
fi
To do that in a loop:
for x in "${arr[#]}"
do
if [ "" = "$(find "$x" -iname '*pass*' -type f | head -n 1)" ]
then
rm -rf "$x"
fi
done
Try this:
# arr is a array of all the directory names
for x in ${arr[#]}
do
ret=$(find "$x" -type f -name "*pass*" -exec echo "0" \;)
# expect zero length $ret value to remove directory
if [ -z "$ret" ]; then
# remove dir
rm -rf "$x"
fi
done

Resources