I am trying to exec a bash script via Node.js using child_process.exec(). However it is blowing up on the second line of the file:
#!/usr/bin/env bash
set -eo pipefail; [[ $TRACE ]] && set -x
echo "we are here"
The error returned is:
/bin/sh: 2: set: Illegal option -o pipefail
Why is this happening? When I run the script manually, not from Node it works fine. Here is the Node.js code:
var child = child_proc.exec(bashScript, {
env: _.extend(process.env, {
'LB_HOST': config.loadBalancers.lb1
}),
timeout: 0
});
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
By default when you invoke child_process.exec() it uses /bin/sh which on Ubuntu is actually a symbolic link pointing to /bin/dash. Dash is a stripped down version of bash and I guess does not support:
set -eo pipefail; [[ $TRACE ]] && set -x
Adding the shell option to the Node.js child_proc.exec() fixes this:
shell: '/bin/bash'
Related
This question already has answers here:
Setting environment variable in shell script does not make it visible to the shell
(2 answers)
Closed 4 months ago.
I am writing a bash script to automate the task of setting environment variables for my project. but when I execute my bash script using sh env.sh (env.sh is my file name). I am able to get value from the AWS secret manager and when I do echo inside the bash script I am able to print the env variable but when I run the echo $variable after the bash file is executed then it returns nothing.
I tried replacing eval to source but no luck
also i searched on stackoverflow for the issue but none of them helped.
find the script below
#! /usr/bin/env bash
if [[ "$OSTYPE" == "darwin"* ]]; then
echo 'running'
if ! [ -x "$(command -v aws)" ]; then
echo 'Aws is not installed. Installing aws............................' >&2
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
if ! [ $(id -u) = 0 ]; then
echo "The script need to be run as root." >&2
exit 1
fi
sudo installer -pkg AWSCLIV2.pkg -target /
if ! [ -x "$(command -v aws)" ]; then
echo 'There was some issue installing aws cli. Install aws-cli manually and then run the script!!!' >&2
exit 1
fi
echo "Running aws command please enter the aws access key and secrect"
aws configure
fi
aws secretsmanager get-secret-value --secret-id abc --query SecretString --output text | jq -r 'to_entries|map("\(.key)=\(.value|tostring)")|.[]' > /tmp/secrets.env
eval $(cat /tmp/secrets.env | sed 's/^/export /')
fi
I am currently running this bash file on Mac OS, but I would like it to operate on any OS.
If the file contained enviroment variable names setup_local_env.sh, Try
source setup_local_env.sh
This will add them to your current session.
There is another solution called dot source. Check the reference here
. ./setup_local_env.sh
The reason if you directly run ./setup_local_env.sh, it does not work, is because it creates a new bash process, and sets the environment variable there, and then it's lost once the new bash process exits.
i have uploaded a test script remote.sh to a remote webserver like this :
#!/usr/bin/bash
echo "input var is : $1"
and i have a local script local.sh like this :
#!/usr/bin/bash
curl -sS https://remote_host/remote.sh | bash
then i run the local script with some inline parameter :
./local.sh "some input here."
but the remote script i grabbed doesn't seem to see the local inline parameter. how can this be done ?
Your code is starting a second copy of bash, and not passing the arguments retrieved to it.
I would generally suggest not starting a second copy of bash at all:
#!/usr/bin/env bash
eval "$(curl -sS https://remote_host/remote.sh)"
...but you could proceed to do so and pass them through. The following passes that code on the command line, leaving stdin free (so the new copy of bash being started can use it to prompt the user):
#!/bin/sh
code=$(curl -sS https://remote_host/remote.sh) || exit
exec bash -c "$code" bash "$#"
...or, to continue using stdin to pass code, bash -s can be used:
#!/bin/sh
curl -sS https://remote_host/remote.sh | bash -s -- "$#"
By the way -- everywhere I use /bin/sh above you could substitute /bin/bash or any other POSIX-compliant shell; the point being made is that the code given above does not depend on behaviors that are unspecified in the POSIX.2 standard.
When I run this command
set -e; echo $(echo "$-");
I get himBH as the output. I was expecting the letter e to be included in the output. Whats going on?
I'm on Ubuntu 16.04.1 LTS with
GNU bash, version 4.3.46(1)-release (x86_64-pc-linux-gnu)
Command substitutions do not inherit the errexit option unless you are in POSIX mode or you use the inherit_errexit shell option (added to bash 4.4).
192% bash -ec 'echo "$(echo "$-")"'
hBc
192% bash --posix -ec 'echo "$(echo "$-")"'
ehBc
192% bash -O inherit_errexit -ec 'echo "$(echo "$-")"' # 4.4+
ehBc
This question!
Worked on this for a couple of hours until I found htis.
I was not able to have set -e inherited to the subshells.
This is my proof of concept:
#!/usr/bin/env bash
set -euo pipefail
# uncomment to handle failures properly
# shopt -s inherit_errexit
function callfail() {
echo "SHELLOPTS - callfail - $SHELLOPTS" >&2
local value
value=$(fail)
echo "echo will reset the result to 0"
}
function fail() {
echo "SHELLOPTS - fail - $SHELLOPTS" >&2
echo "failing" >&2
return 1
}
function root() {
local hello
hello=$(callfail)
echo "nothing went bad in callfail"
callfail
echo "nothing went bad in callfail"
}
root
execution without shopt -s inherit_errexit:
$ ./test.sh
SHELLOPTS - callfail - braceexpand:hashall:interactive-comments:nounset:pipefail
SHELLOPTS - fail - braceexpand:hashall:interactive-comments:nounset:pipefail
failing
nothing went bad in callfail
SHELLOPTS - callfail - braceexpand:errexit:hashall:interactive-comments:nounset:pipefail
SHELLOPTS - fail - braceexpand:hashall:interactive-comments:nounset:pipefail
failing
execution with shopt -s inherit_errexit:
$ ./test.sh
SHELLOPTS - callfail - braceexpand:errexit:hashall:interactive-comments:nounset:pipefail
SHELLOPTS - fail - braceexpand:errexit:hashall:interactive-comments:nounset:pipefail
failing
I have a sh script that I am using on Windows and Mac/Linux machines, and seems to work with no issues normally.
#!/bin/bash
if [ -z "$jmxname" ]
then
cd ./tests/Performance/JMX/ || exit
echo "-- JMX LIST --"
# set the prompt used by select, replacing "#?"
PS3="Use number to select a file or 'stop' to cancel: "
# allow the user to choose a file
select jmxname in *.jmx
do
# leave the loop if the user says 'stop'
if [[ "$REPLY" == stop ]]; then break; fi
# complain if no file was selected, and loop to ask again
if [[ "$jmxname" == "" ]]
then
echo "'$REPLY' is not a valid number"
continue
fi
# now we can use the selected file, trying to get it to run the shell script
rm -rf ../../Performance/results/* && cd ../jmeter/bin/ && java -jar ApacheJMeter.jar -Jjmeter.save.saveservice.output_format=csv -n -t ../../JMX/"$jmxname" -l ../../results/"$jmxname"-reslut.jtl -e -o ../../results/HTML
# it'll ask for another unless we leave the loop
break
done
else
cd ./tests/Performance/JMX/ && rm -rf ../../Performance/results/* && cd ../jmeter/bin/ && java -jar ApacheJMeter.jar -Jjmeter.save.saveservice.output_format=csv -n -t ../../JMX/"$jmxname" -l ../../results/"$jmxname"-reslut.jtl -e -o ../../results/HTML
fi
I am now trying to do some stuff with a Docker container and have used a node:alpine image, as the rest of my project is NodeJS based, but for some reason the script will not run in the Docker container giving the following -
line 12: syntax error: unexpected "do" (expecting "fi")
How can I fix that? The script seems to be working for every system it's been run on so far, and not thrown up any issues.
The error message indicates that the script is executed as '/bin/sh', and not as /bin/bash. You can see the message with '/bin/sh -n script.sh'
Check how the script is invoked. On different systems /bin/sh is symlinked to bash or other shell that is less feature rich.
In particular, the problem is with the select statement, included in bash, but not part of the POSIX standard.
Another option is that bash on your docker is set to be POSIX compliant by default
#dash-o was correct, and adding -
RUN apk update && apk add bash
to my dockerfile added bash into the container and now it works fine :)
Scripts in linux start with some declaration like :
#!/bin/bash
Correct me if I am wrong : this probably says which shell to use.
I have also seen some scripts which say :
#!/bin/bash -ex
what is the use of the flags -ex
#!/bin/bash -ex
<=>
#!/bin/bash
set -e -x
Man Page (http://ss64.com/bash/set.html):
-e Exit immediately if a simple command exits with a non-zero status, unless
the command that fails is part of an until or while loop, part of an
if statement, part of a && or || list, or if the command's return status
is being inverted using !. -o errexit
-x Print a trace of simple commands and their arguments
after they are expanded and before they are executed. -o xtrace
UPDATE:
BTW, It is possible to set switches without script modification.
For example we have the script t.sh:
#!/bin/bash
echo "before false"
false
echo "after false"
And would like to trace this script: bash -x t.sh
output:
+ echo 'before false'
before false
+ false
+ echo 'after false'
after false
For example we would like to trace script and stop if some command fail (in our case it will be done by command false): bash -ex t.sh
output:
+ echo 'before false'
before false
+ false
These are documented under set in the SHELL BUILTIN COMMANDS section of the man page:
-e will cause Bash to exit as soon as a pipeline (or simple line) returns an error
-x will case Bash to print the commands before executing them
-e
is to quit the script on any error
-x
is the debug mode
Check bash -x command
and What does set -e mean in a bash script?