Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

The bash builtin command set, if invoked without arguments, will print all shell and environment variables, but also all defined functions. This makes the output unusable for humans and difficult to grep.

How can I make the bash builtin command set print only the variables and not the functions?

Are there other commands which prints only the shell variables, without the functions?

Note: bash differentiates between shell variables and environment variables. see here Difference between environment variables and exported environment variables in bash

share|improve this question
up vote 16 down vote accepted

"Are there other commands which prints only the shell variables, without the functions?"

In man bash, in section SHELL BUILTIN COMMANDS (in the set section) it says: "In posix mode, only shell variables are listed."

(set -o posix; set)
share|improve this answer
By far the best solution – Ruslan May 25 '14 at 9:27
There are a bunch of usually uninteresting variables starting with _, which are easy to get rid of: (set -o posix ; set | grep -v ^_) – hyde Nov 24 '14 at 15:34
This has been bothering me too for so long! Grepping away the lines that start with _ isn't enough if you have multi-line values, it will still leave the contents of the value in tact. Since set prints the lines in order, all the _ lines will be at the end, so you can simply cut the results after the first _ line, using: set -o posix; set | sed -e '/^_/,$d'; set +o posix; – Scott Apr 6 '15 at 21:52
Sorry I accidentally hit enter before I was done and it only lets you edit comments for a 5 minute window... Setting the -o posix setting will affect the rest of your bash shell session, so it's best to do it in a new invocation like so: bash -c "set -o posix; set | sed -e '/^_/,\$d';" Or if you want to alias it to a new command, for instance "printset", then put this in your ~/.bashrc or ~/.bash_profile: alias printset="bash -c \"set -o posix; set | sed -e '/^_/,\\\$d';\"" Then you can just run something like printset | grep PATH for instance – Scott Apr 6 '15 at 22:07

Here are some workarounds:

$ comm -3 <(declare | sort) <(declare -f | sort)


  1. declare prints every defined variable (exported or not) and function.
  2. declare -f prints only functions.
  3. comm -3 will remove all lines common to both. In effect this will remove the functions, leaving only the variables.

To only print variables which are not exported:

$ comm -3 <(comm -3 <(declare | sort) <(declare -f | sort)) <(env | sort)

Another workaround:

$ declare -p

This will only print the variables, but with some ugly attributes.

declare -- BASH="/bin/bash"
declare -ir BASHPID=""
declare -A BASH_ALIASES='()'
declare -a BASH_ARGC='()'

You can cut the attributes away using... cut:

$ declare -p | cut -d " " -f 3

One downside is that the value of IFS is interpreted instead of displayed.


$ comm -3 <(declare | sort) <(declare -f | sort)
IFS=$' \t\n'
$ declare -p | cut -d " " -f 3

This makes it quite hard to use that output for further processing, because of that lone " in one line. Perhaps some IFS-fu can be done to prevent this.

Yet another workaround, using compgen:

$ compgen -v

The bash builtin compgen was meant to be used in completion scripts. To this end, compgen -v lists all defined variables. The downside: it lists only the variable names, not the values.

Here is a hack to also list the values.

$ compgen -v | while read var; do printf "%s=%q\n" "$var" "${!var}"; done

The advantage: it is a pure bash solution. The disadvantage: some values are messed up because of the interpretation through printf. Also the subshell from the pipe and/or the loop add some extra variables.

share|improve this answer
does your declare ...| cut pipe break if there are no attributes for a variable? I'm guessing that -- is used in that case, but I'm still uneasy. sed might be safer, i.e. declare -p | sed 's/^.* \([^ ]\+\)$/\1/' – jmtd May 12 '11 at 14:29
+1 for compgen :) – RSFalcon7 Mar 6 '14 at 23:55

The typeset builtin strangely has an option to show functions only (-f) but not to show parameters only. Fortunately, typeset (or set) displays all parameters before all functions, function and parameter names cannot contain newlines or equal signs, and newlines in parameter values are quoted (they appear as \n). So you can stop at the first line that doesn't contain an equal sign:

set | awk -F '=' '! /^[0-9A-Z_a-z]+=/ {exit} {print $1}'

This only prints the parameter names; if you want the values, change print $1 to print $0.

Note that this prints all parameter names (parameters and variables are used synonymously here), not just environment variables (“environment variable” is synonymous with “exported parameters”).

Note also that this assumes there is no environment variable with a name that doesn't match bash's constraints on parameter names. Such variables cannot be created inside bash but can have been inherited from the environment when bash starts:

env 'foo ()
  =oops' bash
share|improve this answer
Great solution! I didn't even think of just stopping at the first line that doesn't have a '='. +1 – Steven D Oct 27 '10 at 3:34

I am unsure how one can make set print only variables. However by looking at the output of set, I was able to come up with the following that seems to grab just variables:

$ set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" 

Basically, I am looking for lines that start with letters, numbers, or punctuation followed by a "=". From the output that I saw, this grabs all of the variables; however, I doubt that this is very portable.

If, as your title suggests, you want to subtract from this list the variables that are exported and thus get a list of non-exported variables, then you could do something like this

$ set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars && env | sort | comm -23 ./setvars - 

To break this down a bit, here is what it does:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars : This hopefully gets all of the variables (as discussed before), sorts them, and sticks the result in a file.
  2. && env | sort: After the previous command is complete, we are going to call env and sort its output.
  3. | comm -23 ./setvars -: Finally, we pipe the sorted output of env into comm and use the -23 option to print the lines that are unique to the first argument, in this case the lines unique to our output from set.

When you are done you might want to cleanup the temp file that it created with the command rm ./setvars

share|improve this answer
The problem with your command is not portability (it's specific to bash, of course, but there's no problem on the grep side). The problem is that you're grabbing some lines in function definitions. Bash indents most of the function code, but not all of it, in particular not text inside here documents (f () {|cat <<EOF|foo=bar|EOF|} where | represents a line break). – Gilles Oct 26 '10 at 19:01

Just use the command env. It does not print functions.

share|improve this answer
It does if used inside a script. – DocSalvager Sep 5 '13 at 13:08
+1 this is what I use when at the command line. – Michael Durrant Apr 19 '15 at 14:43

Try the command printenv:

share|improve this answer
printenv and env only print exported (environment) variables and not non-exported (shell) variables. – user495470 Jun 5 '13 at 7:36

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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