-->

Pull fields/attributes from lsof (Linux command li

2020-07-18 20:15发布

问题:

With the recent move to Flash 10 (or maybe it was a distro choice), I and many others are no longer able to copy Flash videos from /tmp. I have, however, found a workaround in the following:

First, execute:

lsof | grep Flash

which should return output like this:

plugin-co 8935    richard   16w      REG        8,1   4139180       8220 /tmp/FlashXXq4KyOZ (deleted)

Note: You can see the problem here....the /tmp file has the file pointer released.

You are, however, able to grab the file by using the cp command thusly:

cp /proc/#/fd/# video.flv

where the 1st # is the process ID (8935) and the second if the next number (16, from 16w).

Currently, this works, but it requires a few manual steps. To automate this, I figure I could pull the PID and the fd number and insert them dynamically into the cp command.

My question is how do I pull the appropriate fields into variables? I know you can use $1, etc. for grabbing input arguments, but how do you retrieve outputs?

Note: I could use pidof plugin-container to find the PID, but I still need the other number (since it tells which specific flash video to save).

回答1:

The following command will return PIDs and FDs for all the files in /tmp that have filenames that begin with "Flash"

lsof -F pfn /tmp/Flash*

and the output will look something like this:

p16471
f16
n/tmp/FlashXXq4KyOZ
f17
n/tmp/FlashXXq4KyOZ
p26588
f16
n/tmp/FlashYYh3JwIW
f17

Where the field identifiers are p: PID, f: FD, n: NAME. The -F option is designed to make the output of lsof easy to parse.

Iterating over these and removing the field identifiers is trivial.

#!/bin/bash
c=-1
while read -r line
do
    case $line in
        f*)
            fds[pids[c]]+=${line:1}" "
            ;;
        n*)
            names[pids[c]]+=${line:1}" "
            ;;
        p*)
            pids[++c]=${line:1}
            ;;
    esac
done < <(lsof -F pfn -- /tmp/Flash*)

for ((i=0; i<=c; i++))
do
    for name in ${names[pids[i]]}
    do
        for fd in ${fds[pids[i]]}
        do
            echo "File: $name, Process ID: ${pids[i]}, File Descriptor: $fd"
        done
    done
done

Lines like this:

fds[pids[c]]+=${line:1}" "

accumulate file descriptors in a string stored in an array indexed by the PID. Doing this for file names will fail for filenames which contain spaces. That could be worked around if necessary.

The line is stripped of the leading field descriptor character by using a substring operator: ${line:1} starts at position one and includes the rest of the string so it drops character zero.

The second loop is just a demo to show iterating over the arrays.



回答2:

var=$(lsof | awk '/Flash/{gsub(/[^0-9]/,"",$4);print $2 FS $4};exit')
set -- $var
pid=$1
number=$2


回答3:

Completed Script:

#!/bin/sh

if [ $1 ]; then
    #lsof | grep Flash | awk '{print $2}' also works for PID
    pid=$(pidof plugin-container)
    file_num=$(lsof -p $pid | grep /tmp/Flash | awk '{print substr($4,1,2)}')

    cp /proc/$pid/fd/$file_num ~/Downloads/"$1".flv
else
    echo "Please enter video name as argument."
fi


回答4:

Avoid using lsof because it takes too long (>30 seconds) to return the path. The below .bashrc line will work with vlc, mplayer, or whatever you put in and return the path to the deleted temp file in milliseconds.

flashplay () {
          vlc $(stat -c %N /proc/*/fd/* 2>&1|awk -F[\`\'] '/lash/{print$2}')
}