Command Line Bash – How to Split a ‘/proc/*/environ’ File into Separate Lines

bashcommand lineproc

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?

Best Answer

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.