gitlab-ci - re-evaluation of variables breaks the "if" rule - if-statement

I have the following gitlab-ci yaml:
job:
stage: cool-stage
variables:
PUSH: "push"
FOO: $PUSH
rules:
- if: '$CI_PIPELINE_SOURCE == $PUSH'
when: always
script:
- echo $CI_PIPELINE_SOURCE
- echo $PUSH
- echo $FOO
- echo $( [[ $FOO == $CI_PIPELINE_SOURCE ]] && echo "FOO equals" || echo "not equals" )
- echo $( [[ $PUSH == $CI_PIPELINE_SOURCE ]] && echo "PUSH equals" || echo "not equals" )
- bash print-ascii-values.sh
It works fine, launches the script on each push and outputs the following:
$ echo $CI_PIPELINE_SOURCE
push
$ echo $PUSH
push
$ echo $FOO
push
$ echo $( [[ $FOO == $CI_PIPELINE_SOURCE ]] && echo "FOO equals" || echo "not equals" )
FOO equals
$ echo $( [[ $PUSH == $CI_PIPELINE_SOURCE ]] && echo "PUSH equals" || echo "not equals" )
PUSH equals
$ bash print-ascii-values.sh
PUSH
112 117 115 104
FOO
112 117 115 104
CI_PIPELINE_SOURCE
112 117 115 104
Cleaning up project directory and file based variables
00:00
Job succeeded
BUT! If I change the if rule to use FOO instead of PUSH, the pipeline doesn't run.
Why is that? What do I miss?

Related

How to structure a compound conditional with several tests (at least 1 of which is regex)

I searched for this but haven't found an answer to this particular situation. I'm familiar with file tests in shells and with using the [[ ]] syntax to perform regex matching.
Is there a way to combine these two operations in a compound conditional that doesn't require multiple nested ifs?
So far I've tried the following (...and other much crazier variations):
if [ -e ~/.profile -a $0 =~ bash ]; then echo yes ; fi
if [ -e ~/.profile -a ( $0 =~ bash ) ]; then echo yes ; fi
if [ -e ~/.profile -a [ $0 =~ bash ] ]; then echo yes ; fi
if [[ -e ~/.profile -a $0 =~ bash ]]; then echo yes ; fi
if [[ ( -e ~/.profile ) -a ( $0 =~ bash ) ]]; then echo yes ; fi
if [ -e ~/.profile -a $0 =~ bash ]; then echo yes; fi
if [ -e ~/.profile -a $( [ $0 =~ bash ] ) ]; then echo yes; fi
if [ -e ~/.profile -a [[ $0 =~ bash ]] ]; then echo yes; fi
if [ -e ~/.profile -a $([[ $0 =~ bash ]]) ]; then echo yes; fi
-a is treated as an AND when using single brackets, eg:
$ [ 3 -gt 1 -a 2 -lt 3 ] && echo 'true'
true
For double brackets you want to use &&, eg:
$ [[ 3 -gt 1 && 2 -lt 3 ]] && echo 'true'
true
Alternatively you can && to separate tests regardless of whether you're using single or double brackets, eg:
$ [ 3 -gt 1 ] && [ 2 -lt 3 ] && echo 'true'
true
$ [[ 3 -gt 1 ]] && [[ 2 -lt 3 ]] && echo 'true'
true
$ [ 3 -gt 1 ] && [[ 2 -lt 3 ]] && echo 'true'
true
NOTE: same rules apply for -o vs || (aka OR)
Apparently, when you want to represent a LOGICAL AND between these two statements, you must use && instead of -a (which the shell interprets as "does this file exist" file test in double brackets). Also, for the regex to work, the statement must be within [[ ]]. What was unknown to me at the time is that even though -a changes its meaning in double brackets, the -e -w -r and other file tests don't change their functionality (e.g. it's the same for single or double brackets).
if [[ -w ~/.bash_profile && $0 =~ bash ]]; then ( echo 1 ; echo 2 ) >> .bash_profile
elif [[ -w ~/.profile && <someothercondition> ]]; then
( echo 3
echo 4
echo 5
) >> .profile
fi

Bash comparison check to accept values only with format 0.1

I have this snippet:
if [[ $1 =~ ^[+-]?[0-9]+\.?[0-9]*$ ]]; then
echo 'version is good'
exit 0
else
exit 1
fi
The problem is that, the snippet $1 =~ ^[+-]?[0-9]+\.?[0-9]*$ should only validate versions formatted as number.number
Currently this chunk of code validates inputs as
1
01
0.1
Is there any way of making the code to only accept inputs formatted as 0.1 / 0.3.2 / 0.1.141 etc.
Thanks in advance.
EDIT:
To clarify this question, the code should only accept numbers separated with dots, like software program versioning.
I suggest this regex: ^[0-9]+(\.[0-9]+){1,2}$
I propose without regex :
[[ "0" == ?([+-])+([0-9]).+([0-9.]) ]] && echo OK # Nothing
[[ "01" == ?([+-])+([0-9]).+([0-9.]) ]] && echo OK # Nothing
[[ "0.1" == ?([+-])+([0-9]).+([0-9.]) ]] && echo OK # OK
[[ "0.3.2" == ?([+-])+([0-9]).+([0-9.]) ]] && echo OK # OK
[[ "0.1.141" == ?([+-])+([0-9]).+([0-9.]) ]] && echo OK # OK
[[ "10.1.141" == ?([+-])+([0-9]).+([0-9.]) ]] && echo OK # OK
To clarify this question, the code should only accept numbers
separated with dots, like software program versioning.
[[ "0" == +([0-9]).+([0-9.]) ]] && echo OK # Nothing
[[ "01" == +([0-9]).+([0-9.]) ]] && echo OK # Nothing
[[ "0.1" == +([0-9]).+([0-9.]) ]] && echo OK # OK
[[ "0.3.2" == +([0-9]).+([0-9.]) ]] && echo OK # OK
[[ "0.1.141" == +([0-9]).+([0-9.]) ]] && echo OK # OK
[[ "10.1.141" == +([0-9]).+([0-9.]) ]] && echo OK # OK

ksh if loop vs [[ ]] && { cmd1 ; } || { cmd2 ; }

I am working on a shell script and following syntax works fine:
if [[ 1 -eq 1 ]] ; then
[[ 2 -eq 1 ]]
else
echo hi
fi
Debug run :
+ [[ 1 -eq 1 ]]
+ [[ 2 -eq 1 ]]
If i shrink the statement it jumps to the else condition:
**[[ 1 -eq 1 ]] && { [[ 2 -eq 1 ]] ; } || echo hi**
Debug run:
+ [[ 1 -eq 1 ]]
+ [[ 2 -eq 1 ]]
+ echo hi
hi
any ideas what is missing?
Thanks much.
Sam
You could say the following:
[[ 1 -eq 1 ]] && { [[ 2 -eq 1 ]] ; true ; } || echo hi
if the first test passes, you go into the {...} block and once you run, what you wanted to run in there, by ending with true you do not continue past || here standing in for your else clause.
The problem is. [[ 2 -eq 1 ]] returned false and hence you get to your echo hi beyond ||.
All that said, you may see it looks a bit ugly and in this case (esp. as multiple conditions are involved which may impact how your pipeline gets evaluated). Well, I would recommend not saving on few newlines and for sake of obvious and readable code use the longer form.

Bash syntax errors: operand expected with arithmetic operations

I am constructing a script to count the occurrences of two letters in a given string. I cannot figure out how to make the variable a testable number.
#!/bin/bash
touch ~/trfindlog.txt ~/trfindt ~/trfindr
echo $1 > ~/trfindlog.txt
cat ~/trfindlog.txt | grep -oi r | wc -l > ~/trfindr
cat ~/trfindlog.txt | grep -oi t | wc -l > ~/trfindt
varR='/trfindr'
varT='/trfindt'
if [[ "${varR}" -eq 0 && "${varT}" -eq 0 ]]
then
echo "This phrase contains no Rs or Ts."
elif [[ "${varR}" -eq 1 && "${varT}" -eq 1 ]]
then
echo "This phrase contains 1 R and 1 T."
elif [[ "${varR}" -gt 1 && "${varT}" -eq 1 ]]
then
echo "This phrase contains ${varR} Rs and 1 T."
elif [[ "${varR}" -eq 1 && "${varT}" -gt 1 ]]
then
echo "This phrase contains 1 R and ${varT} Ts."
elif [[ "${varR}" -gt 1 && "${varT}" -gt 1 ]]
then
echo "This phrase contains ${varR} Rs and ${varT} Ts."
fi
rm ~/trfindlog.txt ~/trfindt ~/trfindr
exit
This script is giving me the following errors.
/automount/home/jcampbell/tools/itc/trfind.sh: line 12: [[: /trfindr: syntax error: operand expected (error token is "/trfindr")
/automount/home/jcampbell/tools/itc/trfind.sh: line 16: [[: /trfindr: syntax error: operand expected (error token is "/trfindr")
/automount/home/jcampbell/tools/itc/trfind.sh: line 20: [[: /trfindr: syntax error: operand expected (error token is "/trfindr")
/automount/home/jcampbell/tools/itc/trfind.sh: line 24: [[: /trfindr: syntax error: operand expected (error token is "/trfindr")
/automount/home/jcampbell/tools/itc/trfind.sh: line 28: [[: /trfindr: syntax error: operand expected (error token is "/trfindr")
Here is the working script. This is just for kicks & educating myself. I am glad to receive a variety of answers.
#!/bin/bash
touch ~/trfindlog.txt
echo $1 > ~/trfindlog.txt
varR=$(echo $1 | tr -cd r)
varT=$(echo $1 | tr -cd t)
if [[ "${#varR}" -eq 0 && "${#varT}" -eq 0 ]]
then
echo "This phrase contains no Rs or Ts."
elif [[ "${#varR}" -eq 1 && "${#varT}" -eq 1 ]]
then
echo "This phrase contains 1 R and 1 T."
elif [[ "${#varR}" -gt 1 && "${#varT}" -eq 1 ]]
then
echo "This phrase contains ${#varR} Rs and 1 T."
elif [[ "${#varR}" -eq 1 && "${#varT}" -gt 1 ]]
then
echo "This phrase contains 1 R and ${#varT} Ts."
elif [[ "${#varR}" -gt 1 && "${#varT}" -gt 1 ]]
then
echo "This phrase contains ${#varR} Rs and ${#varT} Ts."
fi
rm ~/trfindlog.txt
exit
Putting the variable in a file is cumbersome, inelegant, and, with a static filename, will break if you run two instances of the script at the same time. All of this would be a lot more succinct with the variable in memory.
With Bash, you can make a copy of the variable and perform a simple substitution.
Rs=${1//[!R]/}
Ts=${1//[!T]/}
Now, the length of each of these strings is the number of occurrences of the characters you were looking for.
echo "We have ${#Rs} R characters and ${#Ts} T characters."
Deciding whether to print a plural s should be a simple addition. Hint: If the first string is exactly R you want to suppress the s. But if you want flexible wording, it may be simpler to use a case statement over the possibilities.
case $Rs:$Ts in
:) echo "We have none of either";;
R:) echo "We have one R and no Ts.";;
:T) echo "We have no Rs and one T.";;
R:T) echo "We have one of each.";;
*:) echo "We have ${#Rs} Rs and no Ts.";;
*:T) echo "We have ${#Rs} Rs and one T.";;
:*) echo "We have no Rs and ${#Ts} Ts.";;
R:*) echo "We have one R and ${#Ts} Ts.";;
*:*) echo "We have ${#Rs} and ${#Ts} Ts.";;
esac
I would still be tempted to handle the cornermost cases : and R:T as above, and then generate a string from smaller pieces in the remaining cases.
You need to use something like
var=$(grep -c r ~/trfindlog.txt)
Note that grep will count the number of matching lines, and not the number of matches. So, what you really need is more something like:
var=$(echo $1 | tr -cd r)
echo ${#var}

Regex - translate "a" to "zzzz" ranges to code. Only sed or grep

Using ONLY the commands: echo, grep, sed
Argument $1 of the script is a code that has to be translated according to the table below and the translation sent to STDOUT. The code is a one to four digit code that starts in "A" and increments alphabetically to "ZZZZ". For example: A, B, .... Z, AA, AB,.....
Code - Translation
- A -> 1
- B to AA -> 2
- AB to AF -> 3
- AG to ZZZZ -> 4
For example if the script is called script.sh A the output would be 1. If the script is called script.sh ABC the output would be 4.
#!/bin/bash
echo $1 | grep -e '^A$'>/dev/null && echo 1 && exit
echo $1 | grep -e '^[^A]$' -e '^[A][A]$' >/dev/null && echo 2 && exit
echo $1 | grep -e '^[A][B-F]$' >/dev/null && echo 3 && exit
echo $1 | grep -e '^[A-Z]\{2,4\}$' >/dev/null && echo 4 && exit
echo 0
exit
#!/bin/bash
str=$1
if [[ -z "${str#A}" ]]
then
echo 1
elif [[ -z "${str#?}" || -z "${str#AA}" ]]
then
echo 2
elif [[ -z "${str/A[B-F]/}" ]]
then
echo 3
else
echo 4
fi
# just in case, so you can say you used them:
grep . < /dev/null | sed > /dev/null
EDIT: silly ., you should be ?.
EDIT2: Command [ is gone, non-command [[ to the rescue!