有時你會因一個檔案而迷惑,這個檔案可能是在你的資料夾中的一個未知型別的檔案,它可能是你的父母或者客戶給你的。不幸的是,你不知道它到底是一種什麼樣的檔案。在Mac上檔案是不帶有擴充名的,所以可能並沒有足夠的資訊來告訴你“Flongnozzle-2012”到底包含了什麼內容。然而終端(Terminal)可以為你提供一些便利,你可以使用一些內嵌的命令列工具來幫助你鑑別檔案。
識別檔案內容
對於這種情況,file命令恰好是我所需要的。file指令可以檢測一個檔案的內容然後試圖去弄清楚它是什麼。
1 2 3 |
% file launchHandler.m launchHandler.m: ASCII C++ program text |
當然,這其實是Objective-C檔案,不過終端已經非常接近了,終端將其鑑別為一個內有程式碼的檔案。“等等,MarkD(注:作者),它僅僅看下檔案的擴充名不就行了嗎?”file命令也支援這種情況,不過擴充名並不是必須的:
1 2 3 4 5 |
% cp launchHandler.m splunge % file splunge splunge: ASCII C++ program text |
沒有檔案擴充名,不過我們依然鑑別出了這個檔案是什麼。將file命令指向一個可能包含可執行程式碼的檔案或目錄,它會告訴你其內在的結構:
1 2 3 |
% file /bin/ls /bin/ls: Mach-O 64-bit executable x86_64 |
你可能會說,如果你有一個體積龐大的二進位制檔案(例如,原生的App)怎麼辦,下面是辦法:
1 2 3 4 5 |
% 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 3 |
% 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 7 |
% 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 12 |
% 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 11 |
% 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 5 6 |
% 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 11 |
% 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 22 |
% 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 |
@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 15 |
% ./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”是什麼的問題,因為這個檔案沒有擴充名或者其他有用的檔案型別的資訊。
其他工具
可用的命令列工具集非同尋常得多,因此我很可能落下了一個或者兩個或者更多其他的工具來幫助你辨別一個未知檔案。如果你有一個非常喜歡的小技巧,請留下一條評論!