40

I'm trying to get the content of any /proc/*PID*/environ file in more readable format. I'm able to do that in the way shown below, but I'm sure this isn't the proper way.

$ cat "/proc/$(pgrep gnome-session -n -U $UID)/environ"
USER=spasTEXTDOMAIN=im-configXDG_SEAT=seat0XDG_SESSION_TYPE=waylandSHLVL=1QT4_IM_MODULE=ximHOME=/home/spasDESKTOP_SESSION=ubuntuGNOME_SHELL_SESSION_MODE=ubuntuDBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/busIM_CONFIG_PHASE=2LOGNAME=spasGTK_IM_MODULE=ibusJOURNAL_STREAM=9:147845_=/usr/bin/gnome-sessionUSERNAME=spasXDG_SESSION_ID=70PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/binXDG_RUNTIME_DIR=/run/user/1000LANG=en_US.UTF-8XDG_CURRENT_DESKTOP=ubuntu:GNOMEXDG_SESSION_DESKTOP=ubuntuXMODIFIERS=@im=ibusSHELL=/bin/bashGDMSESSION=ubuntuTEXTDOMAINDIR=/usr/share/locale/XDG_VTNR=2QT_IM_MODULE=ximPWD=/home/spasCLUTTER_IM_MODULE=ximXDG_DATA_DIRS=/usr/share/ubuntu:/usr/local/share:/usr/share:/var/lib/snapd/desktopXDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/etc/xdg
$ cat -e "/proc/$(pgrep gnome-session -n -U $UID)/environ"
USER=spas^@TEXTDOMAIN=im-config^@XDG_SEAT=seat0^@XDG_SESSION_TYPE=wayland^@SHLVL=1^@QT4_IM_MODULE=xim^@HOME=/home/spas^@DESKTOP_SESSION=ubuntu^@GNOME_SHELL_SESSION_MODE=ubuntu^@DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus^@IM_CONFIG_PHASE=2^@LOGNAME=spas^@GTK_IM_MODULE=ibus^@JOURNAL_STREAM=9:147845^@_=/usr/bin/gnome-session^@USERNAME=spas^@XDG_SESSION_ID=70^@PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin^@XDG_RUNTIME_DIR=/run/user/1000^@LANG=en_US.UTF-8^@XDG_CURRENT_DESKTOP=ubuntu:GNOME^@XDG_SESSION_DESKTOP=ubuntu^@XMODIFIERS=@im=ibus^@SHELL=/bin/bash^@GDMSESSION=ubuntu^@TEXTDOMAINDIR=/usr/share/locale/^@XDG_VTNR=2^@QT_IM_MODULE=xim^@PWD=/home/spas^@CLUTTER_IM_MODULE=xim^@XDG_DATA_DIRS=/usr/share/ubuntu:/usr/local/share:/usr/share:/var/lib/snapd/desktop^@XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/etc/xdg^@
$ cat -e "/proc/$(pgrep gnome-session -n -U $UID)/environ" | sed 's/\^@/\n/g'
USER=spas
TEXTDOMAIN=im-config
XDG_SEAT=seat0
XDG_SESSION_TYPE=wayland
...

Maybe I must assign a specific value to $IFS, but what is it? What is the proper way to achieve the above result?

pa4080
  • 29,831

6 Answers6

63

The entries are separated by the null character, see man 5 proc:

/proc/[pid]/environ
      This file contains the environment for the process.  The entries
      are separated by null bytes ('\0'), and there may be a null byte
      at  the  end.

So a simple way is to apply xargs -0 -L1 on it:

$ xargs -0 -L1 -a /proc/self/environ
LC_CTYPE=UTF-8
USER=muru
LOGNAME=muru
HOME=/home/muru
MAIL=/var/mail/muru
SHELL=/bin/zsh
...
  • -0 - read null-delimited lines,
  • -L1 - read one line per execution of command
  • -a file read lines from file
  • and if no command is specified, xargs simply prints the line.

Various GNU commands have options to work with null-delimited data: -z for sed, sort, uniq, grep etc, and for filenames, -print0 with find and -Z with grep.

Alternatively, you can use plain old bash:

while IFS= read -d '' -r line
do
    printf "%q\n" "$line"
done < /proc/.../environ

-d '' tells read to read until a null byte, IFS= and -r prevent field-splitting and backslash-escaping, so that the data is read as-is, %q will quote special characters in output.

Since you did use sed, you could have done:

sed -z 's/$/\n/' /proc/.../environ

which just tacks on a newline at the end of each null-delimited line.

muru
  • 197,895
  • 55
  • 485
  • 740
  • 1
    +1 for the default of xargs to print, I wasn't aware of that – Vincent Fourmond Nov 21 '17 at 20:43
  • 2
    Keep in mind, though, that the entries are null-terminated precisely because it is the only character that cannot be part of a value. If you replace them with newlines, you potentially introduce an ambiguity of whether two consecutive lines represent two variables, or one variable whose value contains a newline followed by what looks like an assignment. – chepner Nov 21 '17 at 20:45
  • +1 The plain old bash answer is useful for containers where you don't have a lot else. – Roger Lipscombe Jan 29 '24 at 14:03
23
  • You could use cut:

    cut -d '' -f1- --output-delimiter=$'\n' /proc/$pid/environ
    

    using null as a delimiter, and outputting a newline delimiter, optionally picking only certain fields/lines.

  • Or filter through tr, translating nulls to newlines:

    tr '\0' '\n' </proc/$pid/environ
    
  • Or just stick with your sed version...

pa4080
  • 29,831
Xen2050
  • 8,705
  • 11
    I've been using tr '\0' '\n' ever since I can recall. That's the most intuitive way for me to express "replace all NULs with newlines", and hence my personal preferred solution. – egmont Nov 21 '17 at 19:40
18

You can use strings as follows:

strings /proc/$(pgrep gnome-session -n -U $UID)/environ

Sample of the output:

$ strings /proc/$(pgrep gnome-session -n -U $UID)/environ
GNOME_KEYRING_PID=
LANGUAGE=en_US
J2SDKDIR=/usr/lib/jvm/java-8-oracle
LC_TIME=en_US.UTF-8
XDG_SEAT=seat0
XDG_SESSION_TYPE=x11
COMPIZ_CONFIG_PROFILE=ubuntu
SESSION=ubuntu

man strings

NAME

   strings - print the strings of printable characters in files.

DESCRIPTION

   For each file given, GNU strings prints the printable character
   sequences that are at least 4 characters long (or the number given with
   the options below) and are followed by an unprintable character.  By
   default, it only prints the strings from the initialized and loaded
   sections of object files; for other types of files, it prints the
   strings from the whole file.

   strings is mainly useful for determining the contents of non-text
   files.
Yaron
  • 13,173
  • 4
    that's beautiful. – Prisoner Oct 24 '19 at 09:56
  • 1
    As written in the quoted manual, strings only prints strings of length >= 4. Environment variables like A=1 will be ignored. Also, whitespace (expect space and tab) is ignored. You can change this by using strings -n2 -w instead. – Socowi Mar 16 '21 at 23:44
  • perfect, love it. also thanks for the -n2 thingy – tollo Mar 20 '24 at 12:39
4

"man proc", search "environ", help text says:

Thus, to print out the envi‐ronment of process 1, you would do:

$ strings /proc/1/environ
muru
  • 197,895
  • 55
  • 485
  • 740
shengmx
  • 41
2

Building on @muru's answer, you can then export those variables with the following:

`cat /proc/1/environ | xargs -0 -L1 -I{} echo export {}`
Marcin
  • 121
-4
hexdump -v -e '/1 "%02X "' /proc/PID/environ |sed -e 's/00/0a/g' | xxd -r -p 

how does this work first hexdump -v -e '/1 "%02X "' /proc/PID/environ will (if you replace PID with the pid of the program or self) print all of the file in hex with bite separated with a space. next i pipe it into sed -e 's/00/0a/g' which takes all 00 aka nulls which are used as separators for each entry and converts them into newlines. lest i use xxd -r -p to convert back the hex to binary aka readable text.

lnee
  • 806
  • 3
    Can you explain a little bit how this works and how this will solve OP's question? – Thomas Ward Oct 11 '21 at 02:53
  • A good explaination would be really interesting. I understand that hexdump is used to make a hexadecimal representation of the environ-content which used to convert NUL-Characters into newlines and convert it back into binary. I'm personally interested why @Inee chose those tools. – MadMike Oct 22 '21 at 08:33
  • because i use (and made) a tool called bintools (you can find it here https://github.com/lnee94/resh/blob/main/l/bintools ) and it has thos commands in it and it seems the most deterministic – lnee Oct 22 '21 at 14:30