5

To test if a variable is read-only, there are the following ugly hacks:

# True if readonly
readonly -p | egrep "declare -[:lower:]+ ${var}="

# False if readonly
temp="$var"; eval $var=x 2>/dev/null && eval $var=\$temp

Is there a more elegant solution?

Bobby
  • 11,419
  • 5
  • 44
  • 69
l0b0
  • 55,365
  • 30
  • 138
  • 223
  • Why do you believe this to be important? – Ignacio Vazquez-Abrams Dec 14 '10 at 15:37
  • Ask for something does not make it important. Elsewhere elegant is not associate with important. – enguerran Dec 14 '10 at 15:41
  • It's only important insofar as you care about the readability of your scripts. For example, being able to use either `[ -R varname ]` or `readonly -t varname` (`t` for test) to test for this would be elegant. – l0b0 Dec 14 '10 at 15:52

2 Answers2

10

Using a subshell seems to work. Both with local and exported variables.

$ foo=123
$ bar=456

$ readonly foo

$ echo $foo $bar
123 456

$ (unset foo 2> /dev/null) || echo "Read only"
Read only

$ (unset bar 2> /dev/null) || echo "Read only"
$

$ echo $foo $bar
123 456           # Still intact :-)

The important thing is that even is that the subshell salvages your RW ($bar in this case) from being unset in your current shell.

Tested with bash and ksh.

plundra
  • 18,542
  • 3
  • 33
  • 27
4

You can also add an empty string to the variable, which still leaves its value alone, but is faster than using a subshell, e.g.:

foo+= 2>/dev/null || echo "Read only"

Captured as a function, it'd be:

is-writable() { eval "$1+=" >2/dev/null; }
PJ Eby
  • 8,760
  • 5
  • 23
  • 19
  • If `foo` is unset, this test will set it to the null string, so you need to test for that, too. – Logolept Jul 19 '20 at 03:27
  • In modern bash, if `foo` is unset and also has the nameref attribute, the test will fail even if `foo` isn’t readonly because the null string isn’t a valid variable name. – Logolept Jul 19 '20 at 03:28
  • On the other hand, bash will help you avoid the danger of someone accidentally or deliberately writing something like “`is-writable 'rm -rf / #'`” (**Do not run that!**): use a local nameref variable in your function instead of the eval. Of course, if you happen to use a local varable that was previously set readonly, it’ll break your function. – Logolept Jul 19 '20 at 03:29