Bash error when trying to pipe output: Syntax error - bash

I have a bash script (test.sh) with the following:
#!/bin/bash
npm test |& grep -v '[HPM]'
if [[ $? -ne 0 ]]; then
...
When trying to run this script locally I get this error:
test.sh: line 3: syntax error near unexpected token `&'
test.sh: line 3: `npm test |& grep -v '[HPM]''

The |& syntax is using a non-standard token which is recognized by bash but not all shells. Such a construct is often called a bashism. If your shell is inadvertently invoked as a non-bash shell, then it is a syntax error. You can easily use a standardized construct for this:
npm test 2>&1 | grep -v '\[HPM\]'
Note that this is unusual. It seems odd to capture the stderr of npm, but perhaps you really do want to check if grep prints any lines. There's really no need to explicitly check $?, and your code would normally be written:
if ! npm test 2>&1 | grep -v '\[HPM\]'; then
: grep failed. Do something
fi
But again, this seems strange. grep -v will "fail" if it does not print any lines of text, and it will succeed otherwise. Perhaps you were expecting $? to contain the exit status of npm in your original code, but it does not. $? will be zero if grep prints any text, and non-zero otherwise.

Related

Docker bash: syntax error: unexpected "(" (expecting "then")

I try to run bash scripts in Docker, buit I keep getting this error:
./scripts/proto-generator.sh: line 13: syntax error: unexpected "(" (expecting "then")
Here's my proto-generator.sh file:
function printGreen() {
printf "\e[0;32m$1\e[0;m\n"
}
function printRed() {
printf "\e[0;31m$1\e[0;m\n"
}
service=$1
outDir=./src/services/$service/models
protoDir=./protos/"${service}Service"/*.proto
if ! [[ "$service" =~ ^(file|user)$ ]]; then
printRed "Incorrect service: $service"
exit 1
fi
./node_modules/.bin/proto-loader-gen-types \
--longs=String \
--enums=String \
--defaults \
--oneofs \
--grpcLib=#grpc/grpc-js \
--outDir=$outDir \
$protoDir
printGreen "GRPC codes generated: ${outDir}"
How can I fix the syntax error?
Thanks for any help!!
I would have loved to ask that question as a comment, but I'm not allowed to, yet. Anyway, after having had a quick test, I assume your docker image is Alpine Linux-based. The standard Alpine Linux docker images do not come with a bash as you might be expecting but with an ash and ash does not have a [[-built in command, i.e. you should be sticking to the standard [ aka test command or get goind completely without it. And, sadly enough, test does not have the ability to handle regular expressions.
That said and again assuming that you do not want to bloat the Alpine image with a complete bash, grep comes to the rescue. A solution for your case would be (lines 13, ff.)
if ! echo $service | grep -qE "^(file|user)$" ; then
printRed "Incorrect service: $service"
exit 1
fi
explanation
Make the if directly test the return code from grep using your pattern. grep returns non-zero if there was no match, 0 otherwise. -q suppresses output of the match, -E switches to extended regular expression, as needed for the |.
Just put a
#!/bin/bash
at the top of the script
I think you are executing it in /bin/sh,
and sh doesn't support bash regex.
Or you are using an outdated version of bash, regex in bash where introduced recently.

Shell read from files with tail

I am currently trying to read from files with shell. However, I met one sytax issue. My code is below:
while read -r line;do
echo $line
done < <(tail -n +2 /pathToTheFile | cut -f5,6,7,8 | sort | uniq )
However, it returns me error syntax error near unexpected token('`
I tried with following How to use while read line with tail -n but still cannot see the error.
The tail command works properly.
Any help will be apprepricated.
process substitution isn't support by the posix shell /bin/sh. It is a feature specific to bash (and other non posix shells). Are you running this in /bin/bash?
Anyhow, the process substitution isn't needed here, you could simple use a pipe, like this:
tail -n +2 /pathToTheFile | cut -f5,6,7,8 | sort -u | while read -r line ; do
echo "${line}"
done
Your interpreter must be #!/bin/bash not #!/bin/sh and/or you must run the script with bash scriptname instead of sh scriptname.
Why?
POSIX shell doesn't provide process-substitution. Process substitution (e.g. < <(...)) is a bashism and not available in POSIX shell. So the error:
syntax error near unexpected token('
Is telling you that once the script gets to your done statement and attempts to find the file being redirected to the loop it finds '(' and chokes. (that also tells us you are invoking your script with POSIX shell instead of bash -- and now you know why)

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

Unix Shell Script: Display custom message if given extension file not exists in directory

I have the following unix shell script, which is used to list the files in the given directory. Only we need to pass the extension of the file and script should list the file or files or display custom message.
My try:
Script:
#!/bin/sh
FileNameWithPath=`ls home\docs\customers\*.$1 | wc -w`
if [ $FileNameWithPath -gt 0 ]
then
ls home\docs\customes\*.$1
else
echo "Custom Message about failure(File not found)"
fi
Run:
$ ./Test.sh txt
Note: The above script works fine if i give file extension which is exists but if i give some non exists file extension it will through error plus custom error message. I just want to print custom message that's it.
You can do it with a single command:
ls home/docs/customers/*.$1 2> /dev/null || echo "Custom message about failure (File not found)"
The first command (the 'ls') try to list the files. If it fails, it will print an error message (suppressed by '2> /dev/null') and returns an error code. Since the exit code is different by 0, the second part (the 'echo') will be executed.
If you want to keep your code, you can drop the ls error redirecting stderr to /dev/null in this way:
FileNameWithPath=`ls home\docs\customers\*.$1 2>/dev/null | wc -w`
This doesn't require use of ls.
You can do this with globbing itself:
# turn on glob failure for no matches
shopt -s failglob
# list files or a custom error message
(echo home/docs/customers/*."$1") 2>/dev/null ||
echo "Custom Message about failure"
The error message you get happens in the line where you are assigning to FileNameWithPath. You can suppress it by redirecting it to /dev/null. i.e. 2>/dev/null.
It is much better (and Posix compliant) to use $() instead of the backtick operator, given that you started your script with #!/bin/sh rather than #!/bin/bash. You will then be portable across the modern bourne shells.
Another big win for using $() is that they can be nested easily, whereas you have to escape the backtick when you nest it.
As Andrea Carron points out in their answer, you can do the whole thing on one line using the || logical-or operator. This is a very common idiom.
On the off-chance that your MVCE refers to something more complex, I fixed it for you below.
#!/bin/sh
FileNameWithPath=$(ls home\docs\customers\*.$1 2>/dev/null | wc -w)
if [ $FileNameWithPath -gt 0 ]
then
ls home\docs\customes\*.$1
else
echo "Custom Message about failure(File not found)"
fi
Just add error redirection to null device file in second line of your script:-
FileNameWithPath=`ls home\docs\customers\*.$1 2>/dev/null | wc -w`

Passing a variable into awk within a shell script

I have a shell script that I'm writing to search for a process by name and return output if that process is over a given value.
I'm working on finding the named process first. The script currently looks like this:
#!/bin/bash
findProcessName=$1
findCpuMax=$2
#echo "parameter 1: $findProcessName, parameter2: $findCpuMax"
tempFile=`mktemp /tmp/processsearch.XXXXXX`
#echo "tempDir: $tempFile"
processSnapshot=`ps aux > $tempFile`
findProcess=`awk -v pname="$findProcessName" '/pname/' $tempFile`
echo "process line: "$findProcess
`rm $tempFile`
The error is occuring when I try to pass the variable into the awk command. I checked my version of awk and it definitely does support the -v flag.
If I replace the '/pname/' portion of the findProcess variable assignment the script works.
I checked my syntax and it looks right. Could anyone point out where I'm going wrong?
The processSnapshot will always be empty: the ps output is going to the file
when you pass the pattern as a variable, use the pattern match operator:
findProcess=$( awk -v pname="$findProcessName" '$0 ~ pname' $tempFile )
only use backticks when you need the output of a command. This
`rm $tempFile`
executes the rm command, returns the output back to the shell and, it the output is non-empty, the shell attempts to execute that output as a command.
$ `echo foo`
bash: foo: command not found
$ `echo whoami`
jackman
Remove the backticks.
Of course, you don't need the temp file at all:
pgrep -fl $findProcessName

Resources