在回答我关于 SO 的一个问题时,给出路径的挂载点,一位受访者建议使用stat
来获取与给定路径的卷关联的设备名称。这在 Linux 上运行良好,但在 Mac OS X 10.4 上会产生奇怪的结果。对于我的系统,自由度和山给:
cas cas$ df
Filesystem 512-blocks Used Avail Capacity Mounted on
/dev/disk0s3 58342896 49924456 7906440 86% /
devfs 194 194 0 100% /dev
fdesc 2 2 0 100% /dev
<volfs> 1024 1024 0 100% /.vol
automount -nsl [166] 0 0 0 100% /Network
automount -fstab [170] 0 0 0 100% /automount/Servers
automount -static [170] 0 0 0 100% /automount/static
/dev/disk2s1 163577856 23225520 140352336 14% /Volumes/Snapshot
/dev/disk2s2 409404102 5745938 383187960 1% /Volumes/Sparse
cas cas$ mount
/dev/disk0s3 on / (local, journaled)
devfs on /dev (local)
fdesc on /dev (union)
<volfs> on /.vol
automount -nsl [166] on /Network (automounted)
automount -fstab [170] on /automount/Servers (automounted)
automount -static [170] on /automount/static (automounted)
/dev/disk2s1 on /Volumes/Snapshot (local, nodev, nosuid, journaled)
/dev/disk2s2 on /Volumes/Sparse (asynchronous, local, nodev, nosuid)
但是尝试从挂载点获取设备:
cas cas$ df | grep -e/
| awk '{print $NF}'
| while read line; do echo $line $(stat -f"%Sdr" $line); done
/ disk0s3r
/dev ???r
/dev ???r
/.vol ???r
/Network ???r
/automount/Servers ???r
/automount/static ???r
/Volumes/Snapshot disk2s1r
/Volumes/Sparse disk2s2r
在这里,我正在将从中抓取的每个挂载点自由度到统计,输出结果“%特别提款权”格式字符串,应该是设备名称:参见 stat(1) 手册页:
特殊输出说明符 S 可用于指示 输出(如果适用)应采用字符串格式。可以使用 结合: ... dr 显示实际的设备名称。
发生了什么事?这是统计或者一些达尔文 VFS 怪异现象?
后记根据安德鲁·麦格雷戈的说法,尝试传球“%Sd”到统计更多怪异之处。它列出了一些来自 CWD 的看似任意的文件子集……
答案1
首先,作为统计格式,“%Sdr”被解释为d
字段选择器,由说明符修改S
,后跟一个小写的 R 字符。
如果在 的描述下用“d, r”代替“dr”,那么手册页可能会更清楚。S
和d
都是r
单独的字段选择器。r
从中选择设备编号统计(2)info(即 st_rdev;仅在统计设备条目(即 /dev 下的条目)时有用)。d
选择保存统计条目(即 st_dev)的设备的设备号。它打印的数字是主号码和次号码的组合,如下所示ls(主要 << 24 | 次要)。
S
是一个修饰符,可以应用于d
、r
和其他几个字段选择器。当应用于d
或 时r
,它会尝试打印设备的名称而不是其原始编号。某些设备(如虚拟文件系统设备)没有正确的名称,因此它会打印???
(如果打印出来可能会更好<major>,<minor>
)。这并不意味着这些文件系统没有设备,只是它们的设备没有像“disk0s3”这样的正常名称。
“shell 怪异之处”(“显然是来自 CWD 的任意文件子集”)是由于缺少引号造成的。shell 看到(未加引号的)“???”结果并将其扩展为 glob 模式。如果 cwd 包含任何恰好三个字节(字符,取决于语言环境?)的条目,shell 将用 glob 模式替换这些条目。行为(引号和 glob 扩展)因 shell 而异,通常可以通过各种 shell 选项进行修改。
您可以像这样修改原始命令以避免通配符和尾随的“r”:
df | grep -e/ | awk '{print $NF}' | while read line; do echo "$line" "$(stat -f%Sd "$line"); done
但我可能会这样写:
df | tail +2 | awk '{print $NF}' | xargs stat -f'%N %Sd %d'
- 请注意,如果任何挂载的文件系统的挂载点名称中有空格,此操作将失败。山或者自由度非常容易解析(两者都有可能包含几乎任意字符串的字段)。
通过该输出(名称、设备编号、设备名称),您也许可以更好地了解正在发生的事情。
或者,也许您除了想查看原始设备编号之外,还想查看主编号和次编号(将它们与您在 (eg) 中看到的内容进行比较ls -l /dev/disk0s3
):
df | tail +2 | awk '{print $NF}' | xargs stat -f'%N %Sd %d' |
awk 'BEGIN{f=2^24} {$(NF+1) = int($NF/f) "," ($NF%f) } 1'
这是一个小型 C 程序,可以替换有问题的“df | head | awk”管道。
当然,这样的 C 程序可以自己完成剩下的工作,但是如果有一个独立的程序可以输出以 NUL 终止的挂载点就更好了。
mountz | xargs -0 stat -f'%N %Sd %d' |
awk 'BEGIN{f=2^24} {$(NF+1) = int($NF/f) "," ($NF%f) } 1'
代码:
#include <sys/mount.h>
#include <stdio.h>
#include <stdlib.h>
/* usage: mountz | xargs -0 command_for_each_mount_point */
int main(int argc, const char *argv[]) {
struct statfs *buf;
int flags = MNT_NOWAIT, num_fs, num_stat, i;
unsigned bufsz;
num_fs = getfsstat(NULL, 0, flags);
if (num_fs < 0) {
perror("unable to count mounted filesystems: getfsstat");
exit(1);
}
bufsz = sizeof(*buf) * num_fs;
buf = malloc(bufsz);
if (!buf) {
perror("unable to allocate %u statfs structs");
exit(1);
}
fprintf(stderr, "p=%p\n", buf);
num_stat = getfsstat(buf, bufsz, flags);
if (num_stat < 0) {
perror("unable to getfsstat");
exit(1);
}
if (num_stat != num_fs) {
fprintf(stderr, "Hmm, expected %u, got %d.\n", num_fs, num_stat);
}
for (i = 0; i < num_stat; i++) {
fprintf(stdout, "%s%c", buf[i].f_mntonname, 0);
}
}
答案2
因此虚拟文件系统没有设备 - 我没有尝试过这些文件系统。对于 /Network、/automount/Servers 或 /automount/static 来说这不是问题,因为它们不应该包含文件。但 /dev 很有趣。
答案3
df | grep -e/ | awk '{print $NF}' | while read line; do echo $line $(stat -f"%Sd" $line); done
/ disk0s2
/dev XGS bin tmp
/net XGS bin tmp
/home XGS bin tmp
我想说,达尔文 VFS 很奇怪。