Bash reference variable by string name - bash

I'm trying to write a simple function that checks whether a variables is declared and non empty or not.
my_var="dummy_val"
function validate_var(){
${1:?"Variable is not set"}
}
validate_var my_var
validate_var my_var2
I get the following error:
script: line n: my_var: command not found
Is there a simple way I could reference the variable from the function argument?

This seems to work. It uses the ! variable indirection.
function validate_var(){
: ${!1:?"$1 is not set"}
}

Pass the variable by prepending a $. Also I've added an echo (as an example), otherwise your validate_var function tries to execute it's argument:
my_var="dummy_val"
function validate_var(){
echo ${1:?"Variable is not set"}
}
validate_var $my_var

Related

Setting environment variables with .sh script not working under zsh : how to convert a function [duplicate]

I've noticed that it's usual task to check global variables like PATH, GOPATH and etc. That's why I want to write small function so instead of typing a lot of letters
echo $PATH
I can type just
e PATH
The function itself should be very simple
function e() {
echo $($1) # it produces the error "command not found"
}
But the problem is how to substitute a variable to get the content of PATH?
P.S. I'm using zsh
The traditional (POSIX) notation to handle this uses the eval command, which many will warn you against:
e() {
eval echo \"\$$1\"
}
In bash, however, you can use variable indirection:
function e() {
printf '%s\n' "${!1}"
}
And in zsh, which you added as a tag after my initial answer, indirection is handled differently:
function e() {
printf '%s\n' "${(P)1}"
}
This uses a Parameter Expansion Flag which you can read about in man zshexpn.
P This forces the value of the parameter name to be interpreted as a
further parameter name, whose value will be used where appropriate.

how to call a bash function providing environment variables stored in a Bash array?

I got two variables in a bash script. One contains the name of a function within the script while the other one is an array containing KEY=VALUE or KEY='VALUE WITH SPACES' pairs. They are the result of parsing a specific file, and I can't change this.
What I want to do is to invoke the function whose name I got. This is quite simple:
# get the value for the function
myfunc="some_function"
# invoke the function whose name is stored in $myfunc
$myfunc
Consider the function foo be defined as
function foo
{
echo "MYVAR: $MYVAR"
echo "MYVAR2: $MYVAR2"
}
If I get the variables
funcname="foo"
declare -a funcenv=(MYVAR=test "MYVAR2='test2 test3'")
How would I use them to call foo with the pairs of funcenv being added to the environment? A (non-variable) invocation would look like
MYVAR=test MYVAR2='tes2 test3' foo
I tried to script it like
"${funcenv[#]}" "$funcname"
But this leads to an error (MYVAR=test: command not found).
How do I properly call the function with the arguments of the array put in its environment (I do not want to export them, they should just be available for the invoked function)?
You can do like this:
declare -a funcenv=(MYVAR=test "MYVAR2='test2 test3'")
for pairs in "${funcenv[#]}"; do
eval "$pairs"
done
"$funcname"
Note however that the variables will be visible outside the function too.
If you want to avoid that, then you can wrap all the above in a (...) subshell.
why don't you pass them as arguments to your function?
function f() { echo "first: $1"; echo "second: $2"; }
fn=f; $fn oneword "two words"

assign local variable from function in linux bash a new value

I have a linux bash script with a function:
myfunctiona ()
{
local MYVAR1="one"
local MYVAR2="two"
echo $MYVAR1
# The line beneath is the line in question!
local MYVAR1=$MYVAR1$MYVAR2
}
When I want to give the LOCAL variable MYVAR1 in the function myfunctiona a new value, do I have to write
local MYVAR1=$MYVAR1$MYVAR2
or can I also write
MYVAR1=$MYVAR1$MYVAR2
With the second line without "local" do I create a global variable with the same name?
Once you've defined a local variable you can assign it normally, like this:
#!/bin/bash
myfunctiona ()
{
local MYVAR1="one"
local MYVAR2="two"
echo $MYVAR1
# The line beneath is the line in question!
local MYVAR1=$MYVAR1$MYVAR2
MYVAR1="FOO"
echo $MYVAR1
}
myfunctiona
echo "global" $MYVAR1
which gives the output:
one
FOO
global
As you can see attempting to access the variable from global scope returns null
HTH
The correct way to do it would be:
MYVAR1="${MYVAR1}${MYVAR2}"
The braces are usually used when you concatenate variables. Use quotes.
The variable is still local since you reassigned its value within the scope of the function.
An example:
#!/usr/bin/env bash
_myFunction()
{
local var_1="one"
local var_2="two"
local -g var_3="three" # The -g switch makes a local variable a global variable
var_4="four" # This will be global since we didn't mark it as a local variable from the start
var_1="${var_1}${var_2}"
echo "Inside function var_1=${var_1}"
echo "Inside function var_2=${var_2}"
echo "Inside function var_3=${var_3}"
echo "Inside function var_4=${var_4}"
}
_myFunction
echo "Outside function var_1=${var_1}"
echo "Outside function var_2=${var_2}"
echo "Outside function var_3=${var_3}"
echo "Outside function var_4=${var_4}"
This results in:
$ ./script
Inside function var_1=onetwo
Inside function var_2=two
Inside function var_3=three
Inside function var_4=four
Outside function var_1=
Outside function var_2=
Outside function var_3=three
Outside function var_4=four
You can give this way, but as Ube said for concatenation you need to give like that -
MYVAR1="$MYVAR1$MYVAR2";
Even this works for concatenation

Why doesn't the shell require formal arguments in the function signature?

Why does a shell not need parameters for functions?
Example of addition function below that adds num1 and num2.
I mean that you don't write the parameters inside the () of the line function addition().
addition()
{
echo $(($num1+$num2))
}
If your question is why does this function work, how does it get the num1 and num2 variables?", the answer is: it gets those variables from the parent context, for example this will echo hello Jack:
hello() {
echo hello $name
}
name=Jack
hello
You can rewrite the function to use positional arguments like this:
hello() {
echo hello $1
}
hello Jack
As per why not write variable names in the function declaration: that's just the way bash is made. From the man page:
Shell Function Definitions
A shell function is an object that is called like a simple command and
executes a compound command with a new set of positional parameters.
Shell functions are declared as follows:
name () compound-command [redirection]
function name [()] compound-command [redirection]
This defines a function named name. The reserved word function
is optional. If the function reserved word is supplied, the
parentheses are optional. The body of the function is the com‐
pound command compound-command (see Compound Commands above).
....
That is, the function declaration must be in one of the explained forms, with () (no variable names in between) mandatory when not using the function keyword, and optional otherwise.
From the manpage:
When a function is executed, the arguments to the function become the positional parameters during its execution. The special parameter # is updated to reflect the change. Special parameter 0 is unchanged.
In CS terms, bash functions don't use formal parameters because the positional parameters are always set when (and only when) you apply the function:
$ ##
$ # Show the function arguments
$ showParams() {
> printf '%s\n' "$#"
$ }
$ showParams 1 2 3
1
2
3
$ set -- 1 2 3
$ printf '%s\n' "$#"
1
2
3
$ showParams # Here you can see the parameters in the shell are not set in the function application:
$
…but this also means bash does not support keyword arguments.
You may also wish to read the section under Positional Parameters in the manpage.
Shell functions don't need prototypes because
All variables are string variables. They get converted to numbers as needed e.g. when doing arithmetic. (BTW, declaring variables as integers is a shell extension not found in POSIX).
The number of parameters passed is known when the function is called and available as $# so the function body can deal with variadic functions.

Create variable from string/nameonly parameter to extract data in bash?

I want to save the variable name and its contents easily from my script.
Currently :-
LOGFILE=/root/log.txt
TEST=/file/path
echo "TEST : ${TEST}" >> ${LOGFILE}
Desired :-
LOGFILE=/root/log.txt
function save()
{
echo "$1 : $1" >> ${LOGFILE}
}
TEST=/file/path
save TEST
Obviously the above save function just saves TEST : TEST
Want I want it to save is TEST : /file/path
Can this be done? How? Many thanks in advance!
You want to use Variable Indirection. Also, don't use the function keyword, it is not POSIX and also not necessary as long as you have () at the end of your function name.
LOGFILE=/root/log.txt
save()
{
echo "$1 : ${!1}" >> ${LOGFILE}
}
TEST=/file/path
save TEST
Proof of Concept
$ TEST=foo; save(){ echo "$1 : ${!1}"; }; save TEST
TEST : foo
Yes, using indirect expansion:
echo "$1 : ${!1}"
Quoting from Bash reference manual:
The basic form of parameter expansion is ${parameter} [...] If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion
Consider using the printenv function. It does exactly what it says on the tin, prints your environment. It can also take parameters
$ printenv
SSH_AGENT_PID=2068
TERM=xterm
SHELL=/bin/bash
LANG=en_US.UTF-8
HISTCONTROL=ignoreboth
...etc
You could do printenv and then grep for any vars you know you have defined and be done in two lines, such as:
$printenv | grep "VARNAME1\|VARNAME2"
VARNAME1=foo
VARNAME2=bar

Resources