Bash script passes syntax check, but errors out with missing ']' - bash

I'm putting together a simple backup script that will tar contents of a folder, then move that file to a backup server. The script makes sure that the tar file exists and is not zero bytes before moving around.
The problem is that the script is dying on one of the IF lines
if [ -f /www/archives/pdf/pdf_201207021048.tar && 11294720 -gt 0 ]; then
echo "tar file exists and is greater than 0 bytes."
else
echo "tar file does not exist or is zero bytes"
fi
The error in the console is:
./backup_pdf.sh: line 49: [: missing `]'
Line 49 is the if statement above.
The script is successfully verified with
bash -n backup.sh
What's wrong that bash is seeing a missing ']', yet it passes the syntax check?

The && operator separates commands, so your [ and your ] aren't part of the same command, as is required. Either use two sets of brackets with a && between them, or use the -a operator. Most people prefer the first option these days.

Related

Can anyone help me understand this bash script?

I am trying to do some analysis of SSD firmware and have found a bash script called firmware.sh that seems interesting to me. However, I really don't know what I am looking at here.
If anyone can help me understand what this code might be used for, or what it's doing, I would greatly appreciate it!
Here's the bash:
#!/bin/sh -e
FIRMWARE_DIRS="/lib/firmware /usr/local/lib/firmware"
err() {
echo "$#" >&2
if [ -x /usr/bin/logger ]; then
/usr/bin/logger -t "${0##*/}[$$]" "$#"
fi
}
if [ ! -e /sys$DEVPATH/loading ]; then
err "udev firmware loader misses sysfs directory"
exit 1
fi
for DIR in $FIRMWARE_DIRS; do
[ -e "$DIR/$FIRMWARE" ] || continue
echo 1 > /sys$DEVPATH/loading
cat "$DIR/$FIRMWARE" > /sys$DEVPATH/data
echo 0 > /sys$DEVPATH/loading
exit 0
done
echo -1 > /sys$DEVPATH/loading
err "Cannot find firmware file '$FIRMWARE'"
exit 1
Of particular interest to me is the for loop... I think I understand that the $NAME syntax is used for variables in bash but I don't know what those variables are referencing. Thank you for your consideration!
I'll try to explain this line by line.
FIRMWARE_DIRS="/lib/firmware /usr/local/lib/firmware"
FIRMWARE_DIRS is set up with two directories separated by a space. This is set up for the for loop later on in the script.
...
for DIR in $FIRMWARE_DIRS; do
For each loop, DIR is set to each directory stored in FIRMWARE_DIRS
[ -e "$DIR/$FIRMWARE" ] || continue
[ denotes the start of a test, much like if, and ] marks the end of this test.
-e checks if the argument passed is a file or directory that exists.
|| means or and anything to the right of this will execute if the test to the left fails.
continue stops the current iteration of a loop which starts on the next iteration.
FIRMWARE is presumably an environment variable set up prior to this
script running. You can see its value if it has been set up on login
by executing the command echo $FIRMWARE on the command line.
echo 1 > /sys$DEVPATH/loading
Truncates the file /sys$DEVPATH/loading if it exists, then outputs the number 1 to this file.
cat "$DIR/$FIRMWARE" > /sys$DEVPATH/data
Truncates the file /sys$DEVPATH/data if it exists, then outputs the contents of the file(s) $DIR/$FIRMWARE to /sys$DEVPATH/data. If FIRMWARE contains a wildcard *, it will copy the contents of all the files matched.
echo 0 > /sys$DEVPATH/loading
Truncates the file sys$DEVPATH/loading if it exists, then outputs the number 0 to this file.
exit 0
Exits the script with the return status 0 (means it completed OK). This has the effect of ending the script in the for loop at this point for any iteration which passed the test above (the one checking the file or directory exists).
Overall, it looks like it's checking for the first directory that exists in FIRMWARE_DIRS, copies one or more firmware files from there to another location
(/sys$DEVPATH/data) and exits as soon as it's done that once.

Syntax error: "then" unexpected (expecting "done")

Here is my problem statement.
Write a shell script that takes a name of a folder as a command line argument, and produce a file that contains the names of all sub folders with size 0 (that is empty sub folders)
This is my shell script.
ls $1
while read folder
do
files = 'ls $folder | wc -l'
if[$files -eq 0];
then
echo "$folder">>output.txt
echo "File deleted"
else
echo "File is not empty"
fi
done
When I execute my command (using 'sh filename'), it shows syntax error!
Syntax error: "then" unexpected (expecting "done")
Is there any wrong with my script?
Don't forget, in shell [ is a binary that take parameters and return true or false (0 or 1).
if is a keyword that verifies the return of next binary called is true (0).
So, when you do
if[$files -eq 0]
Your shell understand nothing because it try to launch the if[2 programm, and he find a then after without detecting the if.
For fix your problem, you have to put a space after your if and after the [ because binary must have a space between between his name and their arguments.
ls $1
while read folder
do
files = `ls $folder | wc -l`
if [ $files -eq 0 ]
then
echo "$folder">>output.txt
echo "File deleted"
else
echo "File is not empty"
fi
done
Try something like this
ls $1
while read folder
do
files=`ls $folder | wc -l`
if [ $files -eq 0 ]; then
echo "$folder">>output.txt
echo "File deleted"
else
echo "File is not empty"
fi
done
Notice no space files=.., and there is `(back tick) not '(single quote)
Notice space between 'if' and '[' ...
There may be spacing error:
Just do 2 steps:
run hexdump -C yourscript.sh
run cat yourscript.sh | tr -d '\r' >> yournewscript.sh
it will create new correct file then run new file.
You've already got answers describing how your existing script needs to be fixed:
no spaces around the = when you set the $files variable,
backquotes instead of single ticks for your command substitution,
a space after if, and spaces around the parts of the conditional expression.
Your script suffers from the Parsing LS issue, in that filenames may be treated badly if they contain special characters like newlines. While you may think this isn't a big issue when all you want to do is check for the existence or nonexistence of files (i.e. count == 0), but the way you're doing it is still cumbersome, and encourages bad habits.
How about, instead consider:
while read folder; do
files=0
for files in $folder/*; do
files=1
break
done
if [ $files -eq 0 ]; then
echo "$folder" >> output.txt
else
echo "not empty: $folder" >&2
fi
done
Instead of counting files in a command substitution and pipe, this uses a for loop to set a semaphore if any files exist. This will always be faster.
Note that this is POSIX-compliant. If your shell is a more advanced one, like bash or zsh, you have more elegant/efficient options available.

Test : Argument Expected

I am working on a shell script and I am getting an error saying Test : Argument expected. Its basically a sed command followed by checking if there were any errors
Please find the below
sed "s|${var1}|${var2}|g" $FILE_PATH$FILE_NAME > /tmp/$FILE_NAME
if [ "$command_error" != 0 ] ; then
date
echo "Error $command_error reading file $WS_FILE"
echo "File Does not exist or is not readable"
exit 30
fi
What shell are you using?
I think you are missing a line:
command_error=$?
between the two blocks.
Be aware that many (all?) commands (eg echo $?) will actually change it's value. Therefore, it's a good idea to assign $? to a temporary variable like this.

problems with checking for a directory in bash shell script

I am writing a batch-processing script bash that needs to first check to see if a folder exists to know whether or not to run a certain python script that will create and populate the folder. I have done similar things before that do fine with changing the directories and finding directories from a stored variable, but for some reason I am just missing something here.
Here is roughly how the script is working.
if [ -d "$net_output" ]
then
echo "directory exists"
else
echo "directory does not exist"
fi
when I run this script, I usually echo $net_output in the line before to see what it will evaluate to. When the script runs I get my else block of code saying "Directory does not exist", but when I then copy and paste the $net_output directory path that is echoed before into the shell terminal, it changes directories just fine, proving that the directory does in fact exist. I am using Ubuntu 12.04 on a Dell machine.
Thank you in advance for any help that someone can offer. Let me know what additional information I can provide.
The most common cases I've encountered when someone posts a problem like this are the following:
1. The variable contains literal quotes. Bash does not recursively parse quotes, it only parses the "outer" quotes given on the command line.
$ mkdir "/tmp/dir with spaces"
$ var='"/tmp/dir with spaces"'
$ echo "$var"
"/tmp/dir with spaces"
$ [ -d "/tmp/dir with spaces" ]; echo $?
0
$ [ -d "$var" ]; echo $? # equivalent to [ -d '"/tmp/dir with spaces"' ]
1
2. The variable contains a relative path, and the current directory is not what you expected. Check that the value of echo "$PWD" outputs what you expected.
3. The variable was read from a file with dos line endings, CRLF (\r\n). Unix and unix-like systems use just LF (\n) for line endings. If that's the case, the path will contain a CR (\r) at the end. A CR at the end of a line will be "invisible" in a terminal. Check with printf '%q\n' "$var" while debugging the script. See BashFAQ 52 on how to get rid of them.

shell script working fine on one server but not on another

the following script is working fine on one server but on the other it gives an error
#!/bin/bash
processLine(){
line="$#" # get the complete first line which is the complete script path
name_of_file=$(basename "$line" ".php") # seperate from the path the name of file excluding extension
ps aux | grep -v grep | grep -q "$line" || ( nohup php -f "$line" > /var/log/iphorex/$name_of_file.log & )
}
FILE=""
if [ "$1" == "" ]; then
FILE="/var/www/iphorex/live/infi_script.txt"
else
FILE="$1"
# make sure file exist and readable
if [ ! -f $FILE ]; then
echo "$FILE : does not exists. Script will terminate now."
exit 1
elif [ ! -r $FILE ]; then
echo "$FILE: can not be read. Script will terminate now."
exit 2
fi
fi
# read $FILE using the file descriptors
# $ifs is a shell variable. Varies from version to version. known as internal file seperator.
# Set loop separator to end of line
BACKUPIFS=$IFS
#use a temp. variable such that $ifs can be restored later.
IFS=$(echo -en "\n")
exec 3<&0
exec 0<"$FILE"
while read -r line
do
# use $line variable to process line in processLine() function
processLine $line
done
exec 0<&3
# restore $IFS which was used to determine what the field separators are
IFS=$BAKCUPIFS
exit 0
i am just trying to read a file containing path of various scripts and then checking whether those scripts are already running and if not running them. The file /var/www/iphorex/live/infi_script.txt is definitely present. I get the following error on my amazon server-
[: 24: unexpected operator
infinity.sh: 32: cannot open : No such file
Thanks for your helps in advance.
You should just initialize file with
FILE=${1:-/var/www/iphorex/live/infi_script.txt}
and then skip the existence check. If the file
does not exist or is not readable, the exec 0< will
fail with a reasonable error message (there's no point
in you trying to guess what the error message will be,
just let the shell report the error.)
I think the problem is that the shell on the failing server
does not like "==" in the equality test. (Many implementations
of test only accept one '=', but I thought even older bash
had a builtin that accepted two '==' so I might be way off base.)
I would simply eliminate your lines from FILE="" down to
the end of the existence check and replace them with the
assignment above, letting the shell's standard default
mechanism work for you.
Note that if you do eliminate the existence check, you'll want
to either add
set -e
near the top of the script, or add a check on the exec:
exec 0<"$FILE" || exit 1
so that the script does not continue if the file is not usable.
For bash (and ksh and others), you want [[ "$x" == "$y" ]] with double brackets. That uses the built-in expression handling. A single bracket calls out to the test executable which is probably barfing on the ==.
Also, you can use [[ -z "$x" ]] to test for zero-length strings, instead of comparing to the empty string. See "CONDITIONAL EXPRESSIONS" in your bash manual.

Resources