I am confused with this behavior, I have the following script:
backup.sh
#!/bin/bash -x
set -e
if [[ $# -eq 0 ]] ; then
echo 'No arguments passed'
exit 1
fi
# Get the arguments
for ARGUMENT in "$@"; do
KEY=$(echo $ARGUMENT | cut -f1 -d=)
VALUE=$(echo $ARGUMENT | cut -f2 -d=)
case "$KEY" in
backup_dir) BACKUP_DIR=${VALUE} ;;
postgres_dbs) POSTGRES_DBS=${VALUE} ;;
backup_name) BACKUP_NAME=${VALUE} ;;
postgres_port) POSTGRES_PORT=${VALUE} ;;
postgres_host) POSTGRES_HOST=${VALUE} ;;
*) ;;
esac
done
And I am executing it using:
1.
/bin/bash -c /usr/bin/backup.sh postgres_dbs=grafana,keycloak backup_name=postgres-component-test-20220210.165630 backup_dir=/backups/postgres postgres_port=5432 postgres_host=postgres.default.svc.cluster.local
/usr/bin/backup.sh postgres_dbs=grafana,keycloak backup_name=postgres-component-test-20220210.165630 backup_dir=/backups/postgres postgres_port=5432 postgres_host=postgres.default.svc.cluster.local
But the output is:
set -e
[[ 0 -eq 0 ]]
echo 'No arguments passed'
No arguments passed
exit 1
Environment:
# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
Bash version where I can reproduce this issue:
GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)
However, this is not happening in the Bash version:
GNU bash, version 5.1.8(1)-release (x86_64-apple-darwin20.3.0)
CodePudding user response:
It's not a bug, just a feature!
When you use the bash -c 'code …'
style, actually the first CLI argument is passed to the inline code as $0
, not $1
.
Furthermore, if the 'code …'
itself invokes an external script such as ./script.sh
, then you should not forget to pass the arguments using the "$@"
construct.
So you could just write (as pointed out in the comments):
bash -c './script.sh "$@"' bash "first argument"
Or most succinctly just like you mention you had already tried:
bash script.sh bash "first argument"
Additional notes
As your example was not really "minimal" (it had a very long command-line), here is a complete minimal example that you might want to test for debugging purpose:
script.sh
#!/usr/bin/env bash
echo "\$#: $#"
for arg; do printf -- '- %s\n' "$arg"; done
Then you should get a session similar to:
$ chmod a x script.sh
$ bash -c ./script.sh "arg 1" "arg 2"
$#: 0
$ bash -c './script.sh "$@"' "arg 1" "arg 2"
$#: 1
- arg 2
$ bash -c './script.sh "$@"' bash "arg 1" "arg 2"
$#: 2
- arg 1
- arg 2
$ bash script.sh "arg 1" "arg 2"
$#: 2
- arg 1
- arg 2
$ ./script.sh "arg 1" "arg 2"
$#: 2
- arg 1
- arg 2
CodePudding user response:
You wrote two ways to invoke the script, which boil down to:
bash -c ./script.sh arg1 arg2 arg3
./script.sh arg1 arg2 arg3
The second way is the preferred way to invoke scripts. Running them directly tells Linux to use the interpreter listed in the shebang line. There's no reason I can see for this invocation style to drop arguments.
The first, however, does indeed lose all the arguments. It's because -c
doesn't belong there. If you want to invoke an explicit bash
shell you should write simply:
bash ./script.sh arg1 arg2 arg3
That will correctly pass all the arguments to the script.
When you add -c
it turns ./script.sh
from the name of a script into a full blown command line. What's the difference? Well, now that command line is responsible for forwarding its arguments to the script, if that's what it wants to have happen. With -c
you need to explicitly pass them on:
bash -c './script.sh "$@"' bash arg1 arg2 arg3
Yuck! It's encased in single quotes, and there's an ugly "$@"
in there. It's needed, though. Without "$@"
the arguments are simply dropped on the floor.
-c
also takes an extra argument, the value for $0
. So not only is "$@"
needed, you also have to add an extra bash
argument to set $0
. (bash
is a good choice since that's what $0
is normally set to when running a bash script.)