I have the following script. Unfortunately I not able to get it run on Jenkins.
#!/bin/bash
function pushImage () {
local serviceName=$1
local version=$(getImageVersionTag $serviceName)
cd ./dist/$serviceName
docker build -t $serviceName .
docker tag $serviceName gcr.io/$PROJECT_ID/$serviceName:$version
docker tag $serviceName gcr.io/$PROJECT_ID/$serviceName:latest
docker push gcr.io/$PROJECT_ID/$serviceName
cd ../..
}
function getImageVersionTag () {
local serviceName=$1
if [ $BUILD_ENV = "dev" ];
then
echo $(timestamp)
else
if [ $serviceName = "api" ];
then
echo $(git tag -l --sort=v:refname | tail -1 | awk -F. '{print $1"-"$2"-"$3"-"$4}')
else
echo $(git tag -l --sort=refname | tail -1 | awk -F. '{print $1"-"$2"-"$3"-"$4}')
fi
fi
}
function timestamp () {
echo $(date +%s%3N)
}
set -x
## might be api or static-content
pushImage $1
I'm receiving this error on Jenkins
10:10:17 + sh push-image.sh api
10:10:17 push-image.sh: 2: push-image.sh: Syntax error: "(" unexpected
I already configured Jenkins global parameter to /bin/bash as default shell execute environment, but still having same error.
The main issue here in usage of functions, as other scripts that has been executed successfully don't have any.
How this can be fixed?
Short answer: make sure you're running bash and not sh
Long answer: sh (which is run here despite your effort of adding a shebang) is the bourne shell and does not understand the function keyword. Simply removing it will solve your issue.
Please note however that all your variable expansions should be quoted to prevent against word splitting and globbing. Ex: local version=$(getImageVersionTag "$serviceName")
See shellcheck.net for more problems appearing in your file (usage of local var=$(...)) and explicit list of snippets which are missing quotes.
Related
I'm using the post-checkout hook to try and set environment variables when switching branches.
#!/bin/sh
echo "Updating environment variables..."
OLD_IFS=$IFS
IFS=$'\n'
for x in $(cat .env | sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/g" -e "s/=\(.*\)/='\1'/g")
do
var_name=$( cut -d '=' -f 1 <<< "$x" )
export $x
pwsh.exe -c "\$env:$x"
pwsh.exe -c "echo 1; echo \$env:$var_name"
export $x
done
IFS=$OLD_IFS
The problem is that git hook is executed with WSL so the variables I set are lost after the post-hook
I assume this is because of the shebang?
I've tried #!/usr/bin/env pwsh but I get the error Processing -File '.git/hooks/post-checkout' failed because the file does not have a '.ps1' extension. Specify a valid PowerShell script file name, and then try again.
Is this something that can be done? I want to automatically change the DB connection when I switch branches.
As anthony sottlie noted, you can't do it that way.
What you need instead is a command that you run instead of git switch or git checkout. In this command, you will:
run git switch or git checkout, then
set the environment variables you would have set in your script, the way you would have set them
and since this will be done by the command itself, rather than in a subprocess, it will affect further commands run by this same command-line interpreter.
I have an application that runs inside a docker container. First I build the image and then run the container. My run command is:
docker run --rm -it -e MODE=custom -e Station=RT -e StartDateReport=2022-09-10 -e Period=1 my-image:1.0.0
I declare the variables MODE, Station, StartDateReport and Period as environment variables. When I start a terminal from the container and type echo $MODE I will get the correct value, custom.
So far, so good, but I am interested in using these variables in a bash script. For example in start.sh I have the following code:
#!/bin/bash
if [[ $MODE == custom ]]; then
// do sth
fi
and here inside the script my variable MODE is undefined, and hence I obtain wrong results.
EDIT
As discussed in the comments below, my application if based on a cronjob to start running.
I managed to solve by myself the problem and the answer is in the comments.
In your environment, does your variable definition have the form
export MODE="custom"
Modified version of your script:
#!/bin/bash
test -z "${MODE}" && ( echo -e "\n\t MODE was not exported from calling environment.\n" ; exit 1 )
if [[ $MODE == custom ]]
then
#// do sth
echo "do sth"
fi
I found the solution for this problem, so I will post the answer here to help others that have the same problem. I found the solution here: How to load Docker environment variables in container
I included export xargs --null --max-args=1 echo < /proc/1/environ in start.sh
Thus, start.sh will be:
#!/bin/bash
export xargs --null --max-args=1 echo < /proc/1/environ
if [[ $MODE == custom ]]; then
// do sth
fi
I'm trying to create a system for my scripts -
Each script will be located in a folder, which is the command itself.
The script itself will act as a sub-command.
For example, a script called "who" inside a directory called "git",
will allow me to run the script using git who in the command line.
Also, I would like to create a sub command to a psuedo-command, meaning a command not currently available. E.g. some-arbitrary-command sub-command.
Is that somehow possible?
I thought of somehow extending https://github.com/basecamp/sub to accomplish the task.
EDIT 1
#!/usr/bin/env bash
command=`basename $0`
subcommand="$1"
case "$subcommand" in
"" | "-h" | "--help" )
echo "$command: Some description here" >&2
;;
* )
subcommand_path="$(command -v "$command-$subcommand" || true)"
if [[ -x "$subcommand_path" ]]; then
shift
exec "$subcommand_path" "${#}"
return $?
else
echo "$command: no such command \`$subcommand'" >&2
exit 1
fi
;;
esac
This is currently the script I run for new custom-made commands.
Since it's so generic, I just copy-paste it.
I still wonder though -
can it be generic enough to just recognize the folder name and create the script by its folder name?
One issue though is that it doesn't seem to override the default command name, if it supposed to replace it (E.g. git).
EDIT 2
After tinkering around a bit this is what I came to eventuall:
#!/usr/bin/env bash
COMMAND=`basename $0`
SUBCOMMAND="$1"
COMMAND_DIR="$HOME/.zsh/scripts/$COMMAND"
case "$SUBCOMMAND" in
"" | "-h" | "--help" )
cat "$COMMAND_DIR/help.txt" 2>/dev/null ||
command $COMMAND "${#}"
;;
* )
SUBCOMMAND_path="$(command -v "$COMMAND-$SUBCOMMAND" || true)"
if [[ -x "$SUBCOMMAND_path" ]]; then
shift
exec "$SUBCOMMAND_path" "${#}"
else
command $COMMAND "${#}"
fi
;;
esac
This is a generic script called "helper-sub" I symlink to all the script directories I have (E.g. ln -s $HOME/bin/helper-sub $HOME/bin/ssh).
in my zshrc I created this to call all the scripts:
#!/usr/bin/env bash
PATH=${PATH}:$(find $HOME/.zsh/scripts -type d | tr '\n' ':' | sed 's/:$//')
export PATH
typeset -U path
for aliasPath in `find $HOME/.zsh/scripts -type d`; do
aliasName=`echo $aliasPath | awk -F/ '{print $NF}'`
alias ${aliasName}=${aliasPath}/${aliasName}
done
unset aliasPath
Examples can be seen here: https://github.com/iwfmp/zsh/tree/master/scripts
You can't make a directory executable as a script, but you can create a wrapper that calls the scripts in the directory.
You can do this either with a function (in your profile script or a file in your FPATH) or with a wrapper script.
A simple function might look like:
git() {
local subPath='/path/to/your/git'
local sub="${1}" ; shift
if [[ -x "${subPath}/${1}" ]]; then
"${subPath}/${sub}" "${#}"
return $?
else
printf '%s\n' "git: Unknown sub-command '${sub}'." >&2
return 1
fi
}
(This is the same way that the sub project you linked works, just simplified.)
Of course, if you actually want to create a sub-command for git specifically (and that wasn't just an example), you'll need to make sure that the built-in git commands still work. In that case you could do like this:
git() {
local subPath='/path/to/your/git'
local sub="${1}"
if [[ -x "${subPath}/${sub}" ]]; then
shift
"${subPath}/${sub}" "${#}"
return $?
else
command git "${#}"
return 1
fi
}
But it might be worth pointing out in that case that git supports adding arbitrary aliases via git config:
git config --global alias.who '!/path/to/your/git/who'
The following almost works:
#!/bin/bash
/* 2>&1 >/dev/null
script_dir=$(dirname "$0")
export GROOVY_HOME=${script_dir}/../../../../Tools/groovy/groovy-2.0.2
exec ${GROOVY_HOME}/bin/groovy -cp "${script_dir}:$(ls ${script_dir}/build/lib/runtime/*.jar | xargs echo | sed -e 's| |:|g')" "$0"
*/ // 2>&1 >/dev/null
println("aoeu")
The only problem is that the shell globs /* and tries to execute it. In the end, all I really want to do is to be able to build the Groovy script's classpath without having to have two separate scripts.
You can also try the technique below, which is independent of Groovy syntax (and does not normally produce output on stderr):
#!/bin/sh
script_dir=$(dirname "$0")
export GROOVY_HOME="$script_dir/../../../../Tools/groovy/groovy-2.0.2"
awk 'mark_on{print}/^__END__$/{mark_on=1}' "$0" >/tmp/$$.groovy
"$GROOVY_HOME/bin/groovy" -cp "$script_dir:$(echo "$script_dir"/build/lib/runtime/*.jar | tr " " :)" /tmp/$$.groovy
status=$?
rm -f /tmp/$$.groovy
exit $status
__END__
println("aoeu")
Also notice the simplification in the classpath calculation; remember that globbing (wildcard expansion) is performed by the shell, not by the command that takes the arguments, so you do not have to (nor do you want to) use ls in this case.
The curly brackets ${} in your original code are technically superfluous in this case (they are purely stylistic); they would be needed if you had for example to append a string directly after a variable substitution where there is no clear break between the variable name and what follows, e.g. you cannot say $my_varsome_string but you can write ${my_var}some_string, or any of $my_var"some_string" or $my_var'some_string' or "$my_var"some_string or "$my_var""some_string". I removed the braces for "minimalistic" purposes and in order to illustrate the above, but again, it's perfectly fine to keep them for stylistic reasons.
The quotes I added consistently in the code above protect you from potential blanks and certain other special characters inside $GROOVY_HOME. Feel free to remove them in order to simplify quoting (and be minimalistic) if you know $GROOVY_HOME will not contain blanks.
It seems you want to write a groovy / shell polyglot.
I don't know groovy, but from the documentation it seems [] is a valid groovy command, creating an empty list.
Then you could write it as follow:
#!/bin/bash
[ /* 2> /dev/null > /dev/null
script_dir=$(dirname "$0")
export GROOVY_HOME=${script_dir}/../../../../Tools/groovy/groovy-2.0.2
exec ${GROOVY_HOME}/bin/groovy -cp "${script_dir}:$(ls ${script_dir}/build/lib/runtime/*.jar | xargs echo | sed -e 's| |:|g')" "$0"
*/
]
println("aoeu")
Groovy will read [] and ignore it, and bash will call [ with /*, which will cause an error that is ignored. But it will not run any unexpected programs.
#!/bin/bash
script_dir="$(cd $(dirname $0) >/dev/null; pwd -P)"
function after-bangshe() {
sed -e '1,/^!#$/d' "$1"
}
if [ -z "${GROOVY_HOME}" ]
then
echo 'GROOVY_HOME must be defined.' >&2
exit 1
fi
CLASSPATH="${script_dir}" "${GROOVY_HOME}/bin/groovy" -e "$(after-bangshe $0)" "$#"
exit
!#
println 'aoeu'
The output of cd is redirected to /dev/null in case CDPATH is set (which makes cd noisy).
CLASSPATH is set to the script directory so that any support classes can be found.
The sed command strips out everything after the !# line.
Recently I have migrated to the new dedicated server which is running on the same operating system - FreeBSD 8.2. I got a root account access and all permissions have been set properly.
My problem is that, the bash script I was running on the old server doesn't works on the new machine, the only error appearing while running the script is:
# sh script.sh
script.sh: 3: Syntax error: word unexpected (expecting ")")
Here is the code itself:
#!/usr/local/bin/bash
PORTS=(7777:GAME 11000:AUTH 12000:DB)
MESSG=""
for i in ${PORTS[#]} ; do
PORT=${i%%:*}
DESC=${i##*:}
CHECK=`sockstat -4 -l | grep :$PORT | awk '{print $3}' | head -1`
if [ "$CHECK" -gt 1 ]; then
echo $DESC[$PORT] "is up ..." $CHECK
else
MESSG=$MESSG"$DESC[$PORT] wylaczony...\n"
if [ "$DESC" == "AUTH" ]; then
MESSG=$MESSG"AUTH is down...\n"
fi
if [ "$DESC" == "GAME" ]; then
MESSG=$MESSG"GAME is down...\n"
fi
if [ "$DESC" == "DB" ]; then
MESSG=$MESSG"DB is down...\n"
fi
fi
done
if [ -n "$MESSG" ]; then
echo -e "Some problems ocurred:\n\n"$MESSG | mail -s "Problems" yet#another.com
fi
I don't really code in bash, so I don't know why this happend...
Bourne shell (sh) doesn't support arrays, that's why you're running into this error when you use
sh script.sh
Use bash instead
bash script.sh
Note: I suspect that sh script.sh worked on the old server because sh is linked to bash there.
also you shouldn't need to run it through sh (that's what the
#!
on the first line is for - the OS will run the remainder of the line as a command and pass the contents of the file for it to interpret). Just make the script executable:
chmod +x script.sh
and then you can just run it directly without the sh in front of the name.
It's possible that the default shell is not bash and so by running it through sh you're interpreting it with a different shell which is then giving the error
The code looks good. It is likely that your new dedicated server is running older version of Bash than your last server. Or maybe /usr/local/bin/bash is pointing towards older version.
Run
$ which bash
if the output is other than /usr/local/bin/bash then change the first shebang line to the newer path, if it still does not work
Try replacing third line:
PORTS=(7777:GAME 11000:AUTH 12000:DB)
with
PORTS=('7777:GAME' '11000:AUTH' '12000:DB')
and rerun the script.
If it still does not work then post the BASH version here by running
$ bash --version
try with facing and trailing spaces
PORTS=( 7777:GAME 11000:AUTH 12000:DB )