Why is this bash script fetching http headers - linux

I am not sure if this was the proper place to post this, none-the-less: I am developing a script to negative match git branches, however when this part was run, it tried to get HTTP headers....can someone explain how this is happening?
test.sh
#!/bin/bash
array_not_contains()
{
local array="$1[#]"
local seeking=$2
local in=0
for element in "${!array}"; do
echo $element #Commenting out this echo will stop it from fetching headers
if [[ $element =~ $seeking ]]; then
in=1
break
fi
done
return $in
}
exclude=() #array that will exclude the following matches from deletion
exclude+=(HEAD)
exclude+=(master)
exclude+=(develop)
exclude+=(example.*)
if $(array_not_contains exclude $1); then
echo "win"
else
echo "fail"
fi
Running it like this:
./test.sh bob
will return headers

It's ending up evaluating HEAD as a command.
bash$ type -all HEAD
HEAD is /usr/bin/HEAD
You probably misunderstand what if $(command) does. It runs command, then runs the output of that as a command, and examines its exit code.
The fix is easy: you mean
if array_contains exclude "$1"; then
though I would probably refactor the code to reduce its complexity more significantly.
As pointed out numerous times the if statement runs a command and examines its exit code.
bash$ flase () { echo "flase."; return 1; }
bash$ ture () { echo "ture."; return 0; }
bash$ if ture; then echo It is true.; else echo It is not.; fi
ture.
It is true.
bash$ if flase; then echo It is not false.; else echo It is false.; fi
flase.
It is false.

Related

How to get the output of if condition on terminal

I have this example and i want to get on screen both condition and good
Code:
if [ `echo condition` ]; then echo good; fi
The output that i want to get:
condition
good
The output that i got:
good
As the command echo condition will be replaced with another command. The if statement must check the return code of the condition command.
Simply store it in a variable:
cond=$(echo condition)
if [ "$cond" ] ; then
echo "$cond"
echo good
fi
Do not use backticks anymore nowadays.
Store the output of your sub shell in a variable:
if condition=$(echo condition); then
echo "$condition"
echo good
fi
If you want to return the exit value of the sub shell, you have to write a function:
get-condition-result()
{
local condition
local result
if condition=$(echo condition); then
result=$?
echo "$condition"
echo good
else
result=$?
fi
return "$result"
}
Solution found here: https://stackoverflow.com/a/13343457/12953642
You can run your command without any additional syntax. For example, the following checks the exit code of grep to determine whether the regular expression matches or not:
if echo condition
then
echo "good"
fi
if echo "condition" && curl
then
echo "good"
else
echo "error in command"
fi
Output:
condition
good
condition
curl: try 'curl --help' or 'curl --manual' for more information
error in command

Grep(or alternative) for 2 strings, matching one, another or both are success, contains anything else is a fail

I want to analyse output from a command.
I have 2 potential string matches.
If I match one, the other or both, I class that as a pass.
If the output contains anything else I want to flag it as a fail.
This was my original code, which works most of the time.
But when the output(on a rare occasion) contains both my strings, which is allowed. It flags as a fail:
if [ "$STATUS" != "Ok" ] && [ "$STATUS" != "Non-Critical" ]
then
echo "FAILED: STATUS NOT 'Ok' OR NOT 'Non-Critical'"
else
echo " Status check good."
fi
The above fails for me when my output contains both strings, which is not what I want.
So, to be clear:
Ok - On it's own is a pass.
Non-Critical - On it's own is a pass.
Ok
Non-Critical - is a pass.
Non-Critical
Ok - is a pass.
ANYTHING else is a fail
Hoping someone smarter than me can help :-)
You could use sed for that.
echo -e "Critical\nNon-Critical\nNon-Critical Ok" |
sed 's/.*\(Non-Critical\|Ok\).*/PASS/;t;s/.*/FAIL/'
Explanation: first we replace "Non-Critical" or "Ok" and everything else on that line by PASS; then, the command t jumps to the end of the script upon successful match. If, however, the match is not successful, everything gets replaced by FAIL. Output of the above command:
FAIL
PASS
PASS
How about a case?
case "$STATUS" in
*Non-Critical*) echo PASS;;
*Ok*) echo PASS;;
*) echo FAIL;;
esac
Did I miss something?
Here is a perl version
echo -e "Critical\nNon-Critical\nNon-Critical Ok" | perl -ne 'print((/Non-Critical|Ok/?PASS:FAIL)."\n")'
FAIL
PASS
PASS
For ternary operator a?b:c look here
Also (works only in ksh)
#!/bin/ksh
if [[ $STATUS = #(~(+i:*OK*)) || $STATUS = #(~(+i:*Non-Critical*)) ]]; then
echo "good"
else
echo "bad"
fi
or if you don't want it to be case independant (works both in bash and ksh)
if [[ $STATUS = #(*OK*) || $STATUS = #(*Non-Critical*) ]]; then ............

bash - returning value based on process condition

i stumbled in a confusing way of conditionally returning value based on variable. I would like to check if process is successful then echo "process success", but if it's failed, i want to check specific error message then return the error message,
ERRMSG="$(cd /nonexist 2>&1)"
if [ $? -ne 0 ]
then
if [ -z "$ERRMSG|grep -o 'No such file or directory'|head -1" ]
then
echo "empty" >> $FQLOGNAME
else
echo $ERRMSG|grep -o 'No such file or directory'|head -1 >> $FQLOGNAME
fi
else
echo "success" >> $FQLOGNAME
fi
Please advice,
Thanks
You don't need to use grep to check if a string contains a substring. The built-in pattern matching in Bash is sufficient. This code should do something close to what you want:
if ERRMSG=$(cd /nonexist 2>&1) ; then
echo 'process success'
elif [[ $ERRMSG == *'No such file or directory'* ]] ; then
echo 'No such file or directory'
else
echo 'empty'
fi >> "$FQLOGNAME"
See the Conditional Constructs section of the Bash Reference Manual for details of the pattern matching capabilities of [[...]].
I've retained the ERRMSG and FQLOGNAME variables, but note that it's best to avoid ALL_UPPERCASE variable names. There is a danger that they will clash with environment variables or Bash builtin variables. See Correct Bash and shell script variable capitalization.
To find error messages defined by a pattern in multi-line error messages, and only print the first one, you can use regular expression matching (=~) in [[...]]. To provide a concrete example, this code assumes that error messages consist of 'ERROR' followed by one or more spaces followed by a decimal number:
# Example function for testing
function dostuff
{
printf 'Output line A\n'
printf 'Encountered ERROR 29\n' >&2
printf 'Output line B\n'
printf 'Encountered ERROR 105\n' >&2
printf 'Output line C\n'
return 1
}
# Regular expression matching an error string
readonly error_rx='ERROR +[0-9]+'
if ERRMSG=$(dostuff 2>&1) ; then
echo 'process success'
elif [[ $ERRMSG =~ $error_rx ]] ; then
printf '%s\n' "${BASH_REMATCH[0]}"
else
echo 'empty'
fi >> "$FQLOGNAME"
It appends 'ERROR 29' to the log file.
For more information about Bash's built-in regular expression matching see mklement0's answer to "How do I use a regex in a shell script?".
Make it simpler and easier:
if ! ERRMSG=$(cd /nonexist 2>&1); then
if <<<"$ERRMSG" grep -q 'No such file or directory'; then
# if the error string contains the message 'No such file or directory'
echo "empty" >> "$FQLOGNAME"
else
printf "Unhandled cd error: %s" "$ERRMSG" >> "$FQLOGNAME"
fi
else
echo "process success" >> "$FQLOGNAME"
fi
if statements checks for the return status of a COMMAND. [ or test is just a command, which return a status. The return status of assignment is the same as command status. What I mean, is that out=$(cmd); if [ "$?" -eq 0 ]; then is the same as if out=$(cmd); then.
Using HERE-strings is a bit better than echo "$string". Echo is not that much portable, better get used to printf "%s" "$string" which is a portable way. However HERE-strings puts additional EOF at the end of the stream, which sometimes breaks while read loops, but for most cases works ok.
Don't if [ -z "$(echo smth | grep ..)" ]; then. You can just check grep return status, just if echo smth | grep ...; then or with HERE-strings if <<<"smth" grep -q ...; then or if grep -q ... file; then. The -q option which has --quiet or --silent alternatives makes grep produce no output.
The quoting is not needed when assigning a variable from a single command substitution. tmp="$(...)" is just the same as tmp=$(...).

Convert bash script using regexes to sh

I'm really struggling with writing a Bourne shell script. Basically, I have three input formats for a variable called "ref" that I'm trying to detect:
ref="refs/head/.*" (i.e. begins with "refs/head/" I'm interested in the bit at the end, after the slash)
ref="refs/tags/.*" (i.e. begins with "refs/tags/" I'm interested in the bit at the end, after the slash)
everything else (i.e. ignore everything that doesn't begin with either "refs/head/" or "refs/tags/")
For example,
If ref="refs/head/master", set TAG="master"
If ref="refs/tags/0.2.4", set TAG="0.2.4"
For everything else, set TAG=""
Now I wrote something in bash shell, but I'm really struggling to convert it to Bourne (#!/bin/sh):
#!/bin/bash
#
#This works!
#
TAG=""
re='^refs/head/.*' #regex: begins with refs/head/, ends with anything
re2='^refs/tags/.*' #regex: begins with refs/tags/, ends with anything
if [[ $ref =~ $re ]]; then
#do nothing - OK
true #NOP
else
#check if it's a tag update
if [[ $ref =~ $re2 ]]; then
TAG=${$ref##*/} #looks worse that it is: http://stackoverflow.com/questions/3162385/how-to-split-a-string-in-shell-and-get-the-last-field
fi
exit 0
fi
echo $TAG
It took me ages to a) write this program and b) find out why my program was going nuts - turned out I need #!/bin/sh and not #!/bin/bash
How can I convert this to sh? Maybe someone else has a more elegant solution to my regex gymnastics?
Update:
Thanks for the answers sofar (especially #gboffi). I think I'm almost there.
All I need now is to know if $TAG comes from "refs/head/", "refs/tags/" or neither. I tried to modify some of the answers, but really struggling with sh. I'll need to go away and learn more about sh from first principles instead of trying to hack it.
Update 2:
So after a night's sleep, I figured it out in about 20 minutes. Here is my solution:
#!/bin/sh
ref="refs/asdf/master"
TAG=""
TAG="${ref#refs/heads/}"
if [ "$ref" != "${ref#refs/heads/}" ]; then
echo "heads"
echo $TAG
else
TAG="${ref#refs/tags/}"
if [ "$ref" != "${ref#refs/tags/}" ]; then
echo "heads"
echo $TAG
else
TAG=""
fi
fi
echo "--->$TAG"
I'm sure there's a much more elegant solution; but I just don't have the time!
Here it is a function, defined in dash (the linux version of sh)
% dash
$ tag() {
> TAG=""
> [ "$1" != "${1#refs/head/}" ] && TAG="${1#refs/head/}"
> [ "$1" != "${1#refs/tags/}" ] && TAG="${1#refs/tags/}"
> echo "$TAG"
>}
$ tag poldo
$ tag refs/head/poldo
poldo
$ tag refs/tags/pippo
pippo
$ tag to093u0refs/head/poldo
$exit
%
The most idiomatic way to handle this is with a case statement and pattern matching:
#!/bin/sh
case $ref in
refs/head/*) true ;; # original behavior: no output, success
refs/tags/*) printf '%s\n' "${ref##*/}" ;; # original behavior: emit output
*) false # exit with an error # original code didn't specify behavior
esac
The reason I'm exiting with an error for the case where neither refs/head/* or refs/tags/* matches is that if you wanted to exit with success in that case, you could omit the refs/head/* test entirely.
You can use rev-parse for this, here are some examples from one of my
repositories. Notice the last one produces no output, as desired.
$ git rev-parse --abbrev-ref refs/heads/master
master
$ git rev-parse --abbrev-ref refs/tags/5
5
$ git rev-parse --abbrev-ref #^
Having read your description of the desired function it seems that it boils down to this:
check the input argument. If it starts with refs/head or refs/master, proceed, else stop.
take the terminal piece of information and set the variable TAG to it.
thus, assuming your input is in the variable REF,
TAG=""
echo $REF | egrep -q 'refs/head|refs/master'
if [ $? -eq 0 ]
then
TAG=`echo $REF | sed "s/.*\///"`
fi
ought to do it.
(Side note for shell heads: is basename a better solution than sed in this particular case?)

shell script to compare files and print formatted output

I'm trying to write a shell script which will compare two files, and if there are no differences between then, it will indicate that there was a success, and if there are differences, it will indicate that there was a failure, and print the results. Here's what I have so far:
result = $(diff -u file1 file2)
if [ $result = "" ]; then
echo It works!
else
echo It does not work
echo $result
fi
Anybody know what I'm doing wrong???
result=$(diff -u file1 file2)
if [ $? -eq 0 ]; then
echo "It works!"
else
echo "It does not work"
echo "$result"
fi
Suggestions:
No spaces around "=" in the variable assignment for results
Use $? status variable after running diff instead of the string length of $result.
I'm in the habit of using backticks for command substitution instead of $(), but #Dennis Williamson cites some good reasons to use the latter after all. Thanks Dennis!
Applied quotes per suggestions in comments.
Changed "=" to "-eq" for numeric test.
First, you should wrap strings being compared with quotes.
Second, "!" cannot be use it has another meaning. You can wrap it with single quotes.
So your program will be.
result=$(diff -u file1 file2)
if [ "$result" == "" ]; then
echo 'It works!'
else
echo It does not work
echo "$result"
fi
Enjoy.
Since you need results when you fail, why not simply use 'diff -u file1 file2' in your script? You may not even need a script then. If diff succeeds, nothing will happen, else the diff will be printed.
bash string equivalence is "==".
-n is non-zero string, -z is zero length string, wrapping in quotes because the command will complain if the output of diff is longer than a single string with "too many arguments".
so
if [ -n "$(diff $1 $2)" ]; then
echo "Different"
fi
or
if [ -z "$(diff $1 $2)" ]; then
echo "Same"
fi

Resources