識別Mac電腦上檔案的命令列技巧
(原文: File it Away 作者:Mark Dalrymple 譯者: Mr_cyz )
有時你會因一個檔案而迷惑,這個檔案可能是在你的資料夾中的一個未知型別的檔案,它可能是你的父母或者客戶給你的。不幸的是,你不知道它到底是一種什麼樣的檔案。在Mac上檔案是不帶有拓展名的,所以可能並沒有足夠的資訊來告訴你“Flongnozzle-2012”到底包含了什麼內容。然而終端(Terminal)可以為你提供一些便利,你可以使用一些內嵌的命令列工具來幫助你鑑別檔案。
識別檔案內容
對於這種情況, file 命令恰好是我所需要的。file指令可以檢測一個檔案的內容然後試圖去弄清楚它是什麼。
% file launchHandler.mlaunchHandler.m: ASCII C++ program text當然,這其實是Objective-C檔案,不過終端已經非常接近了,終端將其鑑別為一個內有程式碼的檔案。“等等,MarkD(注:作者),它僅僅看下檔案的拓展名不就行了嗎?”file命令也支援這種情況,不過拓展名並不是必須的:
% cp launchHandler.m splunge% file splungesplunge: ASCII C++ program text沒有檔案拓展名,不過我們依然鑑別出了這個檔案是什麼。將file命令指向一個可能包含可執行程式碼的檔案或目錄,它會告訴你其內在的結構:
% file /bin/ls/bin/ls: Mach-O 64-bit executable x86_64你可能會說,如果你有一個體積龐大的二進位制檔案(例如,原生的App)怎麼辦,下面是辦法:
% file /Applications/Reason/Reason.app/Contents/MacOS/ReasonReason.app/Contents/MacOS/Reason: Mach-O universal binary with 2 architecturesReason.app/Contents/MacOS/Reason (for architecture i386): Mach-O executable i386Reason.app/Contents/MacOS/Reason (for architecture x86_64): Mach-O 64-bit executable x86_64將file指向一個圖片檔案來看看圖片的一些資訊:
% file Flongnozzle-2012Flongnozzle-2012: PNG image data, 1932 x 904, 8-bit/color RGB, non-interlaced哦等等,這裡有一個終端的使用小技巧:將檔案的圖示從Finder中拖入終端視窗,這就相當於將你拖動的這個檔案或資料夾的完整路徑貼上進去了。
進一步探索
有時file也不會讓你滿意,或者你可能想要知道關於檔案的更多資訊。一般來說,你總是可以通過QuickLook在Finder中瀏覽一下檔案,如果這樣不起作用,那麼你可以使用 hexdump 命令來看看出檔案的位元組數,也可以傳入引數-c來看看翻譯成ASCII碼之後的資訊。
例如,回到我們之前的那個圖片檔案:
% hexdump -C Flongnozzle-2012 | head0000000089 50 4e 47 0d 0a 1a 0a00 00 00 0d 49 48 44 52|.PNG........IHDR|0000001000 00 07 8c 00 00 03 8808 02 00 00 00 a2 e0 9b|................|0000002061 00 00 0c 45 69 43 4350 49 43 43 20 50 72 6f|a...EiCCPICC Pro|0000003066 69 6c 65 00 00 48 0dad 57 77 54 53 c9 17 be|file..H..WwTS...|00000040af 24 81 90 84 12 88 8094 d0 9b 28 bd 4a ef 82|.$.........(.J..|在展示出來的資料區並沒有太多有用資訊,但是你可以看到它是PNG型別的,這已經是比較有用了,有些檔案還含有更多字串型別的內容,下面是對一個從 Reason 數碼音訊工作室獲得的補丁檔案使用hexdump指令得到的資訊:
% hexdump -C CV-Spy--md.cmb0000000046 4f 52 4d 00 00 03 d850 54 43 48 43 41 54 20|FORM....PTCHCAT |0000001000 00 00 04 52 45 46 5343 4f 49 4e 00 00 00 06|....REFSCOIN....|00000020bc 01 00 00 00 01 43 4154 20 00 00 00 fc 44 45|......CAT ....DE|0000003056 4c 46 4f 52 4d 00 0000 f0 44 45 56 49 44 45|VLFORM....DEVIDE|0000004053 43 00 00 00 47 bc 0201 00 00 07 00 00 00 10|SC...G..........|0000005000 00 00 12 43 56 20 5661 6c 75 65 73 20 28 30|....CV Values (0|000000602d 3e 32 35 36 29 00 0000 00 00 00 00 00 00 00|->256)..........|0000007000 00 16 44 44 4c 20 4469 67 69 74 61 6c 20 44|...DDL Digital D|0000008065 6c 61 79 20 4c 69 6e65 00 00 00 04 00 50 41|elay Line.....PA|...如果你之前用過Reason,術語“CV Values”和“DDL Digital Delay Line”你一定不會陌生。
strings命令可以從檔案中得到像字串一樣的位元組序列:
% strings CV-Spy--md.cmbFORMPTCHCATREFSCOINCATDEVLFORMDEVIDESCCV Values (0->256)DDL Digital Delay Line...屬性值
屬性列表( Property lists )是Mac和iOS系統上的一種標準型別的檔案,將一些可以預知型別的資料有結構地組織起來就構成了我們的plist檔案。在該系統上,一般你看到的屬性列表檔案都是被壓縮成二進位制格式的檔案,這樣在讀取時會更快。使用者的偏好設定就被儲存為plist檔案:
% pwd/Users/markd/Library/Preferences% file com.apple.iphonesimulator.plistcom.apple.iphonesimulator.plist: Apple binary property list不幸的是,這個壓縮之後的plist檔案是一種非常難讀的檔案:
% hexdump -C com.apple.iphonesimulator.plist0000000062 70 6c 69 73 74 30 30dc 01 02 03 04 05 06 07|bplist00........|0000001008 09 0a 0b 0c 0d 0e 0f10 11 12 13 14 15 16 17|................|0000002018 5e 53 69 6d 75 6c 6174 65 44 65 76 69 63 65|.^SimulateDevice|000000305f 10 2f 4e 53 57 69 6e64 6f 77 20 46 72 61 6d|_./NSWindow Fram|0000004065 20 69 50 68 6f 6e 6553 69 6d 75 6c 61 74 6f|e iPhoneSimulato|0000005072 57 69 6e 64 6f 77 2e32 2e 30 2e 37 35 30 30|rWindow.2.0.7500|0000006030 30 5f 10 2f 4e 53 5769 6e 64 6f 77 20 46 72|00_./NSWindow Fr|0000007061 6d 65 20 69 50 68 6f6e 65 53 69 6d 75 6c 61|ame iPhoneSimula|...幸運的是,有一個工具plutil指令能將這樣二進位制形式的資料轉換為更接近與人類可讀語言的形式:
(“!$”快捷鍵用來獲取上一條指令中得最後一個引數)
Spotlight
在解讀一個特定檔案方面,OS可能做得比你想象得更好。Spotlight的工作就是為磁碟上的檔案編制索引,通過查詢元資料來讓本地搜尋更方便快捷。你可以通過 mdls 命令來獲取這個元資料,所以你能夠問問Spotlight對於這個檔案都知道些什麼:
% mdls launchHandler.mkMDItemContentCreationDate = 2014-07-02 19:22:02 +0000kMDItemContentModificationDate = 2014-07-02 19:23:58 +0000kMDItemContentType = "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 +0000kMDItemLogicalSize = 1443kMDItemPhysicalSize = 4096kMDItemUseCount = 2kMDItemUsedDates = ( "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框架中。在你的機器上,很可能是下面這樣的路徑:
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregisterlsregister主要被用於在OS系統上註冊一個檔案,這個檔案將會由特定的應用程式來處理。不過你可以dump它的資料庫來檢視相關資訊,使用:
% lsregister -dump > services-db.txt(要執行該指令,你需要擴充套件你的PATH,加入Support目錄)。
這條指令產生了大概61000行輸出,因此這對於一個日常教程來說就有些太笨重了,不過瀏覽一下也是挺有趣的。
還有一些有用的功能來自於一個呼叫: LSCopyApplicationURLsForURL 。給這個呼叫傳入一個檔案的路徑作為引數,它會返回一組可以處理該檔案的應用集合。它有不同的查詢模式,像“能開啟這個檔案的所有的應用程式有哪些?”,或者“能編輯這個檔案的所有應用程式有哪些?”載入服務並不像file指令一樣去內探這個檔案的結構。取而代之的是,它利用檔案的拓展名、原始碼、模糊匹配來找出合適的應用。
這裡有一個小工具,它需要在命令列傳入一個檔名。通過呼叫LSCopyApplicationURLsForURL,打印出匹配出來的應用程式的陣列。你可以在 這裡 找到這個原始碼。
@import Foundation;@import CoreServices;// clang -g -fobjc-arc -fmodules launchHandler.m -o launchHandlerint 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,因為它可以返回最合理的一組應用程式。有時候選的應用程式也能給你一些線索來判斷這個檔案到底是什麼。
% ./launchHandler launchHandler.mTextEdit.appXcode-4.6.appXcode-5.0.2.appXcode.appXcode6-Beta2.app% ./launchHandler someGraphic.pngAcorn.appColorSync Utility.appPreview.app% ./launchHandler ./Flongnozzle-2012%不幸的是,它並不能解決“Flongnozzle”是什麼的問題,因為這個檔案沒有拓展名或者其他有用的檔案型別的資訊。
其他工具
可用的命令列工具集非同尋常得多,因此我很可能落下了一個或者兩個或者更多其他的工具來幫助你辨別一個未知檔案。如果你有一個非常喜歡的小技巧,請留下一條評論!
