Reputation: 4496
I like to organize my code with functions, as I am using function I like to use local
variables also. As much as possible I use read-only
variables, so if I make any mistake inside my code it will be easily noticed.
But I have been using it wrong for a long time. So I decided to write it to share my experience.
How to define local
AND read-only
inside bash function? (See the answer and explanation below).
Upvotes: 43
Views: 17704
Reputation: 11
As far as I can see, local readonly variables in bash are not so local:
#!/bin/bash
declare -r TESTVAR="global"
testlocalvar()
{
declare -r TESTVAR="local"
echo "${TESTVAR}"
}
echo "${TESTVAR}"
testlocalvar
echo "${TESTVAR}"
Output is:
+ ./test.sh
global
./test.sh: line 7: declare: TESTVAR: readonly variable
global
global
So, Is it possible to declare really local readonly variable in bash function with same name as in global context, readonly one?
This code works as expected:
#!/bin/bash
declare TESTVAR="global"
testlocalvar()
{
declare -r TESTVAR="local"
echo "${TESTVAR}"
}
echo "${TESTVAR}"
testlocalvar
echo "${TESTVAR}"
Output is:
+ ./test.sh
global
local
global
There is a shell...
Is there a way?
Upvotes: 0
Reputation: 4496
First attempt: local readonly var1
That is the way I used to define it. It is wrong. I will define my variable var1
as local
, but it will not be readonly
, as you can see on example below, I can change the value of var1
, and I don't want that!
:~$ (
> myfunction()
> {
> # Define variable
> local readonly var1="val1"
>
> echo "Readonly output:"
> readonly | grep -E 'readonly|local|var1'
> echo ""
>
> echo "Local output:"
> local | grep -E 'readonly|local|var1'
> echo ""
>
> var1="val2"
> echo "VAR1 INSIDE: ${var1}"
> }
> myfunction
> echo "VAR1 OUTSIDE: ${var1}"
> )
Readonly output:
Local output:
var1=val1
VAR1 INSIDE: val2
VAR1 OUTSIDE:
Second attempt: readonly local var1
This time it will define var1
as readonly
, but it will also define a variable called local
, so using this way it will not handle local
as keyword, it will be a variable name.
Check also that the scope of var1
is not local
, in fact it is global
, we can see the value of var1
outside the function.
:~$ (
> myfunction()
> {
> # Define variable
> readonly local var1="val1"
>
> echo "Readonly output:"
> readonly | grep -E 'readonly|local|var1'
> echo ""
>
> echo "Local output:"
> local | grep -E 'readonly|local|var1'
> echo ""
>
> echo "VAR1 INSIDE: ${var1}"
> }
> myfunction
> echo "VAR1 OUTSIDE: ${var1}"
> )
Readonly output:
declare -r local
declare -r var1="val1"
Local output:
VAR1 INSIDE: val1
VAR1 OUTSIDE: val1
As it should be: local -r var1
This way it will do exactly what I want, it will define var1
as scope local
AND readonly
.
:~$ (
> myfunction()
> {
> # Define variable
> local -r var1="val1"
>
> echo "Readonly output:"
> readonly | grep -E 'readonly|local|var1'
> echo ""
>
> echo "Local output:"
> local | grep -E 'readonly|local|var1'
> echo ""
>
> #var1="val2"
> echo "VAR1 INSIDE: ${var1}"
> }
> myfunction
> echo "VAR1 OUTSIDE: ${var1}"
> )
Readonly output:
declare -r var1="val1"
Local output:
var1=val1
VAR1 INSIDE: val1
VAR1 OUTSIDE:
We can define it as below also, but one line is better than two!
local var1="val1"
readonly var1
Upvotes: 62
Reputation: 46886
The bash man page summarizes things thusly for the declare
command:
-r Make names readonly. These names cannot then be assigned values
by subsequent assignment statements or unset.
and
When used in a function, declare and typeset make each name local, as with
the local command, unless the -g option is supplied.
So if you declare
within a function, the variable you declare will be local by default. And if you use the -r
option, it will be read-only.
$ cat testv
#!/usr/bin/env bash
test1() {
declare -r var="$1"
var="bacon"
}
s=foo
test1 bar
echo "$s - $var"
$ bash testv
testv: line 5: var: readonly variable
foo -
Upvotes: 17