Syntax error in shell script saying unexpected token `( - bash

I have written the following shell script
while :; do
status=$($EMR_BIN/elastic-mapreduce --jobflow $JOBFLOW --list | grep "CopyLogs" | awk '{print $1}')
[[ $status == +( *RUNNING*|*PENDING*|*WAITING* ) ]] || break
sleep 60
done
Its giving me an error in line 3 saying syntax error in conditional expression: unexpected token('' . I tried giving whitespaces between my braces, but its not working.
Can anyone help me out.

Looks like you are trying to use extended globbing. Make sure you have shopt -s extglob somewhere earlier in your script, or rewrite to use standard globbing.
#!/bin/sh
while :; do
case $($EMR_BIN/elastic-mapreduce --jobflow $JOBFLOW --list | awk '/CopyLogs/{print $1}') in
*RUNNING*|*PENDING*|*WAITING* ) sleep 60;;
*) break;;
esac
done
Since there are no remaining Bashisms, this script is now POSIX sh compatible. (Personally, I also think it is more readable this way.)
(Note also the fix for the useless grep | awk.)

Related

failing bash variable matching

I have been driving myself mad on this.
I just don't see why it doesn't work when bash variables are untyped.
There should be no reason why not.
pat1=`. $NVM_DIR/nvm.sh && nvm ls | sed 's/\x1b\[[^\x1b]*m//g' | sed -e 's/[[:alpha:]|\/\)\-\>\(|[:space:]]//g' | sed 's/[-|\*]//g' | sed '/^$/d'`
pat2=`. $NVM_DIR/nvm.sh && nvm ls node | head -1 | awk '{print $2}' | cut -d v -f2`
these expand to:
~$ echo $pat1
9.5.0
9.5.0
9.5
9.5.0
4.8.7
6.12.3
8.9.4
~$ echo $pat2
9.5.0
So I want to check if the string found in $pat2 is in $pat1 string.
however it does not find it even though it clearly is in the pattern 3 times at least.
Checking for the pattern with a test like:
case "$pat1" in
*$pat2*)
echo 'match'
;;
*)
echo 'nomatch'
;;
esac
this gives:
~$ ./test.sh
nomatch
another test:
if [[ "$pat1" =~ "$pat2" ]]; then
echo 'match'
else
echo 'nomatch'
fi
again fails:
~$ ./test.sh
nomatch
Even tried some janky hacky method to no avail,
echo $pat1 | grep "$pat2" > /dev/null
if [ $? -eq 0 ]; then
echo "matched"
else
echo "nomatch"
fi
gives the result:
~$ ./test.sh
grep: brackets ([ ]) not balanced
nomatch
Been pulling my hair out today working on this.
This must be due to the fact that both variables are command substitutions, it leads me to believe it is comparing the actual commands themselves rather than their output stored in the variables.
So even though they echo the output, i think it may be comparing the string of the commands itself.
am I wrong? can anyone explain why this fails to match and if its possible with bash to compare the output of two commands in a string/sub-string type of comparison?
I put your pat1 and pat2 values in files and didn't need to use | anywhere.
#!/bin/bash
pat1=$(cat pat1|tr '\012' ' ')
pat2=$(<pat2)
#tst pat2="10.1"
echo "$pat1"
case "$pat1" in
*$pat2*)
echo 'match'
;;
*)
echo 'nomatch'
;;
esac
Produces
#dbg:pat2=9.5.0 pat1=9.5.0 9.5.0 9.5 9.5.0 4.8.7 6.12.3 8.9.4
match
If I un-comment the #tst pat2 line, the output is
#dbg:pat2=10.1 pat1=9.5.0 9.5.0 9.5 9.5.0 4.8.7 6.12.3 8.9.4
nomatch
Is there any chance your data was created for MS Windows? If so, check for Windows line endings with
head -10 file | cat -vet
if you see ^M$ at the end of each line, then run dos2unix file .... (multiple file can be processed in one invocation, and the original file is overwritten with the same name.
IHTH
nomatch
The grep version is almost right, but you need to quote $pat1, otherwise the newlines will be converted to spaces and everything will be on one line.
if echo "$pat1" | grep -F -q "$pat2"
then
echo "matched"
else
echo "nomatch"
fi
I've also added the -F option to grep so it will treat $pat2 as a fixed string rather than a regular expression.
DEMO

Removing character from variable in BASH script

I have a script which utilises SaltStack's command-line as well as BASH commands. The script is used to gather data from multiple Linux servers (hence SaltStack), one of the checks which I would like to gather is disk space.
I have done this by using the following command:
salt $i cmd.run 'df -Ph / | tail -n1 | awk '"'"'{ print $4}'"'"'' | grep -v $i
$i = hostname and the use of the ugly '"'"' is so that my command can run via SaltStack as Salt's remote execution functionality requires single quotes around the command, if I left them in my command wouldn't run inside my BASH script.
Example syntax:
salt $hostname cmd.run 'command here'
After many questions on here and with colleagues I have this section of the script sorted. However I now the problem of stripping the output of my above command to remove the 'G' so that my script can compare the output with a threshold I have defined and turn the HTML which this script is piping to red.
Threshold:
diskspace_threshold=5
Command:
while read i ; do
diskspace=`salt $i cmd.run 'df -Ph / | tail -n1 | awk '"'"'{ print $4}'"'"'' | grep -v $i`
Validation check:
if [[ "${diskspace//G}" -lt $diskspace_threshold ]]; then
ckbgc="red"
fi
The method I have used for stripping the G works on the command line but not within my script so it must be something to do with the syntax or just the fact that it is now within a script. Any ideas/thoughts would be helpful.
Cheers!
EDIT: Here is the error message I receive when running my script:
serverdetails.sh: line 36: p
: 2.8: syntax error: invalid arithmetic operator (error token is ".8")
I assume the error is coming from here (is this line 36?)
if [[ "${diskspace//G}" -lt $diskspace_threshold ]]; then
Note the error message:
serverdetails.sh: line 36: p : 2.8: syntax error: invalid arithmetic operator (error token is ".8")
bash does not do floating point arithmetic
$ [[ 2.8 -lt 3 ]] && echo OK
bash: [[: 2.8: syntax error: invalid arithmetic operator (error token is ".8")
You'll need to do something like this:
result=$( bc <<< "${diskspace%G} < $diskspace_threshold" )
if [[ $result == 1 ]]; then
echo OK
else
echo Boo
fi

Escaping Shebang in grep

in a shell script, i'm trying to find out if another file is a shell script. i'm doing that by grepping the shebang line. but my grep statement doesn't work:
if [[ $($(cat $file) | grep '^#! /bin' | wc -l) -gt 0 ]]
then
echo 'file is a script!'
else
echo "no script"
fi
i always get the error "bash: #!: command not found". i tried several things to escape the shebang but that didn't work.
maybe you can help me with that? :)
cheers,
narf
I would suggest that you change your condition to this:
if grep -q '^#! */bin' "$file"
The -q option to grep is useful in this case as it tells grep to produce no output, exiting successfully if the pattern is matched. This can be used with if directly; there's no need to wrap everything in a test [[ (and especially no need for a useless use of cat).
I also modified your pattern slightly so that the space between #! and /bin is optional.
It's worth noting that this will produce false positives in cases where the match is on a different line of the file, or when another shebang is used. You could work around the first issue by piping head -n 1 to grep, so that only the first line would be checked:
if head -n 1 "$file" | grep -q '^#! */bin'
If you are searching for a known list of shebangs, e.g. /bin/sh and /bin/bash, you could change the pattern to something like ^#! */bin/\(sh\|bash\).

overwrite a file then append

I have a loop in my script that will append a list of email address's to a file "$CRN". If this script is executed again, it will append to this old list. I want it to overwrite with the new list rather then appending to the old list. I can submit my whole script if needed. I know I could test if "$CRN" exists then remove file, but I'm interested in some other suggestions? Thanks.
for arg in "$#"; do
if ls /students | grep -q "$arg"; then
echo "${arg}#mail.ccsf.edu">>$CRN
((students++))
elif ls /users | grep -q "$arg$"; then
echo "${arg}#ccsf.edu">>$CRN
((faculty++))
fi
Better do this :
CRN="/path/to/file"
:> "$CRN"
for arg; do
if printf '%s\n' /students/* | grep -q "$arg"; then
echo "${arg}#mail.ccsf.edu" >> "$CRN"
((students++))
elif printf '%s\n'/users/* | grep -q "${arg}$"; then
echo "${arg}#ccsf.edu" >> "$CRN"
((faculty++))
fi
done
don't parse ls output ! use bash glob instead. ls is a tool for interactively looking at file information. Its output is formatted for humans and will cause bugs in scripts. Use globs or find instead. Understand why: http://mywiki.wooledge.org/ParsingLs
"Double quote" every expansion, and anything that could contain a special character, eg. "$var", "$#", "${array[#]}", "$(command)". See http://mywiki.wooledge.org/Quotes http://mywiki.wooledge.org/Arguments and http://wiki.bash-hackers.org/syntax/words
take care to false positives like arg=foo and glob : foobar, that will match. You need grep -qw then if you want word boundaries. UP2U

Error using [[ ]] and -eq

Shell script snippet:
tagSearch= $(grep '^\#ctags$' ./"$1" | wc -l)
if [[ $tagSearch -ne "0" ]]
then
...
fi
Results in:
line 2: /bb/bin/1: Permission denied
Context:
I'm trying to detect whether a particular pattern exists in a file so I can take a particular action.
I understand the error I'm getting, the detection is working but the script is trying to evaluate the result '1' and run the program '1' in my path. This isn't what I want. How do I get the behavior I'm looking for?
The problem is
tagSearch= $(grep '^\#ctags$' ./"$1" | wc -l)
----------^
You can't use spaces around the equal sign; what you're actually doing here is to temporarily set tagSearch to the empty string in the environment, then invoking grep '^\#ctags$' ./"$1" | wc -l, then trying to run that as a command since the $() will have inserted the result into the command line.
tagSearch=$(grep '^\#ctags$' ./"$1" | wc -l)
Variable assignments in the bash shell should not have a space after the equals. Actually it should never have whitespace in it at all. See below.
tagSearch=$(grep '^\#ctags$' "./$1" | wc -l)
if [[ $tagSearch -ne 0 ]]
then
...
fi
Not important to your error but also of note, when using the double bracket syntax, you don't need to quote that zero any more than the variable you are comparing it with.
Actually your whole code could be re-factored using grep's quite mode and evaluating the return code to see if you got any matches:
if grep '^\#ctags$' "./$1"
then
...
fi
Actually you can have that simpler, because the return code of grep will be 0 if something is found (1 otherwise), so you don't need wc -l. And you can just write:
if `grep -q pattern file`; then echo "yes"; else echo "no"; fi;

Resources