29.2. /proc

The /proc directory is actually a pseudo-filesystem. The files in /proc mirror currently running system and kernel processes and contain information and statistics about them.

 bash$ cat /proc/devices
 Character devices:
   1 mem
   2 pty
   3 ttyp
   4 ttyS
   5 cua
   7 vcs
  10 misc
  14 sound
  29 fb
  36 netlink
 128 ptm
 136 pts
 162 raw
 254 pcmcia

 Block devices:
   1 ramdisk
   2 fd
   3 ide0
   9 md
 
 
 
 bash$ cat /proc/interrupts
            CPU0       
   0:      84505          XT-PIC  timer
   1:       3375          XT-PIC  keyboard
   2:          0          XT-PIC  cascade
   5:          1          XT-PIC  soundblaster
   8:          1          XT-PIC  rtc
  12:       4231          XT-PIC  PS/2 Mouse
  14:     109373          XT-PIC  ide0
 NMI:          0 
 ERR:          0
 
 
 bash$ cat /proc/partitions
 major minor  #blocks  name     rio rmerge rsect ruse wio wmerge wsect wuse running use aveq

    3     0    3007872 hda 4472 22260 114520 94240 3551 18703 50384 549710 0 111550 644030
    3     1      52416 hda1 27 395 844 960 4 2 14 180 0 800 1140
    3     2          1 hda2 0 0 0 0 0 0 0 0 0 0 0
    3     4     165280 hda4 10 0 20 210 0 0 0 0 0 210 210
    ...
 
 
 
 bash$ cat /proc/loadavg
 0.13 0.42 0.27 2/44 1119
 
 
 
 bash$ cat /proc/apm
 1.16 1.2 0x03 0x01 0xff 0x80 -1% -1 ?
 
 
 
 bash$ cat /proc/acpi/battery/BAT0/info
 present:                 yes
 design capacity:         43200 mWh
 last full capacity:      36640 mWh
 battery technology:      rechargeable
 design voltage:          10800 mV
 design capacity warning: 1832 mWh
 design capacity low:     200 mWh
 capacity granularity 1:  1 mWh
 capacity granularity 2:  1 mWh
 model number:            IBM-02K6897
 serial number:            1133
 battery type:            LION
 OEM info:                Panasonic
  
  
  
 bash$ fgrep Mem /proc/meminfo
 MemTotal:       515216 kB
 MemFree:        266248 kB
          

Shell scripts may extract data from certain of the files in /proc. [1]

   1 FS=iso                       # ISO filesystem support in kernel?
   2 
   3 grep $FS /proc/filesystems   # iso9660

   1 kernel_version=$( awk '{ print $3 }' /proc/version )

   1 CPU=$( awk '/model name/ {print $5}' < /proc/cpuinfo )
   2 
   3 if [ "$CPU" = "Pentium(R)" ]
   4 then
   5   run_some_commands
   6   ...
   7 else
   8   run_other_commands
   9   ...
  10 fi
  11 
  12 
  13 
  14 cpu_speed=$( fgrep "cpu MHz" /proc/cpuinfo | awk '{print $4}' )
  15 #  Current operating speed (in MHz) of the cpu on your machine.
  16 #  On a laptop this may vary, depending on use of battery
  17 #+ or AC power.

   1 #!/bin/bash
   2 # get-commandline.sh
   3 # Get the command-line parameters of a process.
   4 
   5 OPTION=cmdline
   6 
   7 # Identify PID.
   8 pid=$( echo $(pidof "$1") | awk '{ print $1 }' )
   9 # Get only first            ^^^^^^^^^^^^^^^^^^ of multiple instances.
  10 
  11 echo
  12 echo "Process ID of (first instance of) "$1" = $pid"
  13 echo -n "Command-line arguments: "
  14 cat /proc/"$pid"/"$OPTION" | xargs -0 echo
  15 #   Formats output:        ^^^^^^^^^^^^^^^
  16 #   (Thanks, Han Holl, for the fixup!)
  17 
  18 echo; echo
  19 
  20 
  21 # For example:
  22 # sh get-commandline.sh xterm

+

   1 devfile="/proc/bus/usb/devices"
   2 text="Spd"
   3 USB1="Spd=12"
   4 USB2="Spd=480"
   5 
   6 
   7 bus_speed=$(fgrep -m 1 "$text" $devfile | awk '{print $9}')
   8 #                 ^^^^ Stop after first match.
   9 
  10 if [ "$bus_speed" = "$USB1" ]
  11 then
  12   echo "USB 1.1 port found."
  13   # Do something appropriate for USB 1.1.
  14 fi

Note

It is even possible to control certain peripherals with commands sent to the /proc directory.
 	  root# echo on > /proc/acpi/ibm/light
           
This turns on the Thinklight in certain models of IBM/Lenovo Thinkpads. (May not work on all Linux distros.)

Of course, caution is advised when writing to /proc.

The /proc directory contains subdirectories with unusual numerical names. Every one of these names maps to the process ID of a currently running process. Within each of these subdirectories, there are a number of files that hold useful information about the corresponding process. The stat and status files keep running statistics on the process, the cmdline file holds the command-line arguments the process was invoked with, and the exe file is a symbolic link to the complete path name of the invoking process. There are a few more such files, but these seem to be the most interesting from a scripting standpoint.


Example 29-3. Finding the process associated with a PID

   1 #!/bin/bash
   2 # pid-identifier.sh:
   3 # Gives complete path name to process associated with pid.
   4 
   5 ARGNO=1  # Number of arguments the script expects.
   6 E_WRONGARGS=65
   7 E_BADPID=66
   8 E_NOSUCHPROCESS=67
   9 E_NOPERMISSION=68
  10 PROCFILE=exe
  11 
  12 if [ $# -ne $ARGNO ]
  13 then
  14   echo "Usage: `basename $0` PID-number" >&2  # Error message >stderr.
  15   exit $E_WRONGARGS
  16 fi  
  17 
  18 pidno=$( ps ax | grep $1 | awk '{ print $1 }' | grep $1 )
  19 # Checks for pid in "ps" listing, field #1.
  20 # Then makes sure it is the actual process, not the process invoked by this script.
  21 # The last "grep $1" filters out this possibility.
  22 #
  23 #    pidno=$( ps ax | awk '{ print $1 }' | grep $1 )
  24 #    also works, as Teemu Huovila, points out.
  25 
  26 if [ -z "$pidno" ]  #  If, after all the filtering, the result is a zero-length string,
  27 then                #+ no running process corresponds to the pid given.
  28   echo "No such process running."
  29   exit $E_NOSUCHPROCESS
  30 fi  
  31 
  32 # Alternatively:
  33 #   if ! ps $1 > /dev/null 2>&1
  34 #   then                # no running process corresponds to the pid given.
  35 #     echo "No such process running."
  36 #     exit $E_NOSUCHPROCESS
  37 #    fi
  38 
  39 # To simplify the entire process, use "pidof".
  40 
  41 
  42 if [ ! -r "/proc/$1/$PROCFILE" ]  # Check for read permission.
  43 then
  44   echo "Process $1 running, but..."
  45   echo "Can't get read permission on /proc/$1/$PROCFILE."
  46   exit $E_NOPERMISSION  # Ordinary user can't access some files in /proc.
  47 fi  
  48 
  49 # The last two tests may be replaced by:
  50 #    if ! kill -0 $1 > /dev/null 2>&1 # '0' is not a signal, but
  51                                       # this will test whether it is possible
  52                                       # to send a signal to the process.
  53 #    then echo "PID doesn't exist or you're not its owner" >&2
  54 #    exit $E_BADPID
  55 #    fi
  56 
  57 
  58 
  59 exe_file=$( ls -l /proc/$1 | grep "exe" | awk '{ print $11 }' )
  60 # Or       exe_file=$( ls -l /proc/$1/exe | awk '{print $11}' )
  61 #
  62 #  /proc/pid-number/exe is a symbolic link
  63 #+ to the complete path name of the invoking process.
  64 
  65 if [ -e "$exe_file" ]  #  If /proc/pid-number/exe exists,
  66 then                   #+ then the corresponding process exists.
  67   echo "Process #$1 invoked by $exe_file."
  68 else
  69   echo "No such process running."
  70 fi  
  71 
  72 
  73 #  This elaborate script can *almost* be replaced by
  74 #       ps ax | grep $1 | awk '{ print $5 }'
  75 #  However, this will not work...
  76 #+ because the fifth field of 'ps' is argv[0] of the process,
  77 #+ not the executable file path.
  78 #
  79 # However, either of the following would work.
  80 #       find /proc/$1/exe -printf '%l\n'
  81 #       lsof -aFn -p $1 -d txt | sed -ne 's/^n//p'
  82 
  83 # Additional commentary by Stephane Chazelas.
  84 
  85 exit 0


Example 29-4. On-line connect status

   1 #!/bin/bash
   2 # connect-stat.sh
   3 #  Note that this script may need modification
   4 #+ to work with a wireless connection.
   5 
   6 PROCNAME=pppd        # ppp daemon
   7 PROCFILENAME=status  # Where to look.
   8 NOTCONNECTED=85
   9 INTERVAL=2           # Update every 2 seconds.
  10 
  11 pidno=$( ps ax | grep -v "ps ax" | grep -v grep | grep $PROCNAME |
  12 awk '{ print $1 }' )
  13 
  14 # Finding the process number of 'pppd', the 'ppp daemon'.
  15 # Have to filter out the process lines generated by the search itself.
  16 #
  17 #  However, as Oleg Philon points out,
  18 #+ this could have been considerably simplified by using "pidof".
  19 #  pidno=$( pidof $PROCNAME )
  20 #
  21 #  Moral of the story:
  22 #+ When a command sequence gets too complex, look for a shortcut.
  23 
  24 
  25 if [ -z "$pidno" ]   # If no pid, then process is not running.
  26 then
  27   echo "Not connected."
  28 # exit $NOTCONNECTED
  29 else
  30   echo "Connected."; echo
  31 fi
  32 
  33 while [ true ]       # Endless loop, script can be improved here.
  34 do
  35 
  36   if [ ! -e "/proc/$pidno/$PROCFILENAME" ]
  37   # While process running, then "status" file exists.
  38   then
  39     echo "Disconnected."
  40 #   exit $NOTCONNECTED
  41   fi
  42 
  43 netstat -s | grep "packets received"  # Get some connect statistics.
  44 netstat -s | grep "packets delivered"
  45 
  46 
  47   sleep $INTERVAL
  48   echo; echo
  49 
  50 done
  51 
  52 exit 0
  53 
  54 # As it stands, this script must be terminated with a Control-C.
  55 
  56 #    Exercises:
  57 #    ---------
  58 #    Improve the script so it exits on a "q" keystroke.
  59 #    Make the script more user-friendly in other ways.
  60 #    Fix the script to work with wireless/DSL connections.

Warning

In general, it is dangerous to write to the files in /proc, as this can corrupt the filesystem or crash the machine.

Notes

[1]

Certain system commands, such as procinfo, free, vmstat, lsdev, and uptime do this as well.