(原文:File it Away 作者:Mark Dalrymple 譯者:Mr_cyz)
有時(shí)你會(huì)因一個(gè)文件而迷惑,這個(gè)文件可能是在你的文件夾中的一個(gè)未知類型的文件,它可能是你的父母或者客戶給你的。不幸的是,你不知道它到底是一種什么樣的文件。在Mac上文件是不帶有拓展名的,所以可能并沒有足夠的信息來告訴你“Flongnozzle-2012”到底包含了什么內(nèi)容。然而終端(Terminal)可以為你提供一些便利,你可以使用一些內(nèi)嵌的命令行工具來幫助你鑒別文件。
識(shí)別文件內(nèi)容
對(duì)于這種情況,file命令恰好是我所需要的。file指令可以檢測(cè)一個(gè)文件的內(nèi)容然后試圖去弄清楚它是什么。
% file launchHandler.m launchHandler.m: ASCII C++ program text當(dāng)然,這其實(shí)是Objective-C文件,不過終端已經(jīng)非常接近了,終端將其鑒別為一個(gè)內(nèi)有代碼的文件?!暗鹊龋琈arkD(注:作者),它僅僅看下文件的拓展名不就行了嗎?”file命令也支持這種情況,不過拓展名并不是必須的:
% cp launchHandler.m splunge % file splunge splunge: ASCII C++ program text沒有文件拓展名,不過我們依然鑒別出了這個(gè)文件是什么。將file命令指向一個(gè)可能包含可執(zhí)行代碼的文件或目錄,它會(huì)告訴你其內(nèi)在的結(jié)構(gòu):
% file /bin/ls /bin/ls: Mach-O 64-bit executable x86_64你可能會(huì)說,如果你有一個(gè)體積龐大的二進(jìn)制文件(例如,原生的App)怎么辦,下面是辦法:
% 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指向一個(gè)圖片文件來看看圖片的一些信息:
% file Flongnozzle-2012 Flongnozzle-2012: PNG image data, 1932 x 904, 8-bit/color RGB, non-interlaced
哦等等,這里有一個(gè)終端的使用小技巧:將文件的圖標(biāo)從Finder中拖入終端窗口,這就相當(dāng)于將你拖動(dòng)的這個(gè)文件或文件夾的完整路徑粘貼進(jìn)去了。
進(jìn)一步探索
有時(shí)file也不會(huì)讓你滿意,或者你可能想要知道關(guān)于文件的更多信息。一般來說,你總是可以通過QuickLook在Finder中瀏覽一下文件,如果這樣不起作用,那么你可以使用hexdump命令來看看出文件的字節(jié)數(shù),也可以傳入?yún)?shù)-c來看看翻譯成ASCII碼之后的信息。
例如,回到我們之前的那個(gè)圖片文件:
% 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..|在展示出來的數(shù)據(jù)區(qū)并沒有太多有用信息,但是你可以看到它是PNG類型的,這已經(jīng)是比較有用了,有些文件還含有更多字符串類型的內(nèi)容,下面是對(duì)一個(gè)從 Reason 數(shù)碼音頻工作室獲得的補(bǔ)丁文件使用hexdump指令得到的信息:
% 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,術(shù)語“CV Values”和“DDL Digital Delay Line”你一定不會(huì)陌生。
strings命令可以從文件中得到像字符串一樣的字節(jié)序列:
% strings CV-Spy--md.cmb FORM PTCHCAT REFSCOIN CAT DEVLFORM DEVIDESC CV Values (0->256) DDL Digital Delay Line ...
屬性值
屬性列表(Property lists)是Mac和iOS系統(tǒng)上的一種標(biāo)準(zhǔn)類型的文件,將一些可以預(yù)知類型的數(shù)據(jù)有結(jié)構(gòu)地組織起來就構(gòu)成了我們的plist文件。在該系統(tǒng)上,一般你看到的屬性列表文件都是被壓縮成二進(jìn)制格式的文件,這樣在讀取時(shí)會(huì)更快。用戶的偏好設(shè)置就被存儲(chǔ)為plist文件:
% pwd /Users/markd/Library/Preferences % file com.apple.iphonesimulator.plist com.apple.iphonesimulator.plist: Apple binary property list不幸的是,這個(gè)壓縮之后的plist文件是一種非常難讀的文件:
% 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| ...
幸運(yùn)的是,有一個(gè)工具plutil指令能將這樣二進(jìn)制形式的數(shù)據(jù)轉(zhuǎn)換為更接近與人類可讀語言的形式:
(“!$”快捷鍵用來獲取上一條指令中得最后一個(gè)參數(shù))
Spotlight
在解讀一個(gè)特定文件方面,OS可能做得比你想象得更好。Spotlight的工作就是為磁盤上的文件編制索引,通過查詢?cè)獢?shù)據(jù)來讓本地搜索更方便快捷。你可以通過mdls命令來獲取這個(gè)元數(shù)據(jù),所以你能夠問問Spotlight對(duì)于這個(gè)文件都知道些什么:
% 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告訴你這個(gè)文件是Objective-C代碼的源文件,同時(shí)還有其他相同類型的標(biāo)識(shí)符來描述數(shù)據(jù),這確實(shí)是代碼的源文件,并且是文本編輯格式。當(dāng)然也有一些有趣的數(shù)據(jù),例如這個(gè)文件到底在磁盤上占據(jù)著多少空間,以及這個(gè)文件由多少字節(jié)組成。
加載服務(wù)(Launch Services)
另一個(gè)維護(hù)系統(tǒng)數(shù)據(jù)庫信息的工具是加載服務(wù),它決定著哪個(gè)應(yīng)用會(huì)打開哪個(gè)文件。雙擊一個(gè)文件來打開它?Finder會(huì)去詢問加載服務(wù)。在命令行使用open指令來打開文件?系統(tǒng)也會(huì)去詢問加載服務(wù),由它來辨別到底由誰去打開文件。
lsappinfo指令就是一個(gè)使用加載服務(wù)(當(dāng)然還有核心應(yīng)用服務(wù),Core Application Services)的工具,它能給你一些關(guān)于現(xiàn)在正在運(yùn)行的應(yīng)用的信息。這與辨別文件到底是什么無關(guān),但是有了它,你就能了解到一些很cool的信息。試一下使用lsappinfo sharedmemory命令來獲得共享內(nèi)存的信息?;蛘呤褂胠sappinfo visibleProcessList命令列出一組現(xiàn)可見的應(yīng)用程序(順序?yàn)榘创翱趶那暗胶螅?
要獲取加載服務(wù)的其他特性可以通過API,或者是lsregister。其中l(wèi)sregister算是一個(gè)眾所周知但卻非官方的工具了。lsregister在LaunchServices框架下的Support目錄內(nèi),而LaunchServices框架又被包含在CoreServices框架中。在你的機(jī)器上,很可能是下面這樣的路徑:
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregisterlsregister主要被用于在OS系統(tǒng)上注冊(cè)一個(gè)文件,這個(gè)文件將會(huì)由特定的應(yīng)用程序來處理。不過你可以dump它的數(shù)據(jù)庫來查看相關(guān)信息,使用:
% lsregister -dump > services-db.txt
(要運(yùn)行該指令,你需要擴(kuò)展你的PATH,加入Support目錄)。
這條指令產(chǎn)生了大概61000行輸出,因此這對(duì)于一個(gè)日常教程來說就有些太笨重了,不過瀏覽一下也是挺有趣的。
還有一些有用的功能來自于一個(gè)調(diào)用:LSCopyApplicationURLsForURL。給這個(gè)調(diào)用傳入一個(gè)文件的路徑作為參數(shù),它會(huì)返回一組可以處理該文件的應(yīng)用集合。它有不同的查詢模式,像“能打開這個(gè)文件的所有的應(yīng)用程序有哪些?”,或者“能編輯這個(gè)文件的所有應(yīng)用程序有哪些?”加載服務(wù)并不像file指令一樣去內(nèi)探這個(gè)文件的結(jié)構(gòu)。取而代之的是,它利用文件的拓展名、源代碼、模糊匹配來找出合適的應(yīng)用。
這里有一個(gè)小工具,它需要在命令行傳入一個(gè)文件名。通過調(diào)用LSCopyApplicationURLsForURL,打印出匹配出來的應(yīng)用程序的數(shù)組。你可以在這里找到這個(gè)源代碼。
@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()庫,通過調(diào)用它來將命令行參數(shù)中的文件名轉(zhuǎn)換為完整路徑(所以你不用擔(dān)心如果用戶到底傳入的是一個(gè)相對(duì)路徑,還是一個(gè)絕對(duì)路徑,還是一個(gè)帶~的路徑),然后將它傳入LSCopyApplicationURLsForURL,這里還使用了kLSRolesEditor,因?yàn)樗梢苑祷刈詈侠淼囊唤M應(yīng)用程序。有時(shí)候選的應(yīng)用程序也能給你一些線索來判斷這個(gè)文件到底是什么。
% ./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”是什么的問題,因?yàn)檫@個(gè)文件沒有拓展名或者其他有用的文件類型的信息。
其他工具
可用的命令行工具集非同尋常得多,因此我很可能落下了一個(gè)或者兩個(gè)或者更多其他的工具來幫助你辨別一個(gè)未知文件。如果你有一個(gè)非常喜歡的小技巧,請(qǐng)留下一條評(píng)論!