Syntax error near unexpected token 'then' - linux

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

Related

Trying to implement CASE in my shell script

I'm trying to add options to my little safe delete script. For example, I can do ./sdell -s 100 and it will delete files with size above 100 kbs. Anyway, I'm having problems with my safe guard function.
#!/bin/bash
#Purpose = Safe delete
#Created on 20-03-2018
#Version 0.8
#jesus,i'm dumb
#START
##Constants##
dir="/home/cunha/LIXO"
#check to see if the imputs are a files#
for input in "$#"; do
if ! [ -e "$input" ]; then
echo "Input is NOT a file!"
exit 1
fi
done
###main###
case $1 in
-r) echo "test option -r"
;;
*) if [[ -f "$dir/$fwe.tar.bz2" ]]; then
echo "File already exists."
if [[ "$file" -nt "$2" ]]; then
echo "Removing older file." && rm "$dir"/"$fwe.tar.bz2" && tar -czPf "$fwe.tar.bz2" "$(pwd)" && mv "$fwe.tar.bz2" "$d$
fi
else
echo "Ziping it and moving." && tar -czPf "$fwe.tar.bz2" "$(pwd)" && mv "$fwe.tar.bz2" "$dir"
fi
done
;;
esac
The problem is when I call ./sdell -r file1.txt, it says that the input is not a file.
Here is the script without the case, 100% working before having options.
#!/bin/bash
#Purpose = Safe delete
#Created on 20-03-2018
#Version .7
#START
##Constants##
dir="/home/cunha/LIXO"
#check to see if the imputs are a files#
for input in "$#"; do
if ! [ -e "$input" ]; then
echo "Input is NOT a file!"
exit 0
fi
done
###main###
##Cycle FOR so the script accepts multiple file inputs##
for file in "$#"; do
fwe="${file%.*}"
#IF the input file already exist in LIXO#
if [[ -f "$dir/$fwe.tar.bz2" ]]; then
echo "File already exists."
#IF the input file is newer than the file thats already in LIXO#
if [[ "$file" -nt "$2" ]]; then
echo "Removing older file." && rm "$dir"/"$fwe.tar.bz2" && tar -czPf "$fwe.tar.bz2" "$(pwd)" && mv "$fwe.tar.bz2" "$d$
fi
else
echo "Ziping it and moving." && tar -czPf "$fwe.tar.bz2" "$(pwd)" && mv "$fwe.tar.bz2" "$dir"
fi
done
The message you are seeing is unrelated to the case..esac, construct, it is printed by this section:
for input in "$#"; do
if ! [ -e "$input" ]; then
echo "Input is NOT a file!"
exit 1
fi
done
which expands all command-line parameters ($#), including the "-r", and exits the script because "-r" is not a file. The case..esac is never reached. You can run your script with
bash -x file.sh -r test
so that you can see exactly which lines are being executed.
The snippet below probably does what you want, processing all arguments sequentially:
#!/bin/bash
while [ ! -z $1 ]; do
case $1 in
-r) echo "option R"
;;
-f) echo "option F"
;;
*) if [ -f $1 ]; then echo "$1 is a file." ; fi
;;
esac
shift
done
Consider checking if -r has been passed before trying other options and use shift if it was:
#!/usr/bin/env sh
dir="/home/cunha/LIXO"
case $1 in
-r) echo "test option -r"
shift
;;
esac
#check to see if the imputs are a files#
for input in "$#"; do
echo current input: "$input"
if ! [ -e "$input" ]; then
echo "Input $input is NOT a file!"
exit 1
fi
done
if [[ -f "$dir/$fwe.tar.bz2" ]]; then
echo "File already exists."
if [[ "$file" -nt "$2" ]]; then
echo "Removing older file..."
# add stuff
fi
else
echo "Ziping it and moving."
# add stuff
fi

File checking shell script

A program that takes in a file as agument 1 and a time in seconds in argument 2, and then the program will check if:
The file exist
If file has been changed
.
#!/bin/bash
file=$1
sleeptime=$2
bool=true
if [ -e $file ]; then
thetime=$(date -r $file "+%s")
newtime=$(date -r $file "+%s")
while "$bool" = true
do
sleep $sleeptime
newtime=$(date -r $file "+%s")
if [ "$thetime" -ne "$newtime" ]; then
bool=false
echo "Filen $file ble endret"
fi
if [ ! -e $file ]; then
bool=false
echo "Filen $file ble slettet"
fi
done
fi
if [ ! -e $file ]; then
while "$bool" = true
do
sleep $sleeptime
if [ -e $file ]; then
bool=false
echo "Filen $file ble opprettet"
fi
done
fi
Bash is quite hard to get into, to give you a few pointers:
subcommands in conditions are a somewhat tricky, you usually want to run it in a subshell with substitution if the expression contains variables, which is done by $(), not () (explained in this SO question)
while, if needs to be separated from do, then either by a semicolon or by a newline
testing conditions in bash is a bit different from other languages, in your case -f operator checks file is a regular file (see further file testing operators)
it's usually better to quote variables in conditions and variables passed to commands for case they contain special characters (which would be evaluated otherwise)
not sure what should be the nature of "file hasn't been changed" claim, but probably the simplest approach is to test if it's size changed, for a start the -s operator checks if size of a file is more than zero
as it's pointed out in a comment application-specific variables should be lower-case by convention
Your code with these adjustments:
#!/bin/bash
file=$1
sleeptime=$2
while ($(sleep $sleeptime)); do
if [ ! -f "$file" ]; then
touch $file
echo "File $file was created."
elif [ ! -s "$file" ]; then
rm $file
echo "File $file was deleted."
fi
done
That would be something like this:
#!/bin/bash
file=$1
sleeptime=$2
while :; do
sleep "$sleeptime"
if [ -f "$file" ] ; then
if [ "$file" -nt ".tag.$file" ] ; then
echo "Not removed $file, because it was changed"
else
rm "$file"
rm ".tag.$file"
echo "File $file was deleted."
fi
else
touch "$file"
touch ".tag.$file"
echo "File $file was created."
fi
done
Notes:
cleaned-up some of the code.
see man test.
uppercase variables are usually used for environment variables.

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

Exit a script when there is file with certain name but ignoring a directory with the same name

If a file called “output” already exists, rather than a directory, the script
should display an error and quit.
here is my code so far
for file in *
do
if [ ! -f output ]
then echo "error"
exit 1
fi
done
for file in *; do
if [ "$file" = "output" -a -f "$file" ]; then
echo "error"
exit 1
fi
done
Or
for file in *; do
if [ "$file" = "output" ] && [ -f "$file" ]; then
echo "error"
exit 1
fi
done
And with bash, this one's preferred:
for file in *; do
if [[ $file == output && -f $file ]]; then
echo "error"
exit 1
fi
done
If you want to check if the filename contains the word, not just exactly matches it:
for file in *; do
if [[ $file == *output* && -f $file ]]; then
echo "error"
exit 1
fi
done
Why are we processing every file in the subdirectory? Very Odd.
if [ -f output ]; then
echo "'output exists and is a file"
exit 1
fi
The test command (which is also [) (and is also built-in to most shells (see bash man page too) ), responds with a TRUE response for -f output only when output is a file. You can check if it's a directory with -d.
touch something
if [ -f something ]; then echo "something is a file"; fi
if [ -d something ]; then echo "something is not a file"; fi
rm something
mkdir something
if [ -f something ]; then echo "something is not a subdir"; fi
if [ -d something ]; then echo "something is a subdir"; fi
rmdir something
If you try those commands, you'll get:
something is a file
something is a subdir
No point in iterating through the entire directory contents if you're just looking if a specific file/dir exists.

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.

Resources