bash script can't check file exists - linux

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

Related

How to compare two stat values of the same file?

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

if condition in a vvv-init.sh file of vagrant [duplicate]

I typed the code the same as The Linux Command Line: A Complete Introduction, page 369
but prompt the error:
line 7 `if[ -e "$FILE" ]; then`
the code is like:
#!/bin/bash
#test file exists
FILE="1"
if[ -e "$FILE" ]; then
if[ -f "$FILE" ]; then
echo :"$FILE is a regular file"
fi
if[ -d "$FILE" ]; then
echo "$FILE is a directory"
fi
else
echo "$FILE does not exit"
exit 1
fi
exit
I want to figure out what introduced the error. How can I modify the code? My system is Ubuntu.
There must be a space between if and [, like this:
#!/bin/bash
#test file exists
FILE="1"
if [ -e "$FILE" ]; then
if [ -f "$FILE" ]; then
echo :"$FILE is a regular file"
fi
...
These (and their combinations) would all be incorrect too:
if [-e "$FILE" ]; then
if [ -e"$FILE" ]; then
if [ -e "$FILE"]; then
These on the other hand are all ok:
if [ -e "$FILE" ];then # no spaces around ;
if [ -e "$FILE" ] ; then # 1 or more spaces are ok
Btw these are equivalent:
if [ -e "$FILE" ]; then
if test -e "$FILE"; then
These are also equivalent:
if [ -e "$FILE" ]; then echo exists; fi
[ -e "$FILE" ] && echo exists
test -e "$FILE" && echo exists
And, the middle part of your script would have been better with an elif like this:
if [ -f "$FILE" ]; then
echo $FILE is a regular file
elif [ -d "$FILE" ]; then
echo $FILE is a directory
fi
(I also dropped the quotes in the echo, as in this example they are unnecessary)
The solution is pretty simple.
Just give space between if and the opening square braces like given below.
if [ -f "$File" ]; then
<code>
fi

Syntax error near unexpected token 'then'

I typed the code the same as The Linux Command Line: A Complete Introduction, page 369
but prompt the error:
line 7 `if[ -e "$FILE" ]; then`
the code is like:
#!/bin/bash
#test file exists
FILE="1"
if[ -e "$FILE" ]; then
if[ -f "$FILE" ]; then
echo :"$FILE is a regular file"
fi
if[ -d "$FILE" ]; then
echo "$FILE is a directory"
fi
else
echo "$FILE does not exit"
exit 1
fi
exit
I want to figure out what introduced the error. How can I modify the code? My system is Ubuntu.
There must be a space between if and [, like this:
#!/bin/bash
#test file exists
FILE="1"
if [ -e "$FILE" ]; then
if [ -f "$FILE" ]; then
echo :"$FILE is a regular file"
fi
...
These (and their combinations) would all be incorrect too:
if [-e "$FILE" ]; then
if [ -e"$FILE" ]; then
if [ -e "$FILE"]; then
These on the other hand are all ok:
if [ -e "$FILE" ];then # no spaces around ;
if [ -e "$FILE" ] ; then # 1 or more spaces are ok
Btw these are equivalent:
if [ -e "$FILE" ]; then
if test -e "$FILE"; then
These are also equivalent:
if [ -e "$FILE" ]; then echo exists; fi
[ -e "$FILE" ] && echo exists
test -e "$FILE" && echo exists
And, the middle part of your script would have been better with an elif like this:
if [ -f "$FILE" ]; then
echo $FILE is a regular file
elif [ -d "$FILE" ]; then
echo $FILE is a directory
fi
(I also dropped the quotes in the echo, as in this example they are unnecessary)
The solution is pretty simple.
Just give space between if and the opening square braces like given below.
if [ -f "$File" ]; then
<code>
fi

check file or user script assignment problems

Below is the assignment for the bash shell script I'm writing. I'm having a
problem with -u information being output even though I am using the -f option.
This class is a beginner class, so please bear with me. Would be grateful to
have some input on my code. Thanks for taking the time to check this out if you
do.
Here is the sample output:
[***#***]$ chk3 -f share
share is a directory and it is readable | writable | executable | abecker is
currently logged in their home directory is /students/abecker
Here is the usage
chk -f filepath
If filepath exists, output in readable sentences
if it is a symbolic link, say so. You do not have to continue and report the
permissions.
if it doesn't exist, say so. Don't continue to report the permissions
report what it is: file, directory, or something else, and continue to
report the permissions:
report what combination of read, write and execute access rights your
program has for the data. Note that this is dependent on who runs your
program. Do not attempt to do this by looking at the permissions as output
by ls -l. You must use the test operators to do this.
If filepath does not exist (and is not a symbolic link), your program should
report this instead in an informative error message. In this case, you
should exit with an error.
chk -u user
If the user exists on the system, report
the path to the user's home directory
if the user is currently logged in, say so. Otherwise, report when they last
logged in. (Take some care so that this is generated reliably and quickly.)
If the user doesn't exist, report this in an informative error message, and
exit with an error.
Here is my code
#!/bin/bash
if [ $# -gt 2 ]
then
echo "only 2 aruments can be used"
exit 1
fi
if [ "$1" != '-f' -a "$1" != '-u' ]
then
echo "first argument must be -f or -u"
exit 1
fi
if [ "$1" = '-f' -a $# -ne 2 ]
then
echo 'Usage: chk -f [FILEPATH]'
exit 1
fi
if [ "$1" = '-f' ]
then
FILEPATH=$2
fi
if [ -L "$FILEPATH" ]
then
echo "$FILEPATH is a symbolic link"
exit 0
elif [ -d "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a directory and it is \c"
elif [ -f "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a file and it is \c"
else
echo "I cannot determine what $(basename "$FILEPATH") is"
exit 1
fi
if [ -r "$FILEPATH" ]
then
echo -e "readable | \c"
fi
if [ -w "$FILEPATH" ]
then
echo -e "writable | \c"
fi
if [ -x "$FILEPATH" ]
then
echo -e "executable | \c"
fi
if [ "$1" = '-u' -a $# -eq 1 ]
then
USER=$LOGNAME
elif [ "$1" = '-u' -a $# -eq 2 ]
then
USER=$2
fi
USERINFO=$(grep "^$USER:" /etc/passwd)
if ! grep "^$USER:" /etc/passwd > /dev/null
then
echo "$USER cannot be found on this system"
exit 1
fi
if ! who | grep "^$USER " > /dev/null
then
echo "$USER is not currently logged on and last logged on"
echo "$(last -1 "$USER")"
exit 0
else
echo "$USER is currently logged in their home directory is"
echo "$(echo "$USERINFO" | awk -F":" '{print $6}')"
fi
You're not putting the processing of different options into different blocks; the code simply passes through everything for all options.
e.g. for the -f option, you have:
if [ "$1" = '-f' ]
then
FILEPATH=$2
fi
and then process all the options for filepath, without putting them into the if statement, so if you pass in either -f or -u, it always passes into the code:
if [ -L "$FILEPATH" ]
then
echo "$FILEPATH is a symbolic link"
exit 0
elif
If you don't want to break your program into functions, what you want to do is put all the code relating to processing the -f option into the same if-statement, somewhat like:
if [ "$1" = '-f' ]
then
FILEPATH=$2
if [ -L "$FILEPATH" ]
then
echo "$FILEPATH is a symbolic link"
exit 0
elif [ -d "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a directory and it is \c"
elif [ -f "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a file and it is \c"
else
echo "I cannot determine what $(basename "$FILEPATH") is"
exit 1
fi
if [ -r "$FILEPATH" ]
then
echo -e "readable | \c"
fi
if [ -w "$FILEPATH" ]
then
echo -e "writable | \c"
fi
if [ -x "$FILEPATH" ]
then
echo -e "executable | \c"
fi
fi # if [ "$1" = '-f' ]
Similarly for the -u option, you need to break it into multiple statements and then process all the options for the statement:
if [ "$1" = 'u' ]
then
if [ $# -eq 1 ]
then
USER=$LOGNAME
elif [ $# -eq 2 ]
then
USER=$2
fi
USERINFO=$(grep "^$USER:" /etc/passwd)
if ! grep "^$USER:" /etc/passwd > /dev/null
then
echo "$USER cannot be found on this system"
exit 1
fi
if ! who | grep "^$USER " > /dev/null
then
echo "$USER is not currently logged on and last logged on"
echo "$(last -1 "$USER")"
exit 0
else
echo "$USER is currently logged in their home directory is"
echo "$(echo "$USERINFO" | awk -F":" '{print $6}')"
fi
fi # if [ "$1" = '-u' ]
I would, however recommend putting the code that acts on the options into shell functions, which makes it much easier to read the code; e.g.
filepath() {
FILEPATH="$1"
if [ -L "$FILEPATH" ]
then
echo "$FILEPATH is a symbolic link"
exit 0
elif [ -d "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a directory and it is \c"
elif [ -f "$FILEPATH" ]
then
echo -e "$(basename "$FILEPATH") is a file and it is \c"
else
echo "I cannot determine what $(basename "$FILEPATH") is"
exit 1
fi
if [ -r "$FILEPATH" ]
then
echo -e "readable | \c"
fi
if [ -w "$FILEPATH" ]
then
echo -e "writable | \c"
fi
if [ -x "$FILEPATH" ]
then
echo -e "executable | \c"
fi
}
And then for the processing code:
if [ "$1" = '-f' ]
then
filepath "$2"
fi
and something similar for the -u option.

Parse folder name in bash

I have folders with the naming convention:
yearmonthday_jobcode_descriptor
ie 20101007_GR1234_Whatsit
I'm attempting to write a script that does a few things:
move any folders where the date is older than $X into ./old
move any folders where the date is in the future ./old
move any folders that dont conform to the convention ./old
delete any folders in ./old where the date is older than $X + $Y
Here is my current loop, as you can see my bash scripting is pretty mediocre.
#!/bin/bash
for file in $1/*; do
if [ -d $file ]; then
echo $(echo $file | grep -oE "[0-9]{8}_[A-Z]*[0-9]*_?.*")
fi
done
I can handle all the looping, my main concern at the moment is how to extract the folder names into variables that can be compared for dates. The current regex above just checks for conformity to the convention.
You can use the cut command to tokenize the string (-d specifies the delimiter, -f specifies which field you want to keep) -
echo 20101007_GR1234_Whatsit | cut -d'_' -f1
gives
20101007
From there, you can use the date command to parse the date-
foo=`date -d 20101007 +%s`
will convert the date string to the epoch time (seconds since Jan 1, 1970) which can then be easily compared.
If you don't want to mess with epoch time, you can call date multiple times to parse the different parts -
day=`date -d 20101007 +%d`
month=`date -d 20101007 +%m`
year=`date -d 20101007 +%Y`
but it will make your comparisons a little more complicated.
$ foo="20101007_GR1234_Whatsit"
$ echo ${foo%%_*}
20101007
#!/bin/bash
ls -d ????????_[A-Z][A-Z]*_* | awk 'BEGIN{
x=mktime("2010 10 08 00 00 00") # set your X here, whatever it is
y=mktime("2010 10 02 00 00 00") # set your Y here, whatever it is
olddir="/tmp/old"
}
{
yr=substr($0,1,4) # get year
mth=substr($0,5,2) # get month
day=substr($0,7,2) # get day
t= mktime(yr" "mth" "day" 00 00 00") #convert to secs for comparison
if (t < 0) { print "Wrong format: "$0 ;next}
if ( t > x){
print $0 " is later than X: "x
}else{
print "moving "$0" to old"
cmd="mv $0 "oldir
print cmd
#system(cmd) #uncomment to do actual moving
}
if ( t> (x+y) ){
..............
}
}'
This was my final solution, for anyone attempting to do this in the future:
#!/bin/bash
ARCHIVE="old"
NOW=`date +%s`
GRACE_PERIOD=$((60*60*24*7*2))
CUTOFF=$(($NOW-$GRACE_PERIOD))
DEADLINE=$(($CUTOFF-$GRACE_PERIOD))
function getos {
UNAME=`uname`
PLATFORM="unknown"
if [[ "$UNAME" == 'Linux' ]]; then
echo 'linux'
elif [[ "$UNAME" == 'Darwin' ]]; then
echo 'osx'
else
echo 'linux'
fi
}
function dateToTimestamp {
DATE="$1"
OS=`getos`
TIMESTAMP='empty'
if [[ "$OS" == 'linux' ]]; then
TIMESTAMP=`date -d $DATE +%s`
elif [[ "$OS" == 'osx' ]]; then
TIMESTAMP=`date -j -f "%Y%m%d" "$DATE" "+%s"`
fi
echo $TIMESTAMP
}
function movetoarchive {
ITEM="$1"
if [ ! -d "$ARCHIVE" ]; then
mkdir $ARCHIVE
fi
FOLDER=`date +"%Y%m%d"`
if [ ! -d "$ARCHIVE/$FOLDER" ]; then
mkdir $ARCHIVE/$FOLDER
fi
mv $ITEM $ARCHIVE/$FOLDER/$ITEM
}
function deletefromarchive {
ITEM="$1"
WD=`pwd`
MATCH=$(echo $WD | grep -oE ".*?/$WORKING_DIR/$ARCHIVE$")
if [ -n "$MATCH" ]; then
rm -rf $ITEM;
fi
}
WORKING_DIR="$1";
cd "$WORKING_DIR"
for file in *; do
if [ -d $file ]; then
if [ "$file" != "$ARCHIVE" ]; then
MATCH=$(echo $file | grep -oE "[0-9]{8}-[A-Z]*[0-9]*-?.*")
if [ -z "$MATCH" ]; then
movetoarchive $file
else
DATE=$(echo $file | cut -d'-' -f1);
TIMESTAMP=`dateToTimestamp $DATE`
if [[ "$TIMESTAMP" > "$NOW" ]]; then
movetoarchive $file
elif [[ "$TIMESTAMP" < "$CUTOFF" ]]; then
movetoarchive $file
fi
fi
fi
fi
done
cd $ARCHIVE
for file in *; do
if [ -d $file ]; then
MATCH=$(echo $file | grep -oE "^[0-9]{8}$")
if [ -z "$MATCH" ]; then
deletefromarchive $file
else
TIMESTAMP=`dateToTimestamp $file`
if [[ "$TIMESTAMP" > "$NOW" ]]; then
deletefromarchive $file
elif [[ "$TIMESTAMP" < "$DEADLINE" ]]; then
deletefromarchive $file
fi
fi
fi
done

Resources