76

I am not able to use - in variables in shell. Is there a way to be able to use it, because I have one script which depends on such named variables:

$export a-b=c
-bash: export: `a-b=c': not a valid identifier

$export a_b=c

First throws the given error and second works fine.

3
  • 2
    possible cross site duplicate of: stackoverflow.com/questions/2821043/… Aug 21, 2014 at 20:07
  • Shells usually don't allow such variable names. You'd have to bypass the shell, perhaps even with a custom C program loading the variables into your comand's environment. Can't you fix this misfeature (even possible secuirity risk)?
    – vonbrand
    Jul 19, 2019 at 14:56
  • Note ${this-that} POSIXly means "the value of variable this if it is set, and if it is not set the actual string that". This long-standard and very widely-used form couldn't work if a varname can include hyphen. Mar 16 at 23:16

10 Answers 10

93

I've never met a Bourne-style shell that allowed - in a variable name. Only ASCII letters (of either case), _ and digits are supported, and the first character must not be a digit.

If you have a program that requires an environment variable that doesn't match the shell restrictions, launch it with the env program.

env 'strange-name=some value' myprogram

Note that some shells (e.g. modern dash, mksh, zsh) remove variables whose name they don't like from the environment. (Shellshock has caused people to be more cautious about environment variable names, so restrictions are likely to become tighter over time, not more permissive.) So if you need to pass a variable whose name contains special character to a program, pass it directly, without a shell in between (env 'strange-name=some value' sh -c'…; myprogram' may or may not work).

8
  • No longer works. Feb 9, 2017 at 21:00
  • 8
    @JesseGlick Yes it does. (Your comment would be useful if you defined “not working”: with what data? what is the effect instead of the desired effect? and if you said which implementation of env you used on what operating system.) Feb 9, 2017 at 21:17
  • Sorry. Ubuntu Yakkety with all updates, env from coreutils, sh from dash: env 'with-dashes=value' bash -c 'env | fgrep dashes' works but env 'with-dashes=value' sh -c 'env | fgrep dashes' prints nothing. That is, env itself is fine, but Dash seems to specifically block these variables. Thus if the program in question is launched via a shell wrapper with a typical #!/bin/sh header, there is no apparent way to pass in such variables. example workaround Apr 14, 2017 at 12:23
  • 1
    @JesseGlick That's dash removing the variable. You have to use env closer to the calling site of the program that needs this variable name, without a shell in between. Dash isn't alone in removing variables whose name it doesn't like. Apr 14, 2017 at 13:05
  • 1
    @JesseGlick It's true that some things that used to work might no longer work, though: since shellshock, people have become more conservative about variable names, and dash did in fact change since I wrote this answer (not as a consequence of Shellshock though, but to avoid a bug along the same lines). I've added a note to my answer. Apr 14, 2017 at 13:10
23

You can access a hyphenated variable using an indirect reference.

$ env 'my-hyphenated-variable=hello' /bin/bash
$ name='my-hyphenated-variable'
$ echo ${!name}
hello
2
  • 1
    I had never heard of this functionality until reading this comment.Provided a much easier solution for me than the accepted answer. Aug 26, 2015 at 15:03
  • 14
    This behavior has been disabled in newer versions of bash. See stackoverflow.com/questions/36989263/… for a thorough analysis of the situation. Sep 15, 2016 at 2:16
18

That's not possible in Bash.

From the Definitions section in the manual page of bash:

name A word consisting only of alphanumeric characters and underscores, and beginning with an alphabetic character or an underscore. Also referred to as an identifier.

From the Parameters section in the manual page of bash:

A parameter is an entity that stores values. It can be a name, a number, or one of the special characters listed below under Special Parameters. A variable is a parameter denoted by a name.

3
  • 3
    +1 for demonstrating the usefulness of man pages once more
    – ktf
    Oct 31, 2011 at 11:48
  • 2
    Old post I know but, I want to point out that to newcomers man pages can be quite cryptic. I still have times when I need hunt down better explanations/examples. Anyone would be lying if they said it never happened to them.
    – TCZ8
    Dec 17, 2014 at 15:08
  • 2
    @TCZ8 The only people that have told me they thought man pages were 'cryptic' are the same sort of people that don't read error messages and just skim read everything, skipping right over what they need to read.
    – mrr
    Jan 24, 2017 at 22:49
9

If your script depends on having variable names have hyphens, that's a programming error. If it is convenient for you because of the tools that you regularly use to have the variable names contain a hyphen, you may have to learn more and different tools.

Have you tried using tr to convert the hyphens into underscores?

hyphenated_name="a-b"
unhyphenated_name=$(echo $hyphenated_name | tr '-' '_')
declare -x $unhyphenated_name="some value"

Bash does allow '-' to appear in function names. I do this all the time. For example:

function foo-bar() {
   echo "$@"
}
4

The dash (-) character is a break character and not allowed as part of variable names. There are ways to hack this with quoted variables, but the parsing of it is really problematic. There are also other characters with special meanings in the context of variable names in bash, notably braces, parenthesis, operator characters and quotes. (e.g. {}()=+-&'" and more)

I would suggest that practically you need to find another paradigm on which to build your script. You might have a hang over idea from other languages about "variable variable names". This is generally not a good idea in shell scripts.

If you edit this or ask a new question with details of your context and what you are trying to accomplish we might be able to suggest a good way to script it.

4

The Bash manual defines a "name" as:

A 'word' consisting solely of letters, numbers, and underscores, and beginning with a letter or underscore. 'Name's are used as shell variable and function names. Also referred to as an 'identifier'.

So you can't use a hyphen in a name.

1

You can play with env and sed.

As an example, I needed to read this variable 'ELASTICSEARCH_CLUSTER-NODES'.
The env command output this :

~ $ env
ELASTICSEARCH_CLUSTER-NODES=elasticsearch:9200
JAVA_ALPINE_VERSION=8.212.04-r0
HOSTNAME=17eb9e7fec4c
...

So to extract the variable :

ESHOST=`env | sed -n 's/ELASTICSEARCH_CLUSTER-NODES=\(.*\)/\1/p'`
0

You can use the env command to set and unset environment variables with hiphens "-".

To set you must use env to run your command: env command. You pass variables this way:

env a-b=c command

See it working with:

env a-b=c env

or to make it clearer:

env a-b=c env|grep 'a-b'
0

It depends on the Bash version, but as of 2024-03, it is about POSIX overall 1:

...uppercase letters, digits, and the <underscore> ( '_' )...

Example

$ printf '%s\n' "$BASH_VERSION";
5.1.16(1)-release

$ ABC-DEF=1;
ABC-DEF=1: command not found
$ n='ABC_DEF';
$ "$n=1";
ABC_DEF=1: command not found
$ declare ABC-DEF=1;
-bash: declare: `ABC-DEF=1': not a valid identifier
$ declare 'ABC-DEF=1';
-bash: declare: `ABC-DEF=1': not a valid identifier
$ export 'ABC-DEF=1';
-bash: export: `ABC-DEF=1': not a valid identifier
$ n='ABC-DEF';
$ "$n"=1;
ABC-DEF=1: command not found
$ declare -- "$n=1";
-bash: declare: `ABC-DEF=1': not a valid identifier

$ n='ABC_DEF';
$ declare -- "$n"=1;
$ declare -p -- "$n";
declare -- ABC_DEF="1"
$ n='ABC_DEF';
$ declare -- n1='ABC_DEF_1';
$ declare -- "$n"=1;
$ declare -- "$n1"=2;
$ declare -p "$n" "$n1";
declare -- ABC_DEF="1"
declare -- ABC_DEF_1="2"
$ set | grep ABC;
ABC_DEF=1
ABC_DEF_1=2
_=ABC_DEF_1
n=ABC_DEF
n1=ABC_DEF_1

References

1 POSIX.1-2017 (i.e. IEEE Std 1003.1-2017)

...
These strings have the form name=value; names shall not contain
the character '='. For values to be portable across systems conforming
to POSIX.1-2017, the value shall be composed of characters from the
portable character set (except NUL and as indicated below). There is
no meaning associated with the order of strings in the environment. If
more than one string in an environment of a process has the same name,
the consequences are undefined.

Environment variable names used by the utilities in the Shell and
Utilities volume of POSIX.1-2017 consist solely of uppercase letters,
digits, and the <underscore> ( '_' ) from the characters defined in
Portable Character Set and do not begin with a digit. Other characters
may be permitted by an implementation; applications shall tolerate the
presence of such names. Uppercase and lowercase letters shall retain
their unique identities and shall not be folded together. The name
space of environment variable names containing lowercase letters is
reserved for applications. Applications can define any environment
variables with names from this name space without modifying the
behavior of the standard utilities. Note: Other applications may have
difficulty dealing with environment variable names that start with a
digit. For this reason, use of such names is not recommended anywhere.
...

Source

Related

3
  • 1
    "$n=1"; isn't really relevant: it's not an assignment and will fail even if n contains a valid name. (The same with declare or export is different, but that's just the shell language(s) being odd.)
    – ilkkachu
    Mar 16 at 20:55
  • 2
    note that the first part you quoted, "...uppercase letters, digits, and the <underscore> ( '' )..."_ is from a context that starts "Environment variable names used by the utilities in [the specification] consist solely of ...", which seems to say it's just what the standard utilities make use of, not all that is allowed. It goes on to say that names with lowercase letters are reserved for application use, so they're at least implicitly allowed.
    – ilkkachu
    Mar 16 at 21:07
  • 1
    In any case, environment variables are slightly different from shell variables, and for those, the shell language specification is clear: "A variable is a parameter denoted by a name." where a name is "In the shell command language, a word consisting solely of underscores, digits, and alphabetics from the portable character set. The first character of a name is not a digit.", that is, lowercase letters are included.
    – ilkkachu
    Mar 16 at 21:10
-1

I believe that only letters, numbers, and underscore are allowed for bash variables. This is the case in many programming languages (javascript being an exception).

I recommend not having your script depend on those types of variable names.

In fact, you should try to program in such a way that you can replace variable names with other names and it doesn't make a difference. In general, variable names should describe what the variable contains. This makes it much easier to debug; if not for you, then for the next developer that is trying to figure out the code.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .