原文链接:File it away
有时你会因一个文件而迷惑,这个文件可能是在你的文件夹中的一个未知类型的文件,它可能是你的父母或者客户给你的。不幸的是,你不知道它到底是一种什么样的文件。在Mac上文件是不带有拓展名的,所以可能并没有足够的信息来告诉你“Flongnozzle-2012”到底包含了什么内容。然而终端(Terminal)可以为你提供一些便利,你可以使用一些内嵌的命令行工具来帮助你鉴别文件。
识别文件内容
对于这种情况,file命令恰好是我所需要的。file指令可以检测一个文件的内容然后试图去弄清楚它是什么。
1
2
|
% file launchHandler.m
launchHandler.m: ASCII C++ program text
|
当然,这其实是Objective-C文件,不过终端已经非常接近了,终端将其鉴别为一个内有代码的文件。“等等,MarkD(注:作者),它仅仅看下文件的拓展名不就行了吗?”file命令也支持这种情况,不过拓展名并不是必须的:
1
2
3
|
% cp launchHandler.m splunge
% file splunge
splunge: ASCII C++ program text
|
没有文件拓展名,不过我们依然鉴别出了这个文件是什么。将file命令指向一个可能包含可执行代码的文件或目录,它会告诉你其内在的结构:
1
2
|
% file /bin/ls
/bin/ls: Mach-O 64-bit executable x86_64
|
你可能会说,如果你有一个体积庞大的二进制文件(例如,原生的App)怎么办,下面是办法:
1
2
3
4
|
% file /Applications/Reason/Reason.app/Contents/MacOS/Reason
Reason.app/Contents/MacOS/Reason: Mach-O universal binary with 2 architectures
Reason.app/Contents/MacOS/Reason ( for architecture i386): Mach-O executable i386
Reason.app/Contents/MacOS/Reason ( for architecture x86_64): Mach-O 64-bit executable x86_64
|
将file指向一个图片文件来看看图片的一些信息:
1
2
|
% file Flongnozzle-2012
Flongnozzle-2012: PNG image data, 1932 x 904, 8-bit/color RGB, non-interlaced
|
哦等等,这里有一个终端的使用小技巧:将文件的图标从Finder中拖入终端窗口,这就相当于将你拖动的这个文件或文件夹的完整路径粘贴进去了。
进一步探索
有时file也不会让你满意,或者你可能想要知道关于文件的更多信息。一般来说,你总是可以通过QuickLook在Finder中浏览一下文件,如果这样不起作用,那么你可以使用hexdump命令来看看出文件的字节数,也可以传入参数-c来看看翻译成ASCII码之后的信息。
例如,回到我们之前的那个图片文件:
1
2
3
4
5
6
|
% hexdump -C Flongnozzle-2012 | head
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR|
00000010 00 00 07 8c 00 00 03 88 08 02 00 00 00 a2 e0 9b |................|
00000020 61 00 00 0c 45 69 43 43 50 49 43 43 20 50 72 6f |a...EiCCPICC Pro|
00000030 66 69 6c 65 00 00 48 0d ad 57 77 54 53 c9 17 be |file..H..WwTS...|
00000040 af 24 81 90 84 12 88 80 94 d0 9b 28 bd 4a ef 82 |.$.........(.J..|
|
在展示出来的数据区并没有太多有用信息,但是你可以看到它是PNG类型的,这已经是比较有用了,有些文件还含有更多字符串类型的内容,下面是对一个从Reason数码音频工作室获得的补丁文件使用hexdump指令得到的信息:
1
2
3
4
5
6
7
8
9
10
11
|
% hexdump -C CV-Spy--md.cmb
00000000 46 4f 52 4d 00 00 03 d8 50 54 43 48 43 41 54 20 |FORM....PTCHCAT |
00000010 00 00 00 04 52 45 46 53 43 4f 49 4e 00 00 00 06 |....REFSCOIN....|
00000020 bc 01 00 00 00 01 43 41 54 20 00 00 00 fc 44 45 |......CAT ....DE|
00000030 56 4c 46 4f 52 4d 00 00 00 f0 44 45 56 49 44 45 |VLFORM....DEVIDE|
00000040 53 43 00 00 00 47 bc 02 01 00 00 07 00 00 00 10 |SC...G..........|
00000050 00 00 00 12 43 56 20 56 61 6c 75 65 73 20 28 30 |....CV Values (0|
00000060 2d 3e 32 35 36 29 00 00 00 00 00 00 00 00 00 00 |->256)..........|
00000070 00 00 16 44 44 4c 20 44 69 67 69 74 61 6c 20 44 |...DDL Digital D|
00000080 65 6c 61 79 20 4c 69 6e 65 00 00 00 04 00 50 41 |elay Line.....PA|
...
|
如果你之前用过Reason,术语“CV Values”和“DDL Digital Delay Line”你一定不会陌生。
strings命令可以从文件中得到像字符串一样的字节序列:
1
2
3
4
5
6
7
8
9
10
|
% strings CV-Spy--md.cmb
FORM
PTCHCAT
REFSCOIN
CAT
DEVLFORM
DEVIDESC
CV Values (0->256)
DDL Digital Delay Line
...
|
属性值
属性列表(Property lists)是Mac和iOS系统上的一种标准类型的文件,将一些可以预知类型的数据有结构地组织起来就构成了我们的plist文件。在该系统上,一般你看到的属性列表文件都是被压缩成二进制格式的文件,这样在读取时会更快。用户的偏好设置就被存储为plist文件:
1
2
3
4
|
% pwd
/Users/markd/Library/Preferences
% file com.apple.iphonesimulator.plist
com.apple.iphonesimulator.plist: Apple binary property list
|
不幸的是,这个压缩之后的plist文件是一种非常难读的文件:
1
2
3
4
5
6
7
8
9
10
|
% hexdump -C com.apple.iphonesimulator.plist
00000000 62 70 6c 69 73 74 30 30 dc 01 02 03 04 05 06 07 |bplist00........|
00000010 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 |................|
00000020 18 5e 53 69 6d 75 6c 61 74 65 44 65 76 69 63 65 |.^SimulateDevice|
00000030 5f 10 2f 4e 53 57 69 6e 64 6f 77 20 46 72 61 6d |_./NSWindow Fram|
00000040 65 20 69 50 68 6f 6e 65 53 69 6d 75 6c 61 74 6f |e iPhoneSimulato|
00000050 72 57 69 6e 64 6f 77 2e 32 2e 30 2e 37 35 30 30 |rWindow.2.0.7500|
00000060 30 30 5f 10 2f 4e 53 57 69 6e 64 6f 77 20 46 72 |00_./NSWindow Fr|
00000070 61 6d 65 20 69 50 68 6f 6e 65 53 69 6d 75 6c 61 |ame iPhoneSimula|
...
|
幸运的是,有一个工具plutil指令能将这样二进制形式的数据转换为更接近与人类可读语言的形式:
(“!$”快捷键用来获取上一条指令中得最后一个参数)
Spotlight
在解读一个特定文件方面,OS可能做得比你想象得更好。Spotlight的工作就是为磁盘上的文件编制索引,通过查询元数据来让本地搜索更方便快捷。你可以通过mdls命令来获取这个元数据,所以你能够问问Spotlight对于这个文件都知道些什么:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
% mdls launchHandler.m
kMDItemContentCreationDate = 2014-07-02 19:22:02 +0000
kMDItemContentModificationDate = 2014-07-02 19:23:58 +0000
kMDItemContentType = "public.objective-c-source"
kMDItemContentTypeTree = (
"public.objective-c-source" ,
"public.source-code" ,
"public.plain-text" ,
"public.text" ,
"public.data" ,
"public.item" ,
"public.content"
)
...
kMDItemKind = "Objective-C Source"
kMDItemLastUsedDate = 2014-07-02 19:32:46 +0000
kMDItemLogicalSize = 1443
kMDItemPhysicalSize = 4096
kMDItemUseCount = 2
kMDItemUsedDates = (
"2014-07-02 10:00:00 +0000"
|
这里mdls告诉你这个文件是Objective-C代码的源文件,同时还有其他相同类型的标识符来描述数据,这确实是代码的源文件,并且是文本编辑格式。当然也有一些有趣的数据,例如这个文件到底在磁盘上占据着多少空间,以及这个文件由多少字节组成。
加载服务(Launch Services)
另一个维护系统数据库信息的工具是加载服务,它决定着哪个应用会打开哪个文件。双击一个文件来打开它?Finder会去询问加载服务。在命令行使用open指令来打开文件?系统也会去询问加载服务,由它来辨别到底由谁去打开文件。
lsappinfo指令就是一个使用加载服务(当然还有核心应用服务,Core Application Services)的工具,它能给你一些关于现在正在运行的应用的信息。这与辨别文件到底是什么无关,但是有了它,你就能了解到一些很cool的信息。试一下使用lsappinfo sharedmemory命令来获得共享内存的信息。或者使用lsappinfo visibleProcessList命令列出一组现可见的应用程序(顺序为按窗口从前到后)。
要获取加载服务的其他特性可以通过API,或者是lsregister。其中lsregister算是一个众所周知但却非官方的工具了。lsregister在LaunchServices框架下的Support目录内,而LaunchServices框架又被包含在CoreServices框架中。在你的机器上,很可能是下面这样的路径:
1
|
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister
|
lsregister主要被用于在OS系统上注册一个文件,这个文件将会由特定的应用程序来处理。不过你可以dump它的数据库来查看相关信息,使用:
1
|
% lsregister -dump > services-db.txt
|
(要运行该指令,你需要扩展你的PATH,加入Support目录)。
这条指令产生了大概61000行输出,因此这对于一个日常教程来说就有些太笨重了,不过浏览一下也是挺有趣的。
还有一些有用的功能来自于一个调用:LSCopyApplicationURLsForURL。给这个调用传入一个文件的路径作为参数,它会返回一组可以处理该文件的应用集合。它有不同的查询模式,像“能打开这个文件的所有的应用程序有哪些?”,或者“能编辑这个文件的所有应用程序有哪些?”加载服务并不像file指令一样去内探这个文件的结构。取而代之的是,它利用文件的拓展名、源代码、模糊匹配来找出合适的应用。
这里有一个小工具,它需要在命令行传入一个文件名。通过调用LSCopyApplicationURLsForURL,打印出匹配出来的应用程序的数组。你可以在这里找到这个源代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
@import Foundation;
@import CoreServices;
// clang -g -fobjc-arc -fmodules launchHandler.m -o launchHandler
int main (int argc, const char *argv[]) {
// Rudimentary argument checking.
if (argc != 2) {
printf ( "usage: %s filename\n" , argv[0]);
return -1;
}
const char *filename = argv[1];
// Get a string of the full path of the file, using realpath() as the workhorse
char pathbuffer[MAXPATHLEN];
char *fullpath = realpath (filename, pathbuffer);
if (fullpath == NULL) {
fprintf (stderr, "could not find %s\n" , filename);
return -1;
}
NSURL *url = [NSURL fileURLWithPath: @( fullpath )];
// Ask launch services for the different apps that it thinks could edit this file.
// This is usually a more useful list than what can view the file.
LSRolesMask roles = kLSRolesEditor;
CFArrayRef urls = LSCopyApplicationURLsForURL((__bridge CFURLRef)url, roles);
NSArray *appUrls = CFBridgingRelease(urls);
// Extract the app names and sort them for prettiness.
NSMutableArray *appNames = [NSMutableArray arrayWithCapacity: appUrls.count];
for (NSURL *url in appUrls) {
[appNames addObject: url.lastPathComponent];
}
[appNames sortUsingSelector: @selector(compare:)];
// Finally emit to the user.
for (NSString *appName in appNames) {
printf ( "%s\n" , appName.UTF8String);
}
return 0;
} // main
|
最有趣的部分是这里用的是realpath()库,通过调用它来将命令行参数中的文件名转换为完整路径(所以你不用担心如果用户到底传入的是一个相对路径,还是一个绝对路径,还是一个带~的路径),然后将它传入LSCopyApplicationURLsForURL,这里还使用了kLSRolesEditor,因为它可以返回最合理的一组应用程序。有时候选的应用程序也能给你一些线索来判断这个文件到底是什么。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
% ./launchHandler launchHandler.m
TextEdit.app
Xcode-4.6.app
Xcode-5.0.2.app
Xcode.app
Xcode6-Beta2.app
% ./launchHandler someGraphic.png
Acorn.app
ColorSync Utility.app
Preview.app
% ./launchHandler ./Flongnozzle-2012
%
|
不幸的是,它并不能解决“Flongnozzle”是什么的问题,因为这个文件没有拓展名或者其他有用的文件类型的信息。
其他工具
可用的命令行工具集非同寻常得多,因此我很可能落下了一个或者两个或者更多其他的工具来帮助你辨别一个未知文件。如果你有一个非常喜欢的小技巧,请留下一条评论!
翻译:Mr_cyz,于1-19日首发于CocoaChina
时间: 2024-10-27 08:21:57