从lsof(Linux命令行)中提取字段/属性

3
最近升级到Flash 10(或者这是一个发行版的选择),我和许多人无法再从/tmp中复制Flash视频。然而,我已经找到了以下的解决方法:
首先,执行:
lsof | grep Flash

应该返回如下输出:

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

注意:您可以在此处看到问题....../tmp文件已释放文件指针。
但是,您可以使用以下方式使用cp命令获取文件:
cp /proc/#/fd/# video.flv

第一个#号是进程ID (8935),第二个#号是接下来的数字(从16w中取得的16)。

目前,这个方法能够实现,但需要手动操作几个步骤。为了自动化这个过程,我想我可以提取PID和fd数,并将它们动态地插入到cp命令中。

我的问题是,如何将相应的字段提取到变量中?我知道你可以使用 $1等来获取输入参数,但是如何检索输出呢?

注意:我可以使用 pidof plugin-container 来查找PID,但我仍然需要另一个数字(因为它指示保存哪个特定的 Flash 视频)。

4个回答

5
以下命令将返回所有文件名以“Flash”开头的/tmp目录中的进程ID和文件描述符(PID和FD):
lsof -F pfn /tmp/Flash*

并且输出的结果大致如下:
p16471
f16
n/tmp/FlashXXq4KyOZ
f17
n/tmp/FlashXXq4KyOZ
p26588
f16
n/tmp/FlashYYh3JwIW
f17

字段标识符为p: PID,f: FD,n: NAME。 -F选项旨在使lsof的输出易于解析。

遍历这些并删除字段标识符很容易。

#!/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

像这样的行:

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

在一个由PID索引的数组中存储文件描述符的字符串。对于包含空格的文件名,这种方法将会失败。如果必要可以解决这个问题。

通过使用子字符串运算符去掉行首的字段描述符字符:${line:1}从位置一开始并包括剩余的字符串,因此它删除了第零个字符。

第二个循环只是一个演示,展示如何遍历数组。


这很有趣...我感谢您展示如何使用内置的lsof开关。不幸的是,由于/tmp中的文件始终被删除,因此此脚本将无法工作。以前,您可以复制/粘贴文件,但现在它已被取消链接,唯一检索它的方法是通过/proc。这就是为什么我必须将lsof的输出导入grep中。这允许-F开关仅打印名称(n参数)。但再次说明,这是一个很好的学习经验,我感谢您。 - Richard Martinez
@JohnSmith:你仍然可以使用-F选项。在输出中迭代,将“p”和“f”条目保存在找到它们时,当您找到正在寻找的“n”条目时,您就拥有了所需的内容。如果在找到您正在寻找的“n”之前找到新的“p”条目,则删除先前的条目并继续。顺便说一下,在lsof发行版中包含了示例AWK和Perl脚本。 - Dennis Williamson

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

谢谢!最终我使用了以下命令:pid = $(lsof | grep Flash | awk '{print $ 2}') file_num = $(lsof | grep Flash | awk '{print substr($ 4,1,2} )'}) - Richard Martinez
@Richard:请将您的解决方案发布为自己的答案,这样更容易找到。 - Paŭlo Ebermann

1

已完成脚本:

#!/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

0
避免使用lsof,因为它需要太长时间(>30秒)才能返回路径。下面的.bashrc行将与vlc、mplayer或您放入其中的任何内容一起工作,并在毫秒内返回已删除的临时文件的路径。
flashplay () {
           vlc $(stat -c%N /proc/ * /fd / * 2>&1 | awk -F [\`\'] '/lash/{print$2}')
}


哇,我喜欢这个的速度。我将vlc更改为cp并添加了一个目标(通常是点)。唯一的问题是它会复制/proc/pid/fd/中的所有文件,但我真的只想过滤一个文件。请参见我的上面的答案以获取脚本的当前形式。 - Richard Martinez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接