Bash имеет несколько внутренних переменных, и в этой статье мы рассмотрим переменную $$. Что означает эта переменная? Как ее можно использовать?
$$ — это внутренняя переменная Bash, которая содержит идентификатор процесса (PID) оболочки, запускающей ваш скрипт. Иногда переменную $$ путают с переменной $BASHPID, которая содержит PID текущей оболочки Bash.
Давайте рассмотрим несколько примеров, которые прояснят, что такое $$. Мы также увидим, как можно использовать эту переменную в ваших скриптах Bash.
Давайте начнем!
Значение $$ в Bash
Как уже упоминалось выше…
Переменная $$ в Bash содержит идентификатор процесса (PID) оболочки, запускающей ваш скрипт.
Чтобы продемонстрировать эту концепцию, мы создадим очень простой скрипт, который использует команду grep для вывода собственного PID. Затем скрипт также будет использовать команду echo для вывода значения переменной $$.
Таким образом, мы сможем сравнить их значения.
#!/bin/bash
SCRIPT_NAME=$(basename $0)
echo "The name of this script is $SCRIPT_NAME"
echo "This is the output of the ps command:"
ps -aef | grep $SCRIPT_NAME
echo "The value of \$\$ is $$"
Вывод нашего скрипта оболочки:
$./bash_pid.sh
The name of this script is bash_pid.sh
This is the output from the ps command:
myuser 32385 31721 0 17:14 pts/0 00:00:00 /bin/bash./bash_pid.sh
myuser 32388 32385 0 17:14 pts/0 00:00:00 grep bash_pid.sh
The value of $$ is 32385
Вы можете видеть, что значение $$ совпадает с PID процесса Bash, который запускает текущий скрипт (32385).
Третий столбец вывода ps — это PPID (идентификатор родительского процесса), который в данном случае является PID текущей оболочки Bash (или процесса Bash):
$ ps -aef | grep 31721
myuser 31721 31323 0 16:31 pts/0 00:00:00 bash
myuser 32437 31721 0 17:17 pts/0 00:00:00 ps -aef
myuser 32438 31721 0 17:17 pts/0 00:00:00 grep --color=auto 31721
Вы можете увидеть то же значение, используя команду echo в текущей оболочке:
$ echo $$
31721
Разница между $$ и BASHPID
Я просматривал руководство Bash, чтобы найти что-нибудь еще о $$.
$ man bash
И вот что я нашел:
Давайте посмотрим, содержит ли переменная оболочки BASHPID то же значение, что и $$:
$ echo $$
31721
$ echo $BASHPID
31721
В данном случае это так (как мы уже видели, это PID текущей оболочки).
Теперь давайте добавим следующую строку в конец предыдущего скрипта и перезапустим скрипт:
echo "The value of \$BASHPID is $BASHPID"
Вывод:
$./bash_pid.sh
The name of this script is bash_pid.sh
This is the output from the ps command:
myuser 32495 31721 0 17:20 pts/0 00:00:00 /bin/bash./bash_pid.sh
myuser 32498 32495 0 17:20 pts/0 00:00:00 grep bash_pid.sh
The value of $$ is 32495
The value of $BASHPID is 32495
$BASHPID также возвращает PID оболочки Bash, используемой для выполнения текущего скрипта.
Итак, подведем итоги: значение переменных $$ и BASHPID равно:
- PID текущей оболочки, если посмотреть на его значение непосредственно в оболочке.
- PID оболочки, используемой для запуска текущего скрипта при выводе его значения внутри скрипта.
Значение $$ и BASHPID в подоболочках
Теперь сравним значение $$ и BASHPID, если вы используете подоболочки.
#!/bin/bash
echo "\$\$ outside the subshell: $$"
echo "\$BASHPID outside the subshell: $BASHPID"
(
echo "\$\$ inside the subshell: $$"
echo "\$BASHPID inside the subshell: $BASHPID"
)
Вот вывод скрипта:
$./subshell.sh
$$ outside the subshell: 3145
$BASHPID outside the subshell: 3145
$$ inside the subshell: 3145
$BASHPID inside the subshell: 3146
Таким образом, при использовании подоболочки значение $$ отличается от BASHPID.
Давайте воспользуемся командой ps, чтобы понять, как PID, возвращаемые $$ и BASHPID в скрипте, соотносятся с процессами, запущенными в нашей системе.
Сценарий становится таким:
#!/bin/bash
echo "\$\$ outside the subshell: $$"
echo "\$BASHPID outside the subshell: $BASHPID"
ps -aef | grep $$
(
echo "\$\$ inside the subshell: $$"
echo "\$BASHPID inside the subshell: $BASHPID"
ps -aef | grep $$
ps -aef | grep $BASHPID
)
В выводе вы можете видеть, что при выполнении в подоболочке $$ возвращает родительский PID подоболочки.
$./subshell.sh
$$ outside the subshell: 32586
$BASHPID outside the subshell: 32586
myuser 32586 31721 0 17:29 pts/0 00:00:00 /bin/bash./subshell.sh
myuser 32587 32586 0 17:29 pts/0 00:00:00 ps -aef
myuser 32588 32586 0 17:29 pts/0 00:00:00 grep 32586
$$ inside the subshell: 32586
$BASHPID inside the subshell: 32589
myuser 32586 31721 0 17:29 pts/0 00:00:00 /bin/bash./subshell.sh
myuser 32589 32586 0 17:29 pts/0 00:00:00 /bin/bash./subshell.sh
myuser 32591 32589 0 17:29 pts/0 00:00:00 grep 32586
myuser 32593 32589 0 17:29 pts/0 00:00:00 grep 32593
Команда Bash и переменная $$
Я знаю, это может быть сложная тема для понимания…
По этой причине я хочу рассмотреть как можно больше примеров, чтобы дать вам ясность относительно переменной $$ в Bash.
Взгляните на команды ниже:
$ echo $$
10267
$ bash -c 'echo $$'
10363
В чем разница между этими двумя командами?
Чтобы понять это, мы также можем запустить ps как часть каждой команды:
$ echo $$; ps -aef | grep $$
31721
myuser 31721 31323 0 16:31 pts/0 00:00:00 bash
myuser 32726 31721 0 17:38 pts/0 00:00:00 ps -aef
myuser 32727 31721 0 17:38 pts/0 00:00:00 grep --color=auto 31721
$ bash -c 'echo $$; ps -aef | grep $$'
32731
myuser 32731 31721 0 17:39 pts/0 00:00:00 bash -c echo $$; ps -aef | grep $$
myuser 32732 32731 0 17:39 pts/0 00:00:00 ps -aef
myuser 32733 32731 0 17:39 pts/0 00:00:00 grep 32731
Вот несколько вещей, которые мы можем наблюдать:
- Мы использовали точку с запятой (;) для последовательного выполнения двух команд Linux (
echo
иps
). - Значение $$ в текущей оболочке возвращает PID оболочки Bash (как мы видели ранее).
- Команда
bash -c
выполняет команду в кавычках в новой оболочке. Родитель новой оболочки — наша начальная оболочка. - Во второй команде переменная $$ содержит PID новой оболочки, выполняющей команды.
$$ против Mktemp для временных каталогов
Иногда переменная $$ используется для генерации временных имен файлов в Linux.
Предположим, например, что вы хотите создать скрипт резервного копирования, который выполняется ежедневно и записывает в файл список файлов, включенных в каждую резервную копию.
Вы можете использовать переменную $$ для генерации имени файла для отчета о резервном копировании, учитывая, что $$ содержит PID скрипта при каждом его выполнении.
#!/bin/bash
FILENAME=daily_backup.$$
touch $FILENAME
После трехкратного запуска скрипта мы увидим в текущем каталоге следующие файлы:
$./backup.sh
$./backup.sh
$./backup.sh
$ ls
backup.sh daily_backup.855 daily_backup.857 daily_backup.859
У нас есть три разных файла, поскольку каждый раз при выполнении скрипта ему назначается разный PID.
В качестве альтернативы Linux предоставляет команду mktemp
.
$ mktemp
/tmp/tmp.elqilKRddX
Как вы можете видеть, он генерирует случайный временный файл в каталоге /tmp.
Если вы хотите создать временный файл в текущем каталоге с помощью mktemp, вы можете сделать это, передав флаг tmpdir
:
$ mktemp --tmpdir=.
./tmp.V5intPTQHd
Завершение скрипта Bash с помощью $$
Я хочу завершить этот урок интересным экспериментом…
Я хотел бы использовать переменную $$ для написания скрипта, который убивает сам себя. Это то, чего вы, возможно, никогда не сделаете, но это даст вам лучшее понимание того, как работает Bash.
Мы знаем, что переменная $$ содержит PID оболочки, выполняющей наш скрипт.
Итак, что произойдет, если мы уничтожим PID, возвращаемый $$ внутри скрипта?
Давайте узнаем!
#!/bin/bash
echo "The PID of this script is $$"
echo "Killing this script using \$\$…"
kill $$
echo "This should never be executed"
Мы передаем PID текущего скрипта команде kill
, чтобы завершить его.
Я добавил команду echo в конец скрипта, которая никогда не должна выполняться, если скрипт успешно завершен.
$./self_kill.sh
The PID of this script is 17285
Killing this script using $$…
Terminated: 15
Оболочка отправляет сообщение «Terminated» на стандартный вывод. Это сообщение зависит от того, как мы завершаем процесс.
Какой статус выхода возвращает этот скрипт после завершения?
$ echo $?
143
Код выхода Bash 143 возникает из-за того, что скрипт завершается фатальным сигналом, и в этих случаях Bash возвращает код выхода, равный 128+N.
В данном случае N равно 15, что, если посмотреть на вывод kill -l, представляет SIGTERM.
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGEMT 8) SIGFPE
9) SIGKILL 10) SIGBUS 11) SIGSEGV 12) SIGSYS
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGURG
17) SIGSTOP 18) SIGTSTP 19) SIGCONT 20) SIGCHLD
21) SIGTTIN 22) SIGTTOU 23) SIGIO 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGINFO 30) SIGUSR1 31) SIGUSR2
Если мы используем kill -9 в нашем скрипте, мы увидим немного другой вывод:
$./self_kill.sh
The PID of this script is 18102
Killing this script using $$…
Killed: 9
$ echo $?
137
В этом случае статус выхода $? равен 128+9 = 137 (SIGKILL).
Заключение
Надеюсь, вы нашли в этом уроке то, что искали, и он дал вам достаточно ясности для работы с переменной $$ в Bash.
А как вы планируете использовать $$ в своих сценариях?