使用-F
的选项lsof
,我可以指定打印哪些字段:
lsof -w -F pcfn
但是,输出分为多行,即每个字段一行:
p23022
csleep
fcwd
n/home/testuser
frtd
n/
ftxt
n/usr/bin/sleep
fmem
n/usr/lib/locale/locale-archive
fmem
n/usr/lib/x86_64-linux-gnu/libc-2.28.so
fmem
n/usr/lib/x86_64-linux-gnu/ld-2.28.so
f0
n/dev/pts/20
f1
n/dev/pts/20
f2
n/dev/pts/20
如何将自定义字段打印在一行上?
答案1
输出lsof -F
是可后处理的。
AFAICT,lsof
至少当在带有某种\x
符号的字段之一中找到反斜杠和控制字符(包括 TAB 和换行符)时,会呈现反斜杠和控制字符\\
(此处分别\t
表示\n
反斜杠、TAB 和换行符)²,因此应该可以使用以下方式格式化该输出每个打开的文件的制表符分隔值以及仍可进行后处理的值:
LC_ALL=C lsof -w -F pcfn | LC_ALL=C awk -v OFS='\t' '
{t = substr($0, 1, 1); f[t] = substr($0, 2)}
t == "n" {print f["p"], f["c"], f["f"], f["n"]}'
在你的样本上,这给出了:
23022 sleep cwd /home/testuser
23022 sleep rtd /
23022 sleep txt /usr/bin/sleep
23022 sleep mem /usr/lib/locale/locale-archive
23022 sleep mem /usr/lib/x86_64-linux-gnu/libc-2.28.so
23022 sleep mem /usr/lib/x86_64-linux-gnu/ld-2.28.so
23022 sleep 0 /dev/pts/20
23022 sleep 1 /dev/pts/20
23022 sleep 2 /dev/pts/20
之后lsof -w -F pcfn -a -d3 -p "$!"
:
perl -e '$0 = "a\nb\t"; sleep 999' 3> $'x\ny z\tw' &
这给出:
7951 a\nb\t 3 /home/stephane/x\ny z\tw
要从该输出中获取实际的文件n
ames,您仍然需要解码这些\x
序列。
注意使用该lsof
命令,您可以获得每个进程的每个线程的记录,但是您没有在字段列表中包含线程 ID,因此您不知道进程的哪个线程打开了文件,也许不是问题,因为同一进程的线程很少有不同的打开文件,但这仍然意味着您会在那里得到一些重复,您可以通过管道连接到 来消除这些重复LC_ALL=C sort -u
。您还可以使用 lsof 4.90 或更高版本的-Ki
.
您可能还想包括类型领域知道如何解释姓名场地。当打开的文件被删除时要小心lsof
追加 (deleted)
,而且据我所知,没有万无一失的方法可以消除它与名称结尾的文件的歧义 (deleted)
¹ 这并不一定意味着lsof
可以安全地处理包含换行符的文件名。例如,在 Linux 上,它仍然使用旧的/proc/net/unix
API 而不是 netlink 来检索有关 Unix/抽象域套接字的信息,并且如果套接字文件路径包含换行符,则该 API 会完全崩溃。通过绑定到具有伪造文件路径的套接字,人们可以很容易地lsof
认为进程打开了某个套接字,而不是另一个。
² 虽然它保留了非控制字符,但α
某些语言环境中某些字符的编码(例如 BIG5 中编码为 0xa3 0x5c)确实包含 0x5c 字节,这也是反斜杠的编码。因此,在这里,我们强制将语言环境设置为 C,以确保渲染 0x7f 以上的所有字节,以\xHH
避免后处理时出现意外。
答案2
Awk 是我最喜欢的锤子。
- 使用名称与字段匹配的变量,并将其初始化为“-”,因为并不总是提供值。
- 这取决于“n”是否是最后一个。看到它会触发打印输出,假设那时我们已经看到了所有字段。当然,打印顺序可以是任何内容。
lsof -w -F pcfn|awk '
BEGIN {
p=c=f=n="-"
}
# extract field & value for every line
{field=substr($0,1,1); value=substr($0,2)}
# assign value to matching variable name
/^p/{p=value}
/^c/{c=value}
/^f/{f=value}
/^n/{n=value
print p,c,f,n
p=c=f=n="-"
}
'
导致输出如下:
1 systemd cwd /
- - rtd /
- - txt /usr/lib/systemd/systemd
- - mem /lib64/libm-2.26.so
and so on...
答案3
p
如果您想要的只是将每个 PID ( field ) 或每个字段描述符 ( field f
)的输出一线。你可以尝试一下说明书上说的:
例如,
-F pcfn'' will select the process ID (`p'), command name (`c'), file descriptor (`f') and file name (`n') fields with an NL field terminator character;
-F pcfn0'' 选择带有 NUL (000) 字段终止符的相同输出。
lsof -w -F pcfn0
p
它确实为每个或f
组打印一行(包含 NUL) 。你可以用 less 看一下输出。这并不意味着所有字段都会出现,因为手册还指出:
Lsof 不会为每个进程或文件集生成所有字段,只会生成可用的字段。
但显然,-F选项用于将数据传输到其他节目。正如手册所述:
lsof 将生成可由其他程序解析的输出,而不是格式化显示。有关详细信息,请参阅 -F、选项说明和其他程序的输出部分。
因此,别无选择,lsof 的输出必须由其他程序处理。过去已经使用过 ac 程序或 awk 脚本。脚本目录中给出了如何正确处理 lsof 输出的 awk 示例:
/usr/share/doc/lsof/examples/list_fields.awk
https://github.com/Distrotech/lsof/blob/master/scripts/list_fields.awk
或者例如在。
而且,lsof 发行版中有一个lsof_fields.h
头文件,用于根据lsof
.
这似乎是你需要做的。这意味着解析每个字段(行)的第一个字符(提供的字段的标识符)并将它们全部连接到可以打印的单个表中。
这个答案已经展示了一种解析 lsof 输出的方法
答案4
awk
很棒,但我想提供一个不太知名的替代命令:pr
。我将该命令的文本分割功能与column
命令以漂亮的、可定制的格式显示输出。
lsof -w -F pcfn / | pr --column 4 --across | column
4
这样做的另一个好处是,只需将数字与您感兴趣的字段数量相匹配,就可以根据您感兴趣的输出轻松进行更改。
这是输出的示例:
p1682 cPM2 v5.2.0: God fcwd n/
frtd n/ ftxt n/home/aaron/.nvm
fmem n/usr/lib/x86_64- fmem n/usr/lib/x86_64-
fmem n/usr/lib/x86_64- fmem n/usr/lib/x86_64-
fmem n/usr/lib/x86_64- fmem n/usr/lib/x86_64-
pr
如果需要,您还可以指定自定义分隔符,为您提供如下所示的输出,您可以自定义您认为合适的方式。例子:
lsof -w -F pcfn / | head -20 | pr -ts' ' --column 4 -a
输出:
p1682 cPM2 v5.2.0: God fcwd n/
frtd n/ ftxt n/home/aaron/.nvm/versions/node/v16.14.2/bin/node
fmem n/usr/lib/x86_64-linux-gnu/libnss_dns-2.31.so fmem n/usr/lib/x86_64-linux-gnu/libresolv-2.31.so
fmem n/usr/lib/x86_64-linux-gnu/libc-2.31.so fmem n/usr/lib/x86_64-linux-gnu/libpthread-2.31.so
fmem n/usr/lib/x86_64-linux-gnu/libgcc_s.so.1 fmem n/usr/lib/x86_64-linux-gnu/libm-2.31.so