Bash – posix shell: print list of environment variable names (without values)

bashenvironment-variablesposixshell

In a posix-compatible way that works with multiple implementations, how can I print the list of currently defined environment variable without their values?

On some implementations (mksh, freebsd /bin/sh), just using export by itself will fit the bill:

$ export
FOO2
FOO

But for some other implementations (bash, zsh, dash), export also shows the value. With bash, for example:

$ export
export FOO2='as  df\
  asdk=fja:\
asd=fa\
asdf'
export FOO='sjfkasjfd  kjasdf:\
   asdkj=fkajdsf:\
       :askjfkajsf=asdfkj:\
   safdkj'
$ printenv | sed -n l
FOO2=as\tdf\$
  asdk=fja:\$
asd=fa\$
asdf$
FOO=sjfkasjfd  kjasdf:\$
   asdkj=fkajdsf:\$
\t:askjfkajsf=asdfkj:\$
   safdkj$

Other options like env or printenv don't have options to print just the variable names without values, at least not on the linux and freebsd platforms I have tried.

Piping to awk/sed/etc. or trimming the list with parameter expansion techniques (e.g., ${foo%%=*}) is acceptable, but it has to work with values that may span lines and have = and whitespace in the value (see example above).

Answers specific to particular shell implementations are interesting, but I am primarily looking for something that is compatible across implementations.

Best Answer

It's pretty easy in awk.

awk 'BEGIN{for(v in ENVIRON) print v}'

However beware some awk implementations add environment variables of their own (e.g. GNU awk adds AWKPATH and AWKLIBPATH to ENVIRON).

The output is ambiguous if the name of an environment variable contains a newline, which is extremely unusual but technically possible. A pure sh solution would be difficult. Your best bet is to start with export -p but massaging it in pure sh is difficult. You can use sed to massage the output of export -p, then use eval to get the shell to remove what it quoted. Bash and zsh print non-standard prefixes.

report () { echo "${1%%=*}"; };
eval "$(export -p | sed "s/^export /report /;
                         s/^declare -x /report /;
                         s/typeset -x /report /")"

Note that depending on the shell, export -p may or may not show variables whose name is not valid in the shell, and if it doesn't, then it may or may not quote the names properly. For example, dash, mksh and zsh omit variables whose name includes a newline, BusyBox dash and ksh93 print them raw, and bash prints them raw without their value. If you need to defend against untrusted input, don't rely on a pure POSIX solution, and definitely don't call eval on anything derived from the output of export -p.