10

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.

  • 2
    If you're going to parse something, go with the output of export -p which is specified by POSIX to generate output that is also suitable for input in the shell. – Kusalananda Apr 21 '18 at 16:01
  • I don't need anything to input to a shell. I just want the names of the environment variables. So the extra cruft (e.g., the 'export' word at the front of each line) would just have to be removed anyway. Why would I want to use export -p for this? – Juan Apr 21 '18 at 17:22
  • 1
    You'd want to use export -p because that would give you a consistent output across all POSIX shells, which you said you wanted. – Kusalananda Apr 21 '18 at 17:25
  • I want a solution that will print just the variable names that is a consistent solution across shells. export -p does not fit the first requirement - printing just variable names without values. – Juan Apr 21 '18 at 17:30
  • IF you're going to parse something, go with the output of export -p. I'm not going to write that parsing, because in the general case, it would have to also do proper quote parsing, in case you have a variable whose value is something like hello\nexport var=value. One of the few other commands that will give you consistent output in all POSIX shells is env, but that output is harder to parse since it lacks the export = bit. – Kusalananda Apr 21 '18 at 18:11
8

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.

  • I'm not sure how "easy" it really is with sed(1) with multiline values - I'd like to see that. But thanks for the awk(1) method. I think this is the most portable way so far (although I don't think the exit is necessary). – Juan Apr 23 '18 at 12:34
  • Note that if you get FOO<newline>BAR, you don't know whether it's a FOO<newline>BAR environment variable (which export -p wouldn't show with most shells, see env $'FOO\nBAR=test' awk 'BEGIN{for (v in ENVIRON) print v}') or both a FOO and BAR environment variable. – Stéphane Chazelas Apr 23 '18 at 12:50
  • Note that GNU awks sets environment variables of its own (AWKPATH and AWKLIBPATH on my system) – Stéphane Chazelas Apr 23 '18 at 12:54
  • @StéphaneChazelas - re: embedded newline in var name - agreed - hard to defend against that with shell code. Re: AWKPATH & AWKLIBPATH pollution, I noticed that insidious insertion, too. That's gnu awk, not bsd awk. – Juan Apr 24 '18 at 0:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.