comparing folder size in bash? - bash

actual_size="du -h $some_folder";
y='eval $actual_size`
max_size=2MB
if [[ y -lt $max_size ]]; then
echo `du -h $some_folder`
cp "$file" $some_folder` #$file points to some file
Whats wrong with above lines of code? I also tried
if [[ $(stat -c %s $some_folder) -lt $max_size ]]; then
any help would be appreciated, Thnaks!

In order to use POSIX du in a script it helps to have a wrapper function which only prints the output of its second to last field. This can be accomplished with awk, or here is how I do it using eval to expand the second to last argument to a function:
#!/bin/sh
duprint()
{
if [ "$#" -gt 1 ]
then printf "%s" $( eval printf '$'$(( $# - 1 )))
fi
}
duwrap()
{
duprint $(du -s $# 2>/dev/null)
}
duwrap "$#"
This is useful for instance if you want the total size of several directories in anticipation of copying them over to a new filesystem:
./duwrapper /bin /etc /lib /boot
Finally, one can use their du wrapper function to compare directory sizes:
if [ "$(duwrap /lib)" -gt "$(duwrap /bin)" ]
then echo "/lib is greater"
fi

Related

Bash script loop through subdirectories and write to file without using find,ls etc

Sorry for asking this question again. I have already received answer but with using find but unfortunately I need to write it without using any predefined commands.
I am trying to write a script that will loop recursively through the subdirectories in the current directory. It should check the file 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 subdirectories don't include subdirectories in turn.
I am confused about this because it doesn't work as I expect.
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
How can I modify my script to produce the desired output?
This bash script produces the output that you want:
#!/bin/bash
bigfile="$PWD/BigList"
shortfile="$PWD/ShortList"
shopt -s nullglob
loop_folder_recurse() {
(
[[ -n "$1" ]] && cd "$1"
for i in */; do
[[ -d "$i" ]] && loop_folder_recurse "$i"
count=0
files=''
for j in *; do
if [[ -f "$j" ]]; then
files+="$j"$'\n'
((++count))
fi
done
if ((count > 10)); then
outfile="$bigfile"
else
outfile="$shortfile"
fi
echo "$i" >> "$outfile"
echo "$files" >> "$outfile"
done
)
}
loop_folder_recurse
Explanation
shopt -s nullglob is used so that when a directory is empty, the loop will not run. The body of the function is within ( ) so that it runs within a subshell. This is for convenience, as it means that the function returns to the previous directory when the subshell exits.
Hopefully the rest of the script is fairly self-explanatory but if not, please let me know and I will be happy to provide additional explanation.

Copying recursively from one directory to another

I have three variables:
taille : number of iterations
racine : directory from where to copy
rep : directory where to copy<>br
So the code is supposed to start copying recursively from racine to rep, number of files copied is restricted by taille. I cant seem to make the cp command to work, and i dont know how to make the recursivity to work either. My code is as follows
if [ -z "$1" ]
then
taille=0
else
taille=$1
fi
if [ -z "$2" ]
then
racine=`pwd`
else
racine=$2
fi
if [ -z "$3" ]
then
rep="test2"
else
rep=$3
fi
count=0
for i in `ls $racine`;
do
if [ $count -lt $((taille+1)) ]
then
echo $i
`cp $i test2`
fi
count=$((count+1))
done
Can somebody help me?
The following quick and dirty test script seems to do what you want (more or less).
I'm trying to convey a general approach, not provide a complete solution. What's below will misbehave (look at the mkdir -p line) given a relative path (e.g. ../../) for the source directory, so you may have to give that problem a little thought. I hope it conveys the idea clearly enough though.
#!/bin/bash
racine="$1"
rep="$2"
declare -i taille=3
declare -i count=0
while read -r -d $'\0'; do
if [ $count -ge $taille ]; then
break
fi
if [ -d "$REPLY" ]; then
mkdir -p "$rep/$REPLY";
else
cp "$REPLY" "$rep"
fi
count=$((count+1))
done < <(find $racine -print0)
A couple of things:
You don't need the back ticks around the copy command, so just cp $i test2.
You increment a variable like this: count=`expr $count + 1`.
EDIT: The count=$((count+1)) syntax works if you are using Bash.

“unary operator expected” in shell script

I need a script to keep polling "receive_dir" directory till "stopfile" get written in the directory.
This has to run despite empty directory.
So far i have this but fails if receive_dir is empty with no files with "unary operator expected". Help !!
#!/usr/bin/ksh
until [ $i = stopfile ]
do
for i in `ls receive_dir`; do
time=$(date +%m-%d-%Y-%H:%M:%S)
echo $time
echo $i;
done
done
This will do what you ask for (loop until the stop file exist). I added a "sleep 1" to lower resource usage. It's also good practice to use "#!/usr/bin/env ksh" as shebang.
#!/usr/bin/env ksh
until [ -e receive_dir/stopfile ]
do
time=$(date +%m-%d-%Y-%H:%M:%S)
echo $time
sleep 1
done
If you have empty dir, the
until [ $i = stopfile ]
is evaluated as
until [ = stopfile ]
what is ofcourse syntax error.
One comment: Never parse output from ls.
#!/bin/bash
do_something() {
echo $(date +%m-%d-%Y-%H:%M:%S) "$1"
}
dir="."
until [[ -f "$dir/stopfile" ]]
do
find "$dir" -print0 | while IFS= read -r -d '' filename
do
do_something "$filename"
done
done
or (much slower)
do_something() {
echo $(date +%m-%d-%Y-%H:%M:%S) "$1"
}
export -f do_something
dir="."
until [[ -f "$dir/stopfile" ]]
do
find "$dir" -exec bash -c 'do_something "{}"' \;
done
You're evaluating nothing, and the 'test' isn't able to evaluate it.
~> [ $empty_var = stopfile ]
-bash: [: =: unary operator expected
First, don't parse ls:
http://mywiki.wooledge.org/BashPitfalls#for_i_in_.24.28ls_.2A.mp3.29
EDIT: Part of the issue is your loop is actually doing the test, try something like this (assuming receive_dir is relative):
#user000001 is right; my original find example would suffer the same issue, so this:
for i in receive_dir/*
do
time=$(date +%m-%d-%Y-%H:%M:%S)
echo $time
echo $i
[ $i = stopfile ] && break
done
EDIT: Adding in another example based on your comment:
How about this...
FOUND="N"
while [ "${FOUND}" = "N" ]
do
for i in receive_dir/*
do
time=$(date +%m-%d-%Y-%H:%M:%S)
echo $time
echo $i
[ "$i" = stopfile ] && FOUND="Y"
done
sleep 60
done
Another option is to use inotifywait to monitor the status of the directory. For example, this script will run the loop until the file "stopfile" is touched.
until inotifywait "receive_dir" | grep "stopfile"
do
echo "running"
done
echo "done"
The advantage is that these is no busy loop, and that you don't have to repeatedly call the (potentially expensive) find command

BASH if directory contains files or doesn't

I created a script that executes some commands based on a condition. If directory contains files then run "screen -r" anything else run "screen". Problem is screen sometimes gets executed even when the directory contains files.
if [ "$(ls -A $DIR)" ]; then
screen -r
else
screen
fi
What I want to do is refine it and break it down into two statements. If directory contains files then run screen-r" & if a directory doesn't contain files run "screen"
if [ "$(ls -A $DIR)" ]; then
screen -r
fi
&
if ["$(directory without files)"] ; then
screen
fi
Maybe even a statement that executes based on # of file. If directory contains X amount of files.
Can somebody help me out with this? I hope I explained what I want thoroughly.
Thanks,
Geofferey
Again thank you for all your help, I got it all working now. Here is the final script. It's for the iPhone and an app I'm making called MobileTerm Backgrounder.
#Sets up terminal environment?
if [[ $TERM = network || -z $TERM ]]; then
export TERM=linux
fi
# This script automatically runs screen & screen -r (resume) based on a set of conditions.
# Variable DIR (variable could be anything)
DIR="/tmp/screens/S-mobile"
# if /tmp/screens/S-mobile list files then run screen -x
if [ "$(ls -A $DIR)" ]; then
screen -x
fi
#if /tmp/screens/S-mobile contains X amount of files = to 0 then run screen -q
if [ $(ls -A "$DIR" | wc -l) -eq 0 ]; then
screen -q
fi
find could be helpful here:
if [[ $(find ${dir} -type f | wc -l) -gt 0 ]]; then echo "ok"; fi
UPD: what is -gt?
man bash -> / -gt/:
arg1 OP arg2
OP is one of -eq, -ne, -lt, -le, -gt, or -ge. These arithmetic binary operators return true if arg1 is equal to, not
equal to, less than, less than or equal to, greater than, or greater than or equal to arg2, respectively. Arg1 and arg2
may be positive or negative integers.
So, -gt is "greater than" boolean function.
I would use ls and wc this way:
if [ $(ls -A "$DIR" | wc -l) -gt 0 ]; then
screen -r
else
screen
fi
You have to double quote the $DIR variable otherwise you'll have problems with directory names that contains spaces.
This seems simpler to me
if [ `ls -1q /some/dir | wc -l` -eq 0 ]; then echo "huzzah"; fi
The ls outputs one line per file and wc counts the lines

Using a bash script to grep an integer and compare it to another

I have a bash script to copy files from one location to another if the score within the file is less than 36.
I run this script once a month, and it worked before but now I'm getting the error:
line 5: [: -lt: unary operator expected
Here is the script:
#!/bin/bash
for f in `ls $1/*.html`
do
score=`grep -o -P '(?<=ADJ. SCORE: )-?[0-9]?[0-9]' $f`
if [ $score -lt 36 ]
then cp $f $2
fi
done
I'm not sure if the OS matters; I'm using OS X 10.7 and I've troubles in the past with my bash scripts that otherwise work great on Linux boxes.
Thanks in advance!
sehe is right,
Or you can do:
if [[ $score < 36 ]]
then
cp "$f" "$2"
fi
This happens when there was no match, $score is then the empty string.
A simple fix:
#!/bin/bash
for f in `ls $1/*.html`
do
score=`grep -o -P '(?<=ADJ. SCORE: )-?[0-9]?[0-9]' $f`
if [ -z $score ]
then
echo "No match in '$f'"
else
if [ "$score" -lt 36 ]
then
cp "$f" "$2"
fi
fi
done
I think you also need to be more aware of quoting requirements in shell scripting.
On my mac running Mountain Lion version 10.8.4 I don't see -P option with grep. So you can use perl for instead (re-using most of your script):
#!/bin/bash
for f in "${1}"/*.html; do # Don't parse ls
score=$(perl -ne "print $& if /(?<=ADJ. SCORE: )-?[0-9]?[0-9]/" "$f")
if [ "$score" -lt 36 ]; then
cp "$f" $2
fi
done

Resources