136

How can I get the command arguments or the whole command line from a running process using its process name?

For example this process:

# ps
PID   USER     TIME   COMMAND
 1452 root       0:00 /sbin/udhcpc -b -T 1 -A 12 -i eth0 -p /var/run/udhcpc.eth0.pid

And what I want is /sbin/udhcpc -b -T 1 -A 12 -i eth0 -p /var/run/udhcpc.eth0.pid or the arguments. I know the process name and want its arguments. I'm using Busybox on SliTaz.

8
  • Do you want to process the output from ps (not recommended) or are you looking for some alternative command to ps that will give you the output? What needs to be done when ps gives multiple lines of output? print all/first/last?
    – Anthon
    Commented Oct 20, 2014 at 12:47
  • 1
    Which operating system do you use?
    – Cyrus
    Commented Oct 20, 2014 at 12:53
  • I edited my question. I know the process name and want its arguments. I'm using SliTaz (minimalistic linux) with Busybox.
    – Michael
    Commented Oct 20, 2014 at 12:58
  • @Michael Have you tried arg like command as per @John answer?
    – Pandya
    Commented Oct 20, 2014 at 13:03
  • Yes but how to write it to a variable? I think of two command: PID=pidof <process name> ps -o pid,args|grep $PID|tr -s " "|cut -d " " -f 2 But this way I don't get the args in one variable
    – Michael
    Commented Oct 20, 2014 at 13:06

7 Answers 7

150

You could use the -o switch to specify your output format:

$ ps -eo args

From the man page:

Command with all its arguments as a string. Modifications to the arguments may be shown. [...]

You may also use the -p switch to select a specific PID:

$ ps -p [PID] -o args

pidof may also be used to switch from process name to PID, hence allowing the use of -p with a name:

$ ps -p $(pidof dhcpcd) -o args

Of course, you may also use grep for this (in which case, you must add the -e switch):

$ ps -eo args | grep dhcpcd | head -n -1

GNU ps will also allow you to remove the headers (of course, this is unnecessary when using grep):

$ ps -p $(pidof dhcpcd) -o args --no-headers

On other systems, you may pipe to AWK or sed:

$ ps -p $(pidof dhcpcd) -o args | awk 'NR > 1'
$ ps -p $(pidof dhcpcd) -o args | sed 1d

Edit: if you want to catch this line into a variable, just use $(...) as usual:

$ CMDLINE=$(ps -p $(pidof dhcpcd) -o args --no-headers)

or, with grep :

$ CMDLINE=$(ps -eo args | grep dhcpcd | head -n -1)
6
  • 1
    @Michael replace args with command (or cmd).
    – Pandya
    Commented Oct 20, 2014 at 13:00
  • 1
    @Pandya Both cmd and command are aliases to args, this is probably unnecessary. Commented Oct 20, 2014 at 13:05
  • 1
    I don't have a -p option using Busybox: Usage: ps [-o COL1,COL2=HEADER] [-T]
    – Michael
    Commented Oct 20, 2014 at 13:11
  • You may use grep to catch the line you're actually interested in: ps -eo args | grep dhcpcd | head -n -1. I edited my answer. Commented Oct 20, 2014 at 13:13
  • 1
    The command line appears to be truncated at 4096 bytes in the middle of an argvalue. According to unix.stackexchange.com/questions/91561/… this is a problem with older kernels. Commented Dec 3, 2018 at 17:32
58

Method #1 - Using ps

You could use ps -eaf | grep 1234.

Example

$ ps -eaf | grep 28865
saml     28865  9661  0 03:06 pts/2    00:00:00 bash -c sleep 10000; while [ 1 ];do echo hi;sleep 10;done
saml     28866 28865  0 03:06 pts/2    00:00:00 sleep 10000

NOTE: Busybox's ps doesn't include the -eaf switches as shown above from a typical ps that's included with most Linuxes, however Busybox's ps shows what looks to be very similar output to the example I provided. You can install Busybox on most Linuxes and run it like so:

$ busybox ps
  852 root       0:00 /sbin/auditd -n
  855 root       0:01 /sbin/audispd
  857 root       0:00 /usr/sbin/sedispatch
  866 root       0:00 /usr/sbin/alsactl -s -n 19 -c -E ALSA_CONFIG_PATH=/etc/alsa/alsactl.conf --initfile=/lib/alsa/init/00main rdaemon
  867 root       0:00 /usr/libexec/bluetooth/bluetoothd
  869 root       0:01 {firewalld} /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid
  871 root       0:32 /usr/libexec/accounts-daemon
  873 rtkit      0:05 /usr/libexec/rtkit-daemon
  875 root       0:00 /usr/sbin/ModemManager
  876 avahi      0:03 avahi-daemon: running [dufresne.local]
  878 root       0:54 /usr/sbin/irqbalance --foreground
  884 root       0:00 /usr/sbin/smartd -n -q never
  886 avahi      0:00 avahi-daemon: chroot helper
  891 chrony     0:01 /usr/sbin/chronyd
  892 root       0:01 /usr/lib/systemd/systemd-logind
  893 dbus       1:28 /bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation

Method #2 - Using /proc

You can also look at the cmdline file that each PID has under /proc/<pid>.

$ cat /proc/28865/cmdline 
bash-csleep 10000; while [ 1 ];do echo hi;sleep 10;done

But notice that it's missing the spacing. This is due to a NUL character being used within this file to separate your command line arguments. Not to worry though, these can be stripped out.

$ tr '\0' ' ' </proc/28865/cmdline
bash -c sleep 10000; while [ 1 ];do echo hi;sleep 10;done

References

3
  • 3
    The /proc/*/cmdline method is very useful when ps command not available.
    – allenyllee
    Commented Aug 17, 2021 at 11:36
  • I tried this and did hexdump -C /proc/123/cmdline before I read to the end of the post about tr, so I guess that's another way. I have ps but none of the described arguments worked for me.
    – Keeely
    Commented Nov 23, 2021 at 23:51
  • 2
    Thanks for including info on the NUL character being used for spaces in the /proc/ output, I was confused how exactly ps and these other tools were getting the command line with spaces in the right places. Commented Jan 4, 2022 at 14:40
18

Try something like this:

(example output from busybox on OpenWrt on one of my routers)

root@ap8:~# xargs -0 printf '%s\n' </proc/991/cmdline
/usr/sbin/uhttpd
-f
-h
/www
-r
ap8
-x
/cgi-bin
-u
/ubus
-t
60
-T
30
-k
20
-A
1
-n
3
-N
100
-R
-p
0.0.0.0:80
-p
[::]:80

/proc/$PID/cmdline contains the arguments of process $PID like a C-ish strings one after another. Each string is zero terminated.

Quotes arround some arguments or options are shell stuff. You have to look closer at the lines being shown and where spaces or other characters with special meaning for the shell are used. You will need to quote that character(s) somehow or the complete argument when joining these lines to a command line again.

5
  • 1
    tr "\0" " " </proc/991/cmdline
    – Cyrus
    Commented Oct 20, 2014 at 13:20
  • @Cyrus: You cannot distinguish args containg spaces from adjacent separate args then. By replacing the zero byte with a space you destroy information.
    – user62916
    Commented Oct 20, 2014 at 13:32
  • 1
    I do agree replacing \0 with ` ` to be bad, but I think tr '\0' '\n' < /proc/$foo/cmdline to be a bit simpler than xargs.
    – phemmer
    Commented Oct 20, 2014 at 14:00
  • printf makes it easier to add quotes to the output, insert a space instead of the new line and so on. When thinking of adding more processing, the way over printf is a good start.
    – user62916
    Commented Oct 20, 2014 at 14:21
  • This is the best answer for distributions such as Alpine.
    – ivan
    Commented Aug 13, 2020 at 8:58
17

Knowing the PID, just exec

cat /proc/pid/cmdline

For instance, for PID = 127

# cat /proc/127/cmdline ; echo ""
/usr/lib/jvm/jdk-8-oracle-x64//bin/java-Djava.util.logging.config.file=/opt/tomcat/conf/logging.properties-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager-Djava.library.path=/usr/lib/jni-javaagent:/jmxtrans-agent-1.2.2.jar=/opt/tomcat/conf/jmxtrans-agent.xml-Dcom.sun.management.jmxremote.port=5000-Dcom.sun.management.jmxremote-Dcom.sun.management.jmxremote.ssl=false-Dcom.sun.management.jmxremote.authenticate=false-Djava.endorsed.dirs=/opt/tomcat/endorsed-classpath/opt/tomcat/bin/bootstrap.jar:/opt/tomcat/bin/tomcat-juli.jar-Dcatalina.base=/opt/tomcat-Dcatalina.home=/opt/tomcat-Djava.io.tmpdir=/opt/tomcat/temporg.apache.catalina.startup.Bootstrapstart
#
1
  • 1
    Thank you! This is the only answer, which displays the whole length of a very large command.
    – Sim
    Commented Feb 27, 2020 at 9:11
4

If you like short commands and pgrep is available, I would suggest pgrep -fl <process_name>.

ps -o args is truncated

1

pgrep -a <name>

In your example :

pgrep -a udhcpc

pgrep does not truncate line, but ps does.

For those that insist on using ps, then do that to avoid truncation :

ps <processID> | tee

0

Coming late to the party: extracting the strings from /proc/$pid/cmdline, and displaying them with proper shell quoting.

This requires bash version 4.4+ (but possibly v5.1 for some bug fixes noted in the CHANGES file)

mapfile -d '' -t cmd < /proc/$pid/cmdline
echo "${cmd[@]@Q}"
  • mapfile reads the lines of a file into an array
  • the -d '' option uses null instead of newline as the line terminator
  • @Q transformation emits each element of the array with shell quoting

Demo:

$ sh -c "echo 'hello world'; sleep 5m" &
[1] 14844
hello world
$ pid=$!
$ mapfile -d '' -t cmd < /proc/$pid/cmdline
$ declare -p cmd
declare -a cmd=([0]="sh" [1]="-c" [2]="echo 'hello world'; sleep 5m")
$ echo "${cmd[@]@Q}"
'sh' '-c' 'echo '\''hello world'\''; sleep 5m'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .