At that answer of mine you're referring to, one of the other points also mentions:
Or in other words, only the ones that use '...' are safe in that regard
Which doesn't include bash's declare -p
.
At the time I wrote that answer bash
's declare -p
did not use $'...'
for quoting the values of scalar variables, it did use it for array variables though. That has now changed as I can see 5.2 outputting declare -x a=$'\b'
for a scalar variable containing the BS character (see related discussion on the mailing list).
But, in any case older versions did use "..."
for quoting the value of scalar variables inside which `
and \
are special and those characters have an encoding that can be found as part of the encoding of other characters in some locales.
The output of declare -p
is intended (as some comments in the code as well as statements from the maintainer on the mailing list suggest) if not documented to be reusable but in effect that's only (if at all) in the same version of the same bash shell and in the same locale on the same system (same libc and locale definitions).
Here on Ubuntu 20.04 with bash 5.0.17:
$ a=$'\n\xa3`' bash -c 'declare -p a; echo declare -p a' | LC_ALL=zh_CN.gb18030 bash
bash: line 2: unexpected EOF while looking for matching ``'
bash: line 4: syntax error: unexpected end of file
$ a=$'\n\xa3`uname; : \xa3`' bash -c 'declare -p a; echo declare -p a' | LC_ALL=zh_CN.gb18030 bash
declare -x a="
�\\Linux\""
uname
(thankfully harmless) was run when the output of declare -p
obtained in a locale using UTF-8 as the charmap was interpreted by bash running in a locale using GB18030 as the charmap.
A number of bugs (see this or this as examples) have been fixed in the past where the quoting was not done properly, or declare -p
(or export -p
which POSIX requires to output shell code suitable for reinput) alone was including definitions of variables from the environment that could not be mapped to shell variables.
Also note that in bash, what constitutes a valid variable name depends on the locale.
$ locale charmap
UTF-8
$ LC_ALL=fr_FR locale charmap
ISO-8859-1
$ env -i $'\xe9=zzz' LC_ALL=fr_FR bash -c $'declare -p \xe9' | bash
bash: line 1: declare: `�=zzz': not a valid identifier
Byte 0xe9 is é in ISO-8859-1 which is a single-byte [[:alpha:]]
so is allowed in variable names, while in UTF-8, it's not even forming a valid character.
Also beware of:
$ bash -c 'a=1; f() { local b=2; declare -p a b; }; f'
declare -- a="1"
declare -- b="2"
The fact that one is global, one is local is not reflected in declare
's output, and if both were used inside a function, the resulting variable would end up being local to the function.
bash's declare
is obviously shaped after ksh's typeset
(bash also has a typeset
alias to it). In ksh86 and earlier, typeset -p
was to print the typeset
output if any to the co-process (aka two-way pipe). It seems it disappeared in ksh88. In ksh93, typeset -p
reappeared to print variable definitions.
Current versions of the ksh93 manual have:
-p
The name, attributes and values for the given vnames are written on standard output in a form that can be used as shell
input. If +p
is specified, then the values are not displayed.
But that verbiage only appeared in ksh93t in 2008.
-p
was added to bash's declare
in 2.0 released in 1996
From the NEWS files from that version:
kk. The `declare' builtin has new options: -a, -F, -p.
(-F
incompatible with ksh93's)
And the CWRU/changelog
:
3/24
builtins/declare.def
- new -p option to display variables and their values and attributes
declare -p xxx
displays attribs and value of var xxx
Which dates the actual implementation on 1995-03-24, so after ksh93's but before ksh93 documenting it producing reusable output.
declare -p varname
will output a bash statement that defines the variablevarname
with its current value. Writing this to a file gives you a file that you can source to recreate the variable." :) In fact, that's exactly what I use it for right now in a few places, and I'd like to know whether that is indeed a supported use-case. – muru Feb 15 '24 at 10:33declare -p
on a read-only variable (likeUID
orPPID
), and then try to evaluate that output (with the variable still in scope), you'll probably get an error saying you can't set the variable's value as it's read-only. Stéphane points out that you will have to ensure that the locale andbash
revision is not changed between thedeclare -p
call and when you evaluate the result. – Kusalananda Feb 15 '24 at 10:50Stéphane=1
as an example of a variable definition that is interpreted differently based on the locale (and also of course other characters in the variable value itself might have different meanings in different locales) – muru Feb 15 '24 at 11:30