passing parameters and return values in shell - shell

I have written a function named 'connectTo' which takes paramaters named 'options' and it should return some string by echoing before return.
connectTo ${options}
this works i.e arguments get passed in this but when i write
str=$(connectTo ${options})
then connectTo is working as if no arguments were passed>
I am new to shell scripting and obviously doing something wrong but what?
(remember i need a string to be returned from fuction which cannot be a global variable)
function connectTo(){
local flag=false
local str=""
for i in $#; do
if [ "$flag" = true ]; then
str=$i
flag=false
elif [[ "$i" = "--foo" || "$i" = "-f" ]]; then
flag=true
fi
echo "$i"
done;
if [ "$str" = "" ]; then
echo ""
return 0
fi
echo "found"
return 0
}
In case of connectTo ${options} the arguments get printed while in second case they don't

you should not use '$' sign while assigning a variable. so it should be
str =connectTo [value_of_argument]
$ is used to access the value of variable.

Related

Bash - check if argument is string

I am attempting to check if an argument is an array with the following code:
if [[ $(declare -p $1) ]] != *-a*;
Here $1 is a string with the value "123". I get the following error message from bash:
`arrays.bash: line 23: declare: 123: not found
This code works if I pass an array as an argument but not a string. I want to verify that the argument is either an array or an associative array. I have no concern with the contents at this point, I only want the type. Any ideas on how to do this?
After all, why worry about the types, if you are relying on it perhaps your approach is wrong or you may need a strong-typed language
% v=1
% declare -p v
declare -- v="1"
% echo $v
1
% echo ${v[#]}
1
% v[1]=2
% declare -p v
declare -a v=([0]="1" [1]="2")
% echo ${v[#]}
1 2
The Error In The Question
You called yourfunction "$a" instead of yourfunction a, when a=123. Don't do that: You need to pass the name of the variable, not its value.
General Solution: Bash 5.x+
Bash 5 has a new feature called parameter transformation, whereby ${parameter#operator} can perform a variety of actions; one of these is checking the type of the parameter.
myfunc() {
[[ -v "$1" ]] || { echo "No variable named $1 exists" >&2; return 1; }
case ${!1#a} in
*a*) echo "Array";;
*A*) echo "Associative array";;
*i*) echo "Integer";;
"") echo "Default string";;
*) echo "Other/unknown flag set: ${!1#a}";;
esac
}
Older Solution
myfunc() {
local typedesc
typedesc=$(declare -p "$1" 2>/dev/null) || {
echo "No variable named $1 is set" >&2
return 1
}
case $typedesc in
"declare -a"*) echo "Array";;
"declare -A"*) echo "Associative array";;
"declare -i"*) echo "Integer";;
"declare --"*) echo "Regular (default) string variable";;
*) echo "Other/unrecognized type";;
esac
}

How to tokenise string and call a function on each token in bash?

I have a text file with comma delimiter like following
for example str_data.txt
aaa,111,bbb
ccc,222,ddd
eee,333,fff
I have a bash function to validate each token (i.e. if each token is following some rule or not based on that function will echo true or false. (can leave it like [[ XYZ == "$1"]] also, instead of returning echo) )
for example
function validate_token {
local _rule = XYZ
if [[ XYZ == "$1" ]]; then
echo "true"
else
echo "false"
fi
}
I want to write a bash script (one-liner or multi-line) to validate all these tokens separately (i.e. validate_token "aaa" then validate_token "111") and finally answer "true" or "false" based on ANDing of each token's results.
Would yo please try the following:
validate_token() {
local rule="???" # matches a three-chraracter string
if [[ $1 == $rule ]]; then
echo 1
else
echo 0
fi
}
final=1 # final result
while IFS=',' read -ra ary; do
for i in "${ary[#]}"; do
final=$(( final & $(validate_token "$i") ))
# take AND with the individual test result
done
done < "str_data.txt"
(( $final )) && echo "true" || echo "false"
I've also modified your function due to several reasons.
When defining a bash function, the form name() { .. } is preferred.
It is not recommended to start the user's variable name with an underscore.
You have localized it and don't have to care about the variable name
collision.
When evaluating the conditional expression by using == or = operator
within [[ .. ]], it will be better to place the pattern or rule to the right of the
operator.
It will be convenient to return 1 or 0 rather than true or false for further calculation.
Hope this helps.
You can try the below, reading line by line and storing the values into an array, then iterating the array calling the function for each value :
IFS=","
while read line
do
read -ra lineValues <<< "$line"
for value in "${lineValues[#]}"
do
validate_token "$value"
done
done < your_txt_file

Bash function to check for unset and blank variables

I'm writing a bash function to check for blank or unset variables.
function exit_if_var_does_not_exist {
TMP_NAME=$1
TMP_VALUE=`echo $TMP_NAME`
if [ -z ${TMP_VALUE+x} ]; then
echo "Variable [${TMP_NAME}] is unset. Exiting."
exit 1
else
if [ ${TMP_VALUE} = "" ]; then
echo "Variable [${TMP_NAME}] is set to ''. Exiting."
exit 1
fi
fi
echo "Variable [${TMP_NAME}] is set to ${TMP_VALUE}"
}
VAR=Test
exit_if_var_does_not_exist VAR
BLANK=
exit_if_var_does_not_exist BLANK
This does not give me the expected output in TMP_VALUE. Can someone please help me with what I'm missing here?
-bash-3.2$ ./x.sh
Variable [VAR] is set to VAR
Variable [BLANK] is set to BLANK
The problem with the empty test block is that at no point in this snippet do you ever actually get the value of the originally named variable.
When you use TMP_NAME=$1 you assign the name of the input variable to TMP_NAME and then
TMP_VALUE=`echo $TMP_NAME`
just assigns that name to TMP_VALUE. Effectively you just ran TMP_VALUE=$1.
So you aren't testing for whether the originally named variable has contents anywhere here.
To do that you need to use indirect expansion.
Namely TMP_VALUE=${!TMP_NAME} or TMP_VALUE=${!1}.
Side comment your "unset" test block at the top can never trigger.
TMP_VALUE can never be unset because you assign to it. Even TMP_VALUE= marks the variable as "set". So that bit is useless. Though, and thank David C. Rankin for trying long enough to make me think of this, you can use an indirect expansion trick to make this work.
[ -z "${!TMP_NAME+x}" ] will return true for any set variable named in TMP_NAME and false for an unset variable.
That all said if what you want to do is error if a variable is unset or blank the shell has you covered. Just use
: "${VAR:?Error VAR is unset or blank.}" || exit 1
Finally, as David C. Rankin points out inside [ you need to quote expansions that might disappear when they change the meaning of tests (you see this in the -z test above) as well as here.
So [ ${TMP_VALUE} = "" ] needs to be [ "${TMP_VALUE}" = "" ] because is TMP_VALUE is empty the first version would become a syntax error because [ = "" ] isn't a valid invocation of test/[.
I'm not sure this will work for all cases, but try:
#!/bin/bash
function exit_if_var_does_not_exist {
local TMP_NAME=$1
local TMP_VALUE="${!TMP_NAME}"
[ -z "${!TMP_NAME}" ] && {
echo "Variable [${TMP_NAME}] is unset. Exiting."
return 1
}
[ "$TMP_VALUE" = "" ] && {
echo "Variable [${TMP_NAME}] is set to ''. Exiting."
return 1
}
echo "Variable [${TMP_NAME}] is set to ${TMP_VALUE}"
}
VAR=Test
exit_if_var_does_not_exist VAR
# BLANK=
exit_if_var_does_not_exist BLANK
Output
$ bash tstempty.sh
Variable [VAR] is set to Test
Variable [BLANK] is unset. Exiting.
Improved Indirection Soup
After a bit of discussion in the comments and a suggestion, I think I have a version that works more consistently. Again, I qualify this with I am not sure it will work in all situations, but for the testing it seems to:
#!/bin/bash
function exit_if_var_does_not_exist {
local TMP_NAME=$1
local TMP_VALUE="${!TMP_NAME}"
if ! declare -p $1 >/dev/null 2>&1 ; then
[ -z "${!TMP_NAME}" ] && {
echo "Variable [${TMP_NAME}] is unset. Exiting."
return 1
}
elif [ "$TMP_VALUE" = "" ]; then
echo "Variable [${TMP_NAME}] is set to ''. Exiting."
return 1
fi
echo "Variable [${TMP_NAME}] is set to ${TMP_VALUE}"
}
VAR=Test
exit_if_var_does_not_exist VAR
# BLANK=
exit_if_var_does_not_exist BLANK
EMPTY=""
exit_if_var_does_not_exist EMPTY
Output
$ bash tstempty.sh
Variable [VAR] is set to Test
Variable [BLANK] is unset. Exiting.
Variable [EMPTY] is set to ''. Exiting.
Another way:
exit_if_var_does_not_exist() {
if ! (set -u; : "${!1}") >/dev/null 2>&1; then
printf 'Variable [%s] is unset. Exiting.\n' "$1"
exit 1
elif [ -z "${!1}" ]; then
printf "Variable [%s] is set to ''. Exiting.\n" "$1"
exit 1
fi
printf 'Variable [%s] is set to %s\n' "$1" "${!1}"
}
If you don't need to separate the two error messages:
exit_if_var_does_not_exist() {
: "${!1:?$1 is unset or blank}"
printf 'Variable [%s] is set to %s\n' "$1" "${!1}"
}

How to echo (print) the value of a variable in Bash?

I am trying to write a little script, and I can not figure out how to choose the variable to be echo'ed (echo $'TEST'_"$response") dynamically depending on the user's input:
#!/usr/bin/env sh
response=response
TEST_1="Hi from 1!"
TEST_2="Hi from 2!"
while [ $response ]; do
read -p "Enter a choice between 1 - 2, or 'bye': " response
if [ $response = 'bye' ]; then
echo "See You !"; exit
elif [ $response -ge 1 ] && [ $response -le 2 ]; then
echo $'TEST'_"$response"
else
echo "Input is not a valid value."
fi
done
The desired output would be the value of one of the variables declared at the beginning of my script ("Hi from 1!" or "Hi from 2!"). Instead my script simple outputs the name of the variable as a string "TEST_1" or "TEST_2". I do not simply want to hardcode the variable that will be printed like:
if [ $response -ge 1 ]; then
echo $TEST_1
fi
since it is not scalable. Using backticks like
echo `$'TEST'_"$response"`
doesn't help either since bash will expect to run the result "TEST_1" or "TEST_2" as a command.
Any hint will be greatly appreciated.
You need indirect expansion, to be used with ${!var}:
$ TEST1="hello"
$ TEST2="bye"
$ v=1
$ var="TEST$v" #prepare the variable name of variable
$ echo ${!var} #interpret it
hello
$ v=2
$ var="TEST$v" #the same with v=2
$ echo ${!var}
bye
That is, you need to use a variable name of a variable and this is done with the indirect expansion: you use a variable with the name of the variable and then you evaluate it with the ${!var} syntax.
In your case, use:
myvar="TEST$response"
echo "${!myvar}"
Always use quotes, such as "$string", for anything other than numbers. For numbers, just keep it normal (i.e. $number).

Why is this Bash script not changing the string's value?

I can't understand why userType is not changing.
I know for certain it's successfully reaching determineType, but it isn't changing the value to "bbb" when I try to print out userType later.
userType="aaa"
function determineType {
userType="bbb"
}
function checkUser {
cat users.csv | \
while read userLine; do
if [[ $userLine =~ .*$user.* ]]
then
determineType
echo "1"
fi
done
echo "0"
}
As soulseekah said in a comment, your while loop is executed in a subshell. Instead, do (and, as a benefit, you get rid of the useless use of cat):
userType="aaa"
determineType() {
userType="bbb"
}
checkUser() {
while read userLine; do
if [[ $userLine = *$user* ]]; then
determineType
return 1
fi
done < users.csv
return 0
}
Note. I also changed a few things:
got rid of the useless regexp since the same can be achieved with globbing,
used more common ways of defining functions in bash,
used return instead of echo for returning values: you'd run into the same problem again with an echo: you'd probably use your function checkUser in another subshell to obtain the value returned by the echo.
You are using a pipe, which launch the while .. do in a subshell.
Changing the value of a variable in a subshell won't affect the original variable
You should replace the:
function checkUser {
cat users.csv | \
while read userLine; do
if [[ $userLine =~ .*$user.* ]]
then
determineType
echo "1"
fi
done
echo "0"
}
with
function checkUser {
while read userLine; do
if [[ $userLine =~ .*$user.* ]]
then
determineType
echo "1"
fi
done < users.csv
echo "0"
}
(This also get rid of a Useless Use Of Cat)

Resources