How to do named command line arguments in Bash Scripting better way? - bash

This is my sample Bash Script example.sh:
#!/bin/bash
# Reading arguments and mapping to respective variables
while [ $# -gt 0 ]; do
if [[ $1 == *"--"* ]]; then
v="${1/--/}"
declare $v
fi
shift
done
# Printing command line arguments through the mapped variables
echo ${arg1}
echo ${arg2}
Now if in terminal I run the bash script as follows:
$ bash ./example.sh "--arg1=value1" "--arg2=value2"
I get the correct output like:
value1
value2
Perfect! Meaning I was able to use the values passed to the arguments --arg1 and --arg2 using the variables ${arg1} and ${arg2} inside the bash script.
I am happy with this solution for now as it serves my purpose, but, anyone can suggest any better solution to use named command line arguments in bash scripts?

You can just use environment variables:
#!/bin/bash
echo "$arg1"
echo "$arg2"
No parsing needed. From the command line:
$ arg1=foo arg2=bar ./example.sh
foo
bar
There's even a shell option to let you put the assignments anywhere, not just before the command:
$ set -k
$ ./example.sh arg1=hello arg2=world
hello
world

Related

How to echo script invocation without variable expansion of its args

From within a bash script, I'd like to echo script invocation without expanding variables passed as arguments.
Echoing script invocation with expanded variables can be achieved with
echo "${BASH_SOURCE[0]} ${*}"
Echoing (the script's, or any other comand's) history using
echo "$(tail -n 1 ~/.bash_history)"
shows script invocation without variable expansions, as desired, however not for the running script (only for scripts completed).
How to echo script invocation without variable expansion of its arguments from within the running script?
If you can execute your script with bash -c script args, what you want is doable with the BASH_EXECUTION_STRING variable:
$ bash -c 'echo "$BASH_EXECUTION_STRING"'
echo "$BASH_EXECUTION_STRING"
This output is not easy to understand but you can see that the echo command, when executed, prints the unexpanded command. This is because the value of the BASH_EXECUTION_STRING variable is the literal: echo "$BASH_EXECUTION_STRING".
So, if your script is, for instance:
#!/usr/bin/env bash
script="$0"
cmd="$1"
shift
echo "script name: $script"
echo "command line: $cmd"
echo "parameter: $1"
you can execute it as:
$ a=42 bash -c './foo.sh "$BASH_EXECUTION_STRING" "$a"'
script name: ./foo.sh
command line: ./foo.sh "$BASH_EXECUTION_STRING" "$a"
parameter: 42

Can I put a breakpoint in shell script?

Is there a way to suspend the execution of the shell script to inspect the state of the environment or execute random commands?
alias combined with eval gives you basic functionality of breakpoints in calling context:
#!/bin/bash
shopt -s expand_aliases
alias breakpoint='
while read -p"Debugging(Ctrl-d to exit)> " debugging_line
do
eval "$debugging_line"
done'
f(){
local var=1
breakpoint
echo $'\n'"After breakpoint, var=$var"
}
f
At the breakpoint, you can input
echo $var
followed by
var=2
then Ctrl-d to exit from breakpoint.
Due to eval in the while loop, use with caution.
Bash or shell scripts do not have such debugging capabilities as other programming languages like Java, Python, etc.
We can put the echo "VAR_NAME=$VAR_NAME" command in the code where we want to log the variable value.
Also, a little bit more flexible solution is to put this code somewhere at the beginning in the shell script we want to debug:
function BREAKPOINT() {
BREAKPOINT_NAME=$1
echo "Enter breakpoint $BREAKPOINT_NAME"
set +e
/bin/bash
BREAKPOINT_EXIT_CODE=$?
set -e
if [[ $BREAKPOINT_EXIT_CODE -eq 0 ]]; then
echo "Continue after breakpoint $BREAKPOINT_NAME"
else
echo "Terminate after breakpoint $BREAKPOINT_NAME"
exit $BREAKPOINT_EXIT_CODE
fi
}
export -f BREAKPOINT
and then later, at the line of code where we need to break we invoke this function like this:
# some shell script here
BREAKPOINT MyBreakPoint
# and some other shell script here
So then the BREAKPOINT function will log some output then launch /bin/bash where we can run any echo or some other shell command we want. When we want to continue running the rest of the shell script (release breakpoint) we just need to execute exit command. If we need to terminate script execution we would run exit 1 command.
There exist solutions like bash-debug.
A poor-man's solution which works for me is the interactive shell.
By adding three lines of code, you can introspect and alter variables as follows:
Let's assume, that you have the script test.bash
A=FOO
export B=BAR
echo $A
echo $B
$ test.bash
FOO
BAR
If you add an interactive shell at line 3, you can look around and inspect variables which have been exported before:
A=FOO
export B=BAR
bash -c "$SHELL"
echo $A
echo $B
$ test.bash
$ echo $A
$ echo $B
BAR
$ exit
FOO
BAR
If you want to see all variables in your interactive shell, you have to add set -a to the preamble of your script, such that all variables and functions are exported:
set -a
A=FOO
export B=BAR
bash -c "$SHELL"
echo $A
echo $B
$ test.bash
$ echo $A
FOO
$ echo $B
BAR
$ exit
FOO
BAR
Note, that you cannot change the variables in your interactive shell. The only solution for me is to source an additional script of variables, which will be sourced rightafter the interactive shell
set -a
A=FOO
export B=BAR
bash -c "$SHELL"
source /tmp/var
echo $A
echo $B
$ test.bash
$ echo "export A=alice" > /tmp/var
$ echo "export B=bob" >> /tmp/var
$ exit
alice
bob

Including $# to pass on all command line arguments when a shell script invokes itself with bash -c

I need a bash script to invoke itself (actually in a different context, inside Docker container) and I'm using a bash -c command to do so. However, I'm struggling with how to pass on all command line variables, even after reading lots of related questions here. This is an example script:
#!/bin/bash
# If not in the right context, invoke script in right context and exit
if [ -z ${NESTED+x} ]; then
NESTED=true bash -c "./test.sh $#"
exit
fi
echo "$1"
echo "$2"
echo "$3"
If I save this as test.sh and call it with ./test.sh 1 2 "3 4" I'd want to see those arguments echo'ed, but only the first one is output.
If I use set -x it shows bash inserts some unexpected quoting so the call becomes NESTED=true bash -c './test2.sh 1' 2 3 4. That explains the output but I haven't been able to figure out the right way to do this.
bash -c should not be used as it cannot handle "3 4" easily:
#!/bin/bash
# If not in the right context, invoke script in right context and exit
if [ -z ${NESTED+x} ]; then
NESTED=true ./test.sh "$#"
exit
fi
echo "$1"
echo "$2"
echo "$3"

Why it runs different for source and sh calling in shell

My shell a.sh script like this:
#!/bin/sh
# $ret maybe from database or pipe,whatever it likes:
ret="cnt
1"
echo -e $ret
and calling in different ways produces different results:
$ sh a.sh
cnt 1
$ source a.sh
cnt
1
$
How can I get the same output under sh and source?
How can I get the same output under sh and source?
you need to quote echo. – fedorqui
thanks #fedorqui. that means echo -e "$ret" – tonylee0329
Exactly, quoting echo's argument is the way to adjust the difference between the two shells' echos.

Bash script not running in Ubuntu

I'm getting started with bash scripting and made this little script following along a short guide but for some reason when I run the script with sh myscript I get
myscript: 5: myscript: 0: not found running on ubuntu 12.04
here is my script below I should at least see the echo message if no args are set:
#!/bin/bash
#will do something
name=$1
username=$2
if (( $# == 0 ))
then
echo "##############################"
echo "myscript [arg1] [arg2]"
echo "arg1 is your name"
echo "and arg2 is your username"
fi
var1="Your name is ${name} and your username is ${username}"
`echo ${var1} > yourname.txt`
`echo ${var1} > yourname.txt`
Get rid of the backticks.
echo ${var1} > yourname.txt
...for some reason when I run the script with sh myscript...
Don't run it that way. Make the script executable and run it directly
chmod +x myscript
./script
(or run with bash myscript explicitly).
It looks like that expression will work in bash but not in sh. As others pointed out change it to executable, make sure your shebang line is using bash and run it like this:
./myscript
If you want to run it with sh then it is complaining about line 5. Change it to this and it will work in /bin/sh.
if [ $# -ne 0 ]
Check out the man page for test.
Also you don't need the backticks on this line:
echo ${var1} > yourname.txt

Resources