Giambi
Giambi

Reputation: 23

Get PID of a timed process, along with the output of time

I have this line of code:

{ time cp $PWD/my_file $PWD/new_file ; } 2> my_log.log

I need to know how long it takes to execute the 'cp' command and I also need to get the PID of the 'cp'. I just want to print the PID of the 'cp' process and get the following in the my_log.log file:

<output of time> 

I have tried PID=$! but this does not provide PID of the cp process.

Upvotes: 2

Views: 724

Answers (5)

mklement0
mklement0

Reputation: 437052

  • First, you need to send your (timed) cp command to the background with a trailing &, so you can inspect the running processes after launching it. (I suspect you're already doing this, but it's not currently reflected in the question).

  • $!, the special variable that contains the PID of the most recently launched background job, in this case reflects the subshell that runs the time command, so we know that it is the parent process of the cp command. To get the (one and only, in this case) child process:

    • If your platform has the nonstandard pgrep utility (comes with many Linux distros and BSD/macOS platforms), use:
      pgrep -P $!

    • Otherwise, use the following POSIX-compliant approach:
      ps -o pid=,ppid= | awk -v ppid=$! '$2 == ppid { print $1 }'

To put it all together, using prgep for convenience:

# Send the timed `cp` command to the background with a trailing `&`
{ time cp "$PWD/my_file" "$PWD/new_file"; } 2> my_log.log &

# Get the `cp` comand's PID via its parent PID, $!
cpPid=$(pgrep -P $!)

Upvotes: 3

Nahuel Fouilleul
Nahuel Fouilleul

Reputation: 19305

To know approximately how long takes cp command you can check new file size

size=$(stat -c %s "${old_file}")
cp "${old_file}" "${new_file}" &
cp_pid=$!
while kill -0 ${cp_pid}; do
    cpsize=$(stat -c %s "${new_file}")
    echo elapsed time $(ps -p${cp_pid} -oetime=)
    echo $((100*cpsize/size)) % done so far..
    sleep 3
done

EDIT: following comment stat -c %s "${file}" can be replaced by du "${file}" it's POSIX and more suitable command (see man page).

Upvotes: 2

Riccardo Petraglia
Riccardo Petraglia

Reputation: 2003

Following what written in one of your comment above... the following is a proper answer:

The following code (just an example):

time (sleep 10 & echo -n "$!"; wait)

will return something like:

30406
real    0m10.009s
user    0m0.004s
sys 0m0.005s

In your case:

time (cp $PWD/old_file $PWD/new_file & echo -n "$!"; wait) &>  my_log.log

will do the job.

I find this solution quite elegant since it's a "one liner" despite the "completely negligible overhead" you got in the timing (the timing will be related to the entire subshell (also the echo and the wait). That the overhead is negligible is evident from the result of the sleep command.

The &> redirect stdout and stderr to the same file (so you do not need to specify the 1>&2).

Pay attention that doing

(time sleep 10 & echo -n "$!")

You will get the pid of the time process not sleep or cp in your case.

Upvotes: 0

Jack
Jack

Reputation: 6158

OK -- from the comments: "Contents of my_log.log will be PID of the cp command followed by the timing output of the cp command":

( time cp $PWD/my_file $PWD/new_file & 2>&1; echo $! ) > my_log.log 2>&1 

First, you need to use /usr/bin/time explicitly, and pass the options to append to an output file. Then, use pgrep on the name of the file you are copying (cp will get too many hits):

/usr/bin/time --output=my_log.log --append cp $PWD/my_file $PWD/new_file &  pgrep -f my_file > my_log.log

You may want to change the output format, because it's kinda ugly:

18400
0.00user 0.30system 0:02.43elapsed 12%CPU (0avgtext+0avgdata 2520maxresident)k
0inputs+496424outputs (0major+141minor)pagefaults 0swaps

Upvotes: 0

Jack
Jack

Reputation: 6158

The simplest one I can think of is

pgrep cp

Upvotes: 0

Related Questions