Screenshot of the my code
I am trying to make a shell program that tells me when a file has been created, when it has been modified, and when it has been deleted. I think I can solve this but my only issue is that I cant compare the stat values. It tells me that I have "too many arguments". Any help would be much appreciated :)
#!/bin/bash
run=yes
if [ -f $1 ]
then
while [ run=yes ]
do
time1=$(stat -c %y $1)
time2=$(stat -c %y $1)
if [ ! $time2 ]
then
echo "The file "$1" has been deleted."
run=no
elif [ $time2 -gt $time1 ]
then
echo "The file "$1" has been modified."
run=no
fi
done
else
while [ run=yes ]
do
sleep 2
if [ -f $1 ]
then
echo "The file "$1" has been created."
run=no
fi
done
fi
The output of static -c %y ... includes spaces, which is what the shell uses to separate arguments. When you then run:
if [ ! $time2 ]; then
This translates into something like:
if [ ! 2017-09-02 08:57:19.449051182 -0400 ]; then
Which is an error. The ! operator only expects a single argument. You could solve it with quotes:
if [ ! "$time2" ]; then
Or by using the bash-specific [[...]]] conditional:
if [[ ! $time2 ]]; then
(See the bash(1) man page for details on that second solution).
Separately, you're not going to be able to compare the times with -gt as in:
elif [ $time2 -gt $time1 ]
This (a) has the same problem as the earlier if statement, and (b) -gt can only be used to compare integers, not time strings.
If you were to use %Y instead of %y, you would get the time as an integer number of seconds since the epoch, which would solve all of the above problems.
The code is now working and I thought I would share the final result if anyone wanted to know.
#!/bin/bash
run=true
if [ -f $1 ]
then
while [ "$run" = true ]
do
time1=$(stat -c %Y $1 2>/dev/null)
sleep $2
time2=$(stat -c %Y $1 2>/dev/null)
if [ ! "$time2" ]
then
echo "The file "$1" has been deleted."
run=false
elif [ $time2 -gt $time1 ]
then
echo "The file "$1" has been modified."
run=false
fi
else
while [ "$run" = true ]
do
sleep 2
if [ -f $1 ]
then
echo "The file "$1" has been created."
run=false
fi
done
fi
Related
Hello everybody: I have a bash script that check if a file exists and with that information create a new file. The part of the script with the problem is this..
#!/bin/bash
FECHA=$(date +%Y-%m-%d)
FECHAD=$(date +%d)
FECHAM=$(date +%m)
FECHAA=$(date +%Y)
DIRECTORY="/home/usuario/Build_WRF/DATA/"
FILE1=$DIRECTORY"GFS_24"
FILE2=$DIRECTORY"GFS_48"
FILE3=$DIRECTORY"GFS_72"
FILE4=$DIRECTORY"GFS_96"
FILE5=$DIRECTORY"GFS_120"
FILE6=$DIRECTORY"GFS_144"
FILE7=$DIRECTORY"GFS_168"
FILE8=$DIRECTORY"GFS_192"
FILE9=$DIRECTORY"GFS_216"
FILE10=$DIRECTORY"GFS_240"
if [ -f $FILE10 ]; then
dias="10 days"
echo $dias
elif [ -f $FILE9 ]; then
dias="9 days"
echo $dias
elif [ -f $FILE8 ]; then
dias="8 days"
echo $dias
elif [ -f $FILE7 ]; then
dias="7 days"
echo $dias
elif [ -f $FILE6 ]; then
dias="6 days"
echo $dias
elif [ -f $FILE5 ]; then
dias="5 days"
echo $dias
elif [ -f $FILE4 ]; then
dias="4 days"
echo $dias
elif [ -f $FILE3 ]; then
dias="3 days"
echo $dias
elif [ -f $FILE2 ]; then
dias="2 days"
echo $dias
elif [ -f $FILE1 ]; then
dias="1 day"
echo $dias
else
exit
fi
FECHAF=$(date +%Y-%m-%d --date="$dias")
FECHAFD=$(date +%d --date="$dias")
FECHAFM=$(date +%m --date="$dias")
FECHAFA=$(date +%Y --date="$dias")
The files exists, for example today I have the file GFS_72, the script should return 3 days, but return nothing.
I'm missing something.
Cheers.
I tried your script and it worked for me.
Please check that /home/usuario/Build_WRF/DATA/GFS_72 actually exists and the directory /home/usuario/Build_WRF/DATA/ is readable and executable as the user the script is executing as.
For instance, as a debugging technique, insert at the top of the script:
ls -la /home/usuario/Build_WRF/DATA/ || {
echo "Directory not readable as user $USER" >&2
id
exit 1
}
Further style issues / Bash tips:
When using [...], you should always quote the arguments to prevent split on space.
if [ -f "$FILE10" ]; then
dias="10 days"
echo "$dias"
elif ...
If you know this is going to be running in Bash, you should use [[...]] instead, where you don't have to use quoting (except on the RHS of =).
[[...]] is also marginally faster than [...] in most cases, and it has more features like glob comparisons, regex, and more.
if [[ -f $FILE10 ]]; then
dias="10 days"
echo "$dias"
elif ...
Or perhaps, to avoid the ifs
dias="$(( $(echo $DIRECTORY/GFS_* | sed 's![^ ]*GFS_!!g; s! !\n!g' | sort -n | tail -n 1) / 24 )) days"
Considering you go from 10 to 1, I would do it shorter and without external calls, something like this:
DIRECTORY="./DATA"
dias=""
for (( i=10; i>=1; i-- ))
do
[ -f "${DIRECTORY}/GFS_$((i * 24))" ] && dias="$i days" && break
done
[ -z "$dias" ] && exit
echo $dias
A few months back, I installed a utility on my mac so that instead of typing something like this:
vim /type/path/to/the/file
I could just type:
v file
9 times out of 10 it would guess the right file based on the past history, similar to the way autojump works. And instead of typing in vim I can just type the letter v.
I can't remember how I set this up though. It still works on my mac but I don't see anything in my .bash_profile that shows how I did that.
I'm trying to get this to work on my linux box.
This can be found here
https://github.com/rupa/v/blob/master/v
it should work in Linux too. It is a bash script that uses the viminfo
history file to fill in partial strings.
It can be installed on macOS with brew install v
Ah! I found the command with which. Here is the magical script. I can't determine where I got it.
#!/usr/bin/env bash
[ "$vim" ] || vim=vim
[ $viminfo ] || viminfo=~/.viminfo
usage="$(basename $0) [-a] [-l] [-[0-9]] [--debug] [--help] [regexes]"
[ $1 ] || list=1
fnd=()
for x; do case $x in
-a) deleted=1;;
-l) list=1;;
-[1-9]) edit=${x:1}; shift;;
--help) echo $usage; exit;;
--debug) vim=echo;;
--) shift; fnd+=("$#"); break;;
*) fnd+=("$x");;
esac; shift; done
set -- "${fnd[#]}"
[ -f "$1" ] && {
$vim "$1"
exit
}
while IFS=" " read line; do
[ "${line:0:1}" = ">" ] || continue
fl=${line:2}
[ -f "${fl/\~/$HOME/}" -o "$deleted" ] || continue
match=1
for x; do
[[ "$fl" =~ $x ]] || match=
done
[ "$match" ] || continue
i=$((i+1))
files[$i]="$fl"
done < "$viminfo"
if [ "$edit" ]; then
resp=${files[$edit]}
elif [ "$i" = 1 -o "$list" = "" ]; then
resp=${files[1]}
elif [ "$i" ]; then
while [ $i -gt 0 ]; do
echo -e "$i\t${files[$i]}"
i=$((i-1))
done
read -p '> ' CHOICE
resp=${files[$CHOICE]}
fi
[ "$resp" ] || exit
$vim "${resp/\~/$HOME}"
I'm trying to write recursive scripts in bash that receive as an argument a single path and prints the depth of the directory tree rooted at this path.
This is the list_dirs.sh script:
ls -l $dir | grep dr..r..r.. | sed 's/.*:...\(.*\)/\1/'
And this is the isdir.sh script:
if [ -d $1 ]; then
echo 1
elif [ -e $1 ]; then
echo 0
else
echo -1
fi
They both work good.
This is the script dir_depth.sh that I wrote that doesn't work:
if [ $# -eq 0 ]; then
echo "Usage: ./dir_depth.sh <path>"
exit1
fi
x=`source isdir.sh $1`
if [ $x -eq -1 ]; then
echo "no such path $1"
fi
dir=$1
maxD=0
dirs=`source list_dirs.sh`
for f in $dirs
do
if [ $x -ne 0 ]; then
x=`dir_depth.sh $f`
if [ "$x" -eq "$maxD" ]; then
maxD=x;
fi
fi
echo $f
done
echo $((maxD++))
I'm really new to bash scripting and I don't know how to debug or what's wrong in my script.
Some missing items are:
If you have a directory parent/child/ and run list_dirs.sh parent/, it will output child. You then try to look up child/ in the current directory instead of parent/child/.
You do echo $f for debug purposes and echo $((maxD++)) to return a result. They are being confused for each other. Use >&2 to write errors and debug messages to stderr.
echo $((maxD++)) is a classic error equivalent to return x++. You return the number, and then increment a variable that's no longer used.
[ "$x" -eq "$maxD" ] makes no sense. Use -ge since you're trying to find the max.
Here's dir_depth.sh with these changes in place:
if [ $# -eq 0 ]; then
echo "Usage: ./dir_depth.sh <path>" >&2
exit 1
fi
x=`source ./isdir.sh $1`
if [ $x -eq -1 ]; then
echo "no such path $1" >&2
fi
dir=$1
dirs=`source ./list_dirs.sh`
maxD=0
for f in $dirs
do
if [ $x -ne 0 ]; then
x=`./dir_depth.sh "$1/$f"`
if [ "$x" -ge "$maxD" ]; then
maxD="$x";
fi
fi
echo $f >&2
done
echo $((maxD+1))
I made this code
if [ $# -ne 2 ]; then
echo "use $0 dir1 dir2"
exit 1
fi
if [ ! -d $1 ]; then
echo "$1 nu este un director"
exit 1
fi
if [ ! -d $2 ]; then
echo "$2 nu este un director "
exit 1
fi
a=0
k=1
for $1 in `ls`
do
if [ -f $1 ]; then
a=`exp $a + 1`
fi
done
echo "Ther are $a file "
I want to compare two folders and the folder are arguments to the command line.. it should be something like this : ./script.sh dir1 dir2
But i have this eror :
**./director.sh: line 29: `$1': not a valid identifier
**
I want to count the file from dir1 who is argument to the command line.
Can someone help me please ?
This is the main error:
for $1 in `ls`
$1 is not a valid variable name
don't parse ls
Do this instead
for file in *
Also, quote your variables: you want to protect your script from any filenames containing whitespace.
if [ ! -d "$1" ]
if [ -f "$file" ]
Instead of this part:
a=0
k=1
for $1 in `ls`
do
if [ -f $1 ]; then
a=`exp $a + 1`
fi
done
do this:
a=$(ls "$1" | wc -l)
If you absolutely have to use your looping, change it like this:
a=0
for i in ${1}/*
do
if [ -f "$i" ]; then
let a=a+1
fi
done
echo "There are $a files"
A recent test I took had a question on the output of the following bash command:
var=; [ -n $var ]; echo $?; [ -z $var ]; echo $?
The results are 0 and 0, indicating the return codes for both unary operators had no errors. This means $var resolves to both null (empty) and 'non-null' (not empty), correct?
How is this possible?
No, it means that [ is unfixably broken. In both cases $var evaluates to nothing, and the commands simply execute [ -n ] and [ -z ] respectively, both of which result in true. If you want to test the value in the variable itself then you must quote it to have it handled properly.
$ var=; [ -n "$var" ]; echo $?; [ -z "$var" ]; echo $?
1
0
You will need to surround $var:
$ [ -n "$var" ]; echo $?
1
Remember that the closing square bracket is just syntactic sugar: you don't need it. That means your line:
$ [ -n $var ]; echo $?
will expand to (since $var is empty):
$ [ -n ]; echo $?
The above asks: "is the string ']' non-empty?" And the answer is yes.
It's surprising indeed. If you were to try the same with the bashism [[ syntax, you'd get 1 and 0 as results. I reckon this is a bug.
var=; [[ -n $var ]]; echo $?; [[ -z $var ]]; echo $?
or, as Ignacio points out and as in fact I have always been doing intuitively, with defensive coding and quoting:
var=; [[ -n "$var" ]]; echo $?; [[ -z "$var" ]]; echo $?
It's surprising to me that [ behaves this way, because it's a builtin:
$ type [
[ is a shell builtin
Just did a little test and the system command [ behaves in the same broken way as the builtin. So probably it's buggy for compatibility:
var=; /usr/bin/\[ -n $var ]; echo $?; /usr/bin/\[ -z $var ]; echo $?