Counting directories and files in bash - bash

Simple counting dirs and files does not work. I am checking each file by -f and -d flag.
Where is a problem?
LOCATION=$1
for FILE in $(ls $LOCATION | egrep '^.{0,3}$');
do
echo "$FILE"
if [ -f $FILE ]
then
echo "its a file"
fi
if [ -d $FILE ]
then
echo "its a dir"
fi
done

shopt -s dotglob # count hidden files
for file in "$LOCATION/"*; do
[[ -f $file ]] && ((f++))
[[ -d $file ]] && ((d++))
done
echo "${d:-0} dirs"
echo "${f:-0} files"
without involving external utilities

Related

Bash script that remove duplicates

This works incorrectly. The script should delete only copies, but this script deletes all files.
#!/bin/bash
DIR=$1
if [[ -z "$DIR" ]]; then
echo "Error: files dir is undefined"
fi
files="$( find ${DIR} -type f )"
for file1 in $files; do
for file2 in $files; do
if cmp -s "$file1" "$file2"; then
rm $file2
fi
done
done
Found this on superuser:
#!/bin/bash
declare -A arr
shopt -s globstar
for file in **; do
[[ -f "$file" ]] || continue
read cksm _ < <(md5sum "$file")
if ((arr[$cksm]++)); then
echo "rm $file"
fi
done
I find answer:
#!/bin/bash
DIR=$1
if [[ -z "$DIR" ]]; then
echo "Error: files dir is undefined"
fi
files="$( find ${DIR} -type f )"
for file1 in $files; do
for file2 in $files; do
if cmp -s "$file1" "$file2"; then
rm $file2
fi
done
done

Passing ls command to a for loop breaks when trying to use command line arguments

Im writing a simple script to list all files in a directory and whether each file is in fact a file or if it is a directory. If it is a directory then it outputs how many files are in the directory.
#!/bin/bash
for filename in $(ls)
do
if [ -f "$filename" ]
then
printf "$filename - file\n"
fi
if [ -d "$filename" ]
then
count=$(ls "$filename" | wc -l)
printf "$filename - directory $count files\n"
fi
done
This works perfectly fine. But if I try to pass a command line argument (directory name) to ls then the script doesn't work. Does anyone know what causes this to break. Example below.
#!/bin/bash
for filename in $(ls $1)
do
if [ -f "$filename" ]
then
printf "$filename - file\n"
fi
if [ -d "$filename" ]
then
count=$(ls "$filename" | wc -l)
printf "$filename - directory $count files\n"
fi
done
$filename exists in directory/$filename. And you are check $filename in current (./) directory.
You should check against directory/$filename
if [ -z $1 ]; then
echo USAGE: basename $0 directory
exit
else
directory=$1
fi
...
for ...
if [ -f "$directory/$filename" ]; then
...
...
When you are passing directory as argument you shouldn't check files $filename but $1/$filename.
If you are sure that there will always be an argument you should use something like:
#!/bin/bash
for filename in $(ls "$1")
do
if [ -f "$1/$filename" ]
then
printf "$1/$filename - file\n"
fi
if [ -d "$1/$filename" ]
then
count=$(ls "$1/$filename" | wc -l)
printf "$1/$filename - directory $count files\n"
fi
done
If dirname as argument is optional you should check if there is argument and process as you want. My suggestion:
#!/bin/bash
if [ -z ${1+x} ];
then
echo "No argument";
else
echo "There is argument";
cd $1;
fi
for filename in $(ls)
do
if [ -f "$filename" ]
then
printf "$filename - file\n"
fi
if [ -d "$filename" ]
then
count=$(ls "$filename" | wc -l)
printf "$filename - directory $count files\n"
fi
done

Bash script loop through subdirectories and write to file

I have no idea I have spent a lot of hours dealing with this problem. I need to write script. Script should loop recursively through subdirectories in current directory. It should check files count in each directory. If file count is greater than 10 it should write all names of these file in file named "BigList" otherwise it should write in file "ShortList". This should look like
---<directory name>
<filename>
<filename>
<filename>
<filename>
....
---<directory name>
<filename>
<filename>
<filename>
<filename>
....
My script only works if subdirecotries don't include subdirectories in turn.
I am confused about this. Because it doesn't work as I expect. It will take less than 5 minutes to write this on any programming language for my.
Please help to solve this problem , because I have no idea how to do this.
Here is my script
#!/bin/bash
parent_dir=""
if [ -d "$1" ]; then
path=$1;
else
path=$(pwd)
fi
parent_dir=$path
loop_folder_recurse() {
local files_list=""
local cnt=0
for i in "$1"/*;do
if [ -d "$i" ];then
echo "dir: $i"
parent_dir=$i
echo before recursion
loop_folder_recurse "$i"
echo after recursion
if [ $cnt -ge 10 ]; then
echo -e "---"$parent_dir >> BigList
echo -e $file_list >> BigList
else
echo -e "---"$parent_dir >> ShortList
echo -e $file_list >> ShortList
fi
elif [ -f "$i" ]; then
echo file $i
if [ $cur_fol != $main_pwd ]; then
file_list+=$i'\n'
cnt=$((cnt + 1))
fi
fi
done
}
echo "Base path: $path"
loop_folder_recurse $path
I believe that this does what you want:
find . -type d -exec env d={} bash -c 'out=Shortlist; [ $(ls "$d" | wc -l) -ge 10 ] && out=Biglist; { echo "--$d"; ls "$d"; echo; } >>"$out"' ';'
If we don't want either to count subdirectories to the cut-off or to list them in the output, then use this version:
find . -type d -exec env d={} bash -c 'out=Shortlist; [ $(ls -p "$d" | grep -v "/$" | wc -l) -ge 10 ] && out=Biglist; { echo "--$d"; ls -p "$d"; echo; } | grep -v "/$" >>"$out"' ';'

I'm trying to create a script that'll delete all files which have a bitrate less than 130 kbps

to do that
#!/bin/bash
find ./ -name '*.mp3' | while read -r i; do
echo "----------------------------------------"
if [ $(mp3info -x "$i" | grep Audio | awk '{print $2}') < 130 ]
then
read -p "Delete? " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]
then
rm -f "$i" && echo "$i succesfully deleted!"
fi
fi
echo "----------------------------------------"
done
it stops with this output:
Error opening MP3: /Like A Prayer/Madonna - Act Of Contrition.mp3: No such file or directory
It looks like there is an error with the filepath, cause the leading dot is missing.
I think IFS is set to a value with ".".
Also, to compare integers, use [[ ]]:
#!/bin/bash
find ./ -name '*.mp3' | while IFS='' read -r i; do
echo "----------------------------------------"
if [[ $(mp3info -x "$i" | grep Audio | awk '{print $2}') -lt 130 ]]; then
read -p "Delete? " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
rm -f "$i" && echo "$i succesfully deleted!"
fi
fi
echo "----------------------------------------"
done
Btw I think you have to add your file to the question:
read -p "Delete $i? " -n 1 -r

Simulating the find command: why is my code not recursing correctly?

My assignment is to write a Unix shell script that asks the user for the name of a directory, and then works exactly like find.
Here is what I have so far:
#!/bin/bash
dir_lister()
{
cd "$1"
echo "$1"
list=$(ls -l ${1})
nolines=$(echo "$list" | awk 'END{printf "%d",NF}')
if [ $nolines -eq 2 ]
then
echo "$1"
return
fi
filelist=$(echo "$list" | grep ^-.*)
dirlist=$(echo "$list" | grep ^d.*)
filename=$(echo "$filelist"| awk '{printf "%s\n",$NF}')
present=$(pwd)
echo "$filename"| awk -v pres=$present '{printf "%s/%s\n",pres,$0}'
dirlist2=$(echo "$dirlist" | awk '{printf "%s\n",$NF}')
echo "$dirlist2" | while IFS= read -r line;
do
nextCall=$(echo "$present/$line");
dir_lister $nextCall;
cd ".."
done
cd ".."
}
read -p "Enter the name of the direcotry: " dName
dir_lister $dName
The problem is, after a depth of three directories, this script gets into an infinite loop, and I don't see why.
EDIT:
Here is the code i came up with after looking at your answer, it still doesn't go more than 1 directory depth:
#!/bin/bash
shopt -s dotglob # don't miss "hidden files"
shopt -s nullglob # don't fail on empty directories
list_directory()
{
cd "$2"
cd "$1"
##echo -e "I am called \t $1 \t $2"
for fileName in "$1/"*
do
##echo -e "hello \t $fileName"
if [ -d "$fileName" ];
then
echo "$fileName"
list_directory $fileName $2
else
echo "$fileName"
fi
done
}
read -p "Enter the direcotory Name: " dirName
var=$(pwd)
list_directory $dirName $var
Okay, that is completely the wrong way to list files in a directory (see ParsingLs). I'll give you the pieces and you should be able to put them together into a working script.
Put this at the top of your script:
shopt -s dotglob # don't miss "hidden files"
shopt -s nullglob # don't fail on empty directories
Then you can easily loop over directory contents with:
for file in "$directory/"* ; do
#...
done
Test if you have a directory:
if [ -d "$file" ] ; then
# "$file" is a directory, recurse...
fi

Resources