if-else on arguments in npm run script - npm

I would like to call different other scripts, depending on whether a paramter is given or not:
"paramtest": "if [ -z $1 ]; then echo Foo $1; else echo Bar; fi",
npm run paramtest
should give "Bar".
npm run paramtest -- whatever
should give "Foo whatever".
However in practice I only get: (The parameter is added to the whole line, not 'passed in')
> if [ -z $1 ]; then echo Foo; else echo Bar; fi "whatever
sh: 1: Syntax error: word unexpected
What can I do better?
Essentially I am after running full test suite / only individual test with the same command...
"test" : "if [ -z $1 ]; then mocha ./test/**/*.test.js; else mocha $1

Wrapping it in a shell function should do the trick:
"test": "f() { if [ $# -eq 0 ]; then mocha './test/**/*.test.js'; else mocha -- \"$#\"; fi; }; f"
Note that I changed the if condition and the else branch slightly so you can specify multiple file arguments if necessary.
A more succinct method:
"test": "f() { mocha -- \"${#:-./test/**/*.test.js}\"; }; f"
Using a shell function this way might look familiar, as the same technique is often used for git aliases.
Detailed Explanation
Let's use this script for demonstration:
"scripts": {
"myscript": "if [ \"$1\" = one ]; then printf %s\\\\n \"$#\"; else echo false; fi"
}
Here if the first argument is "one", we print all the arguments, and otherwise we print "false". We are of course assuming that npm run-script is using an sh-like shell, and not, e.g., Windows' cmd.exe.
I can't see anything in the npm documentation specifically detailing how arguments are passed to the script, so let's take a look at the source code (npm v6.14.7 at the time of writing). It seems that the script is joined with its arguments here and is then executed here. Essentially, npm run myscript -- one two three becomes
sh -c 'if [ "$1" = one ]; then printf %s\\n "$#"; else echo false; fi "one" "two" "three"'
Our arguments one two three are simply quote-escaped and concatenated to the script command. In terms of the shell grammar, this means that they are ending up as arguments to fi. sh of course rejects this because fi is just a builtin to end if and takes no arguments.
Our goal is something more like
sh -c 'if [ "$1" = one ]; then printf %s\\n "$#"; else echo false; fi' sh "one" "two" "three"
Here one, two, and three are arguments to sh itself and thus become the argument variables $1, $2, and $3 in the given script. npm doesn't let us do this directly, but we can accomplish the same thing by wrapping our script in a shell function:
"scripts": {
"myscript": "f() { if [ \"$1\" = one ]; then printf %s\\\\n \"$#\"; else echo false; fi; }; f"
}
The script here ends with an invocation of the function, so npm will end up concatenating the arguments to this invocation, ultimately calling the function as f "one" "two" "three":
sh -c 'f() { if [ "$1" = one ]; then printf %s\\n "$#"; else echo false; fi; }; f "one" "two" "three"'

Related

jenkins on windows. Declarative pipeline jenkinsfile. how to set and get correct variable values

I use Jenkinsefile file to run the Stages.
It is in Jenkins pipeline installed on windows, Declarative pipeline.
On the begining I do:
pipeline {
agent { label 'master'}
environment {
My_build_result = 7
}
....
Than
stage('Test') {
steps {
echo 'Testing..'
bat """
cd Utils
"C:\\Program Files\\MATLAB\\R2019b\\bin\\matlab.exe" -wait -nodisplay -nosplash -nodesktop -r "run('automatic_tests\\run_test.m');"
echo %errorlevel%
set /a My_build_result_temp = %errorlevel%
set My_build_result = %My_build_result_temp%
"""
script {
My_build_result = bat(returnStatus:true , script: "exit (2)").trim()
echo "My_build_result ${env.My_build_result}"
if (My_build_result != 0) {
echo "inside if"
}
}
}
}
The variable My_build_result get value 7 at the begining
Inside the bat section, it suppose to get value 0 from %errorlevel%
Inside the script section it suppose to get value 2
BUT
in the echo "My_build_result ${env.My_build_result}" I get print of 7
(and it goes inside the if sentense)
How do I define variable that can be set value in bat"""
"""
and in script """
"""
section of the stage
and also be familiar in another stages and in the post { always { .. }} at the end ???
BTW: add env.before My_build_result (env.My_build_result ) does not work
Thanks a lot
In the first bat call, you are setting the environment variable only inside of the batch script environment. Environment variable values that are assigned through set don't persist when the script ends. Think of these like local variables. Simply use returnStatus: true to return the last value of ERRORLEVEL. There is no need to use %ERRORLEVEL% in the batch script here.
steps {
script {
My_build_result = bat returnStatus: true, script: """
cd Utils
"C:\\Program Files\\MATLAB\\R2019b\\bin\\matlab.exe" -wait -nodisplay -nosplash -nodesktop -r "run('automatic_tests\\run_test.m');"
"""
// My_build_result now has the value of ERRORLEVEL from the last command
// called in the batch script.
}
}
In the 2nd bat call the 1st mistake is to call the trim() method. Result type of bat step is Integer, when returnStatus: true is passed. The trim() method is only available when returnStdout: true is passed in which case the result type would be String. The 2nd mistake is to use brackets around the exit code value. The fixed code should look like:
My_build_result = bat returnStatus: true, script: "exit 2"
// My_build_result now equals 2

Unable to substitute Jenkins build number variable in pipeline script

I have a pipeline in which I am trying to pass the Build Number.
When I print the Build Number its prints fine however, I am having issues passing it to the a shell ansible command inside a stage.
pipeline {
agent { label 'Prod_Slave' }
stages {
stage('Ansible stuff') {
parallel {
stage('APP') {
steps {
echo "${env.BUILD_NUMBER}"
println "${env.BUILD_NUMBER}"
sh 'ansible-playbook $ansible_playbook/site.yml -e Latest_Build_Number4=${env.BUILD_NUMBER}'
}
}
}
}
}
}
Output:
[Pipeline]
echo (hide)
16 [Pipeline]
echo 16 [Pipeline]
echo 16
/script.sh: line 1: Latest_Build_Number4=${env.BUILD_NUMBER}: bad
substitution
When i change to:
sh 'ansible-playbook $ansible_playbook/site.yml -e Latest_Build_Number4="${env.BUILD_NUMBER}"'
Output:
#2#tmp/durable-5ed077ca/script.sh: line 1: ${env.BUILD_NUMBER}: bad
substitution
Finally tried this:
sh 'ansible-playbook $ansible_playbook/site.yml -e Latest_Build_Number4="$env.BUILD_NUMBER"'
Output:
-e Latest_Build_Number4=.BUILD_NUMBER
Single quotes won't allow substitution. Try double quotes, escaping the dollar sign:
sh "ansible-playbook \$ansible_playbook/site.yml -e Latest_Build_Number4=${env.BUILD_NUMBER}"

SSHRC scrip not working > Unexpected Operator [duplicate]

I'm writing script for git hook and have trouble with if statement inside while.
File:
#!/bin/sh
while read oldrev newref ref
do
branch=$(git rev-parse --symbolic --abbrev-ref $ref)
if [ "a" == "a" ]
then
echo "Condition work"
fi
echo "$branch"
done
Error:
hooks/post-receive: 6: [: a: unexpected operator
I'll try with variables, double quotes but if doesn't work. What kind of error is here?
Thanks
if [ "a" == "a" ] should be if [ "a" = "a" ].
bash accepts == instead of =, but your /bin/sh probably isn't bash.
So either change the == to =, or your shebang to #!/bin/bash

Keep set -e setting inside || or &&

I have a simple script with a simple function which can lead to an error. Let's define this function, and make it broken:
brokenFunction () {
ls "non-existing-folder"
}
If we execute this function in a block detecting if it is broken, it works well:
brokenFunction || printf "It is broken\n"
prints "It is broken"
Now, let's make the function a bit more complex, by adding a correct command at the end :
#!/bin/sh
brokenFunction () {
ls "non-existing-folder"
printf "End of function\n"
}
brokenFunction || printf "It is broken\n"
This script prints :
$ ./script.sh
ls: cannot access 'non-existing-folder': No such file or directory
End of function
while I expected the function to stop before the printf statement, and the next block to display "It is broken".
And indeed, if I check the exit status code of brokenFunction, it is 0.
I tried adding set -e to the top of the script. The behavior is still the same, but the exit code of brokenFunction if called without || now becomes 2. If called with it, the status code is still 0.
Is there any way to keep the set -e setting inside a function called with ||?
EDIT: I just realized that the function in the example was useless. I encounter the same issue with a simple block and a condition.
#!/bin/sh
set -e
{
ls "non-existing-dir"
printf "End of block\n"
} || {
printf "It is broken\n"
}
prints
$ ./script.sh
ls: cannot access 'non-existing-dir': No such file or directory
End of block
As written in man bash, set -e is ignored in some contexts. A command before || or && is such a context.
trap looks like a possible solution here. A working alternative to the last script using trap would look like that:
#!/bin/sh
abort () {
printf "It is broken\n"
}
trap 'abort' ERR
(
set -e
false
printf "End of block\n"
)
trap - ERR
Some things have to be noticed here:
trap 'abort' ERR binds the abort function to any raised error ;
the broken block is executed in a sub-shell for 2 reasons. First is to keep the set -e setting inside the block and limit the border effects. Second is to exit this sub-shell on error (set -e effect), and not the whole script ;
trap - ERR at the end resets the trap binding, meaning the following part of the script is executed as before.
To test the border effects, we can add the previously non-working part :
#!/bin/sh
abort () {
printf "It is broken\n"
}
trap 'abort' ERR
(
set -e
false
printf "End of block\n"
)
trap - ERR
{
false
printf "End of second block\n"
} || {
printf "It is broken too\n"
}
prints:
It is broken
End of second block

Match pattern for variable in Shell Programming

it seems that I keep getting -gt or == error with the following. Can someone help?
flag= echo $flightSeatBooked | awk -F[,] '{print match($flightSeatBooked, $orderSeats)}'
if $flag == 0; then
echo "Success";
else
echo "fail";
Given:
flightSeatBooked= 9;,A1,A2,A3,A4,B2,E4,C3,B3,D3,D2,E1,E2,C2,B4,C4,D4,C1,D1,E3,B1
orderSeats= B2 (not found in the variable)
Expected output:
Success
Quite a few mistakes. Change it like this:
flag=$(echo $flightSeatBooked | awk -v flseat=$flightSeatBooked -v orseat=$orderSeats '{print match(flseat, orseat)}')
if [ $flag -eq 0 ]; then
echo "Success";
else
echo "fail";
fi
Command substitution has been done using the $(...) notation.
It is not a good practice to use the shell variables directly in awk, and hence passed shell variables to awk using -v.
The syntax of if used was incorrect, updated to correct it.
This is how to do what you ask:
flag=$(awk -v flseat="$flightSeatBooked" -v orseat="$orderSeats" 'BEGIN{print index(flseat, orseat)}')
if [ $flag -eq 0 ]; then
echo "Success"
else
echo "fail"
fi
BUT I don't think what you ask is a good idea. It at least should be something like this:
awk -v flseat="$flightSeatBooked" -v orseat="$orderSeats" 'BEGIN{exit index(flseat, orseat)}')
if [ $? -eq 0 ]; then
echo "Success"
else
echo "fail"
fi
and all you probably really need is something like this:
case "$flightSeatBooked" in
*"$orderSeats"* ) echo "fail";;
* ) echo "Success" ;;
esac
Check the logic (as I haven't!), but hopefully you get the approach.
You can also use this below to check whether $orderSeats is in $flightSeatBooked. If it is in then it return the length of string that matched or 0 is returned.
expr "$flightSeatBooked" : ".*,${orderSeats},"