Azize
Azize

Reputation: 4496

Bash local AND readonly variable

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

Answers (3)

X bl lJJ H i K
X bl lJJ H i K

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

Azize
Azize

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

ghoti
ghoti

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

Related Questions