Why does echo $$ return a number?

Why does running echo $$ in bash return a number like 7190, while running echo $ only returns a $?


Solution 1:

Convention.

$$: Expands to the process ID of the shell. In a () subshell, it expands to the process ID of the invoking shell, not the subshell (see the link to the manual below).

rinzwind@schijfwereld:~$ echo $$
3244
rinzwind@schijfwereld:~$ ps -ef |grep 3244
rinzwind  3244  3237  0 19:06 pts/0    00:00:00 /bin/bash

Very useful when coding software. And it can be used as a crude (mktemp would be the better method) way of creating temp files

1 $ has no special meaning so it gives you what echo always does: return it.

There is a manual page dedicated to this (3.4.2 Special Parameters).

Solution 2:

In bash to state using a variable we use $, using $$ with the first dollar sign we are saying that I want to use a variable and using the second one we are telling that the name of that variable is actually a $. it's actually a naming convention, and this variable contains the process id of the current shell.

As you asked in the comments with $$$$ you are returning the same process id twice.


There are other variables too (From here):

  • $1, $2, $3, ... are the positional parameters.
  • "$@" is an array-like construct of all positional parameters, {$1, $2, $3 ...}.
  • "$*" is the IFS expansion of all positional parameters, $1 $2 $3 ....
  • $# is the number of positional parameters.
  • $- current options set for the shell.
  • $$ pid of the current shell (not subshell).
  • $_ most recent parameter (or the abs path of the command to start the current shell immediately after startup).
  • $IFS is the (input) field separator.
  • $? is the most recent foreground pipeline exit status.
  • $! is the PID of the most recent background command.
  • $0 is the name of the shell or shell script.

Solution 3:

Here is a real life application of $$ taken from Lock Screen Timer:

# Check if lock screen timer already running
pID=$(pgrep -f "${0##*/}") # All PIDs matching lock-screen-timer name
PREVIOUS=$(echo "$pID" | grep -v ^"$$") # Strip out this running copy ($$$)
if [ "$PREVIOUS" != "" ]; then
    kill "$PREVIOUS"
    rm ~/.lock-screen-timer-remaining
    zenity --info --title="Lock screen timer already running" --text="Previous lock screen timer has been terminated."
fi

In this code snippet the line:

PREVIOUS=$(echo "$pID" | grep -v ^"$$") # Strip out this running copy 

uses the current running process ($$) to remove it (denoted by not -v) from the list of all processes running under the same name (lock-screen-timer in this case).

If there was a previous running copy the code kills it and delete the work file it was using.