2

How can I start a program and write its output to a log file, where the log file contains the PID in its name? I tried

program_a > log_$! 

which doesn't work since $! is the PID of the last program and `program_a' has not finished when the log file is created.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
greole
  • 239
  • 1
  • 4
  • 10

4 Answers4

2

If you are talking about a script I think this is what you are after...

#!/bin/bash

exec 1>test_$$.txt 
echo "Hello \$\$ == $$" 

ps

which gave this output

$ cat test_20000.txt 
Hello $$ == 20000
  PID TTY           TIME CMD
18651 ttys000    0:00.06 -bash
20000 ttys000    0:00.00 /bin/bash ./execredir.sh
Rui F Ribeiro
  • 55,929
  • 26
  • 146
  • 227
Dude named Ben
  • 231
  • 2
  • 7
1

You can't do that from the shell that way because the commandline is created before the program is even called. You have essentially two options:

1) Create the filename and write to it from within the program (easy if it is a shell script)

2) Create a named pipe, background the process and then redirect the pipe to a file. Like

mkfifo "tmp.log"  
program_a > "tmp.log" &
cat "tmp.log" > "log_$!"
orion
  • 12,302
  • 2
  • 31
  • 41
1

This doesn't answer the question directly, but I would question why I need to have a file with the pid in the name. If it is simply a unique filename that you are looking for, then there are more robust ways to do this. Most Unices have a mktemp command (unfortunately this is not POSIX though). Using GNU mktemp, you could do:

tmp_file=$(mktemp --tmpdir=. log_XXXXXXXXXX)
program_a >"$tmp_file"

If you have to access the files at a later date, then it may be useful to include the date/time in the filename:

log_file=$(mktemp --tmpdir=. log_"$(date +%F_%T)".XXX)
program_a >"$log_file"

If you are looking to ensure that only one instance of a specific process is running, then on Linux you can use flock:

(
  flock -n 9 || { echo "program_a already running"; exit 1; }
  program_a
) 9>/var/lock/program_a

Otherwise, if you are looking to have another program read the output of program_a while it is still running, then using a file is surely a method of last resort. Much better to use a pipe or a named pipe as per orion's answer.

Graeme
  • 33,607
  • 8
  • 85
  • 110
1

You can't know the PID until the process has started. So you need to first start the process, then create the log file, then execute the program you want to execute (exec replaces the calling shell with the given program, it doesn't fork a new process).

sh -c 'exec program_a >log_$$'

$! is the PID of the last program started in the background and cannot help you here.

Alternatively, you could create the log file under a temporary name, start the program, and then rename the log file, but it's needlessly more complicated.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • Is that possible in combination with `nohup`? When I put the `nohup` command either between `exec` and `program_a` or before `sh`, the value of `$$` equals to (`PID` of the `program_a instance`) - 1 – stofl Feb 27 '17 at 22:24
  • @stofl Sure, you can run `nohup sh …`. And indeed `$$` is the PID of the new program instance, that was the point of the question. What's the problem? – Gilles 'SO- stop being evil' Feb 27 '17 at 22:33
  • When I run `sh -c 'nohup ./test >log_$$' &` it returns me (right now) `9096`. When I call `ps ax | grep test`, the started process has the PID `9097`. I think, `nohup` itself is a program that starts a new process detached from the shell and has a different PID because of that. And I think, this difference if `1` is not really reliable. – stofl Mar 05 '17 at 23:12
  • 1
    @stofl You need `exec`, otherwise `nohup` is a child of the shell and so has a different PID. (Some shells optimize this but not all.) `sh -c 'exec nohup ./test >log_$$'` – Gilles 'SO- stop being evil' Mar 06 '17 at 00:33
  • Uh! Yes, that works, thank you. Seems that I have forgotten to write that `exec` in both of my tests! – stofl Mar 06 '17 at 18:16