这篇博文中的程序 将要示范 如何编写 一个命令行工具,从而 实现 简单的复制文件操作。这项命令的用法 可以是 这样:
与 适用于NSFileManager这类物件的措施copyPath:toPath:handler: 不同,copy这项命令 允许 目标 可以是 目录名称。另外一点不同的 是 如果 目标文件 已经存在,那么 copy这项命令 将会用 来源文件 覆盖 目标文件。这一点 跟标准UNIX复制命令cp 是 一致的。
你 可以通过主程序main()函数的两个参数argc 和 argv 从命令行中 获取 来源文件 和 目标文件的路径名称。这两个参数 包含了 若干命令行参数(包括 命令名称),以及 存储着c语言字符串地址的指针。
与其需要处理 c语言字符串,还不如使用 基础框架所提供的NSProcessInfo这类物件。有 不少措施 适用于 NSProcessInfo这类物件,这些措施 可以帮 你 设定、读取 你所正在运行的程序(也就是 你的进程)的各种信息。这些措施 总结 在下面的表格当中:
措施名称 | 描述 |
+(NSProcessInfo *)processInfo | 创建NSProcessInfo类型的物件 |
-(NSArray *)arguments | 获取当前程序的命令行参数 |
-(NSDictionary *)environment | 获取当前环境变量 |
-(int)processIdentifier | 获得当前进程识别号码 |
-(NSString *)processName | 获取当前进程名称 |
-(NSString *)globallyUniqueString | 用来生成随机字符串,可用作临时文件名称 |
-(NSString *)hostName | 获取主机系统名称 |
-(NSUInteger)operatingSystem | 获取代表操作系统的整数值,比如5代表mac系统 |
-(NSString *)operatingSystemName | 获取操作系统的名称 |
-(NSString *)operatingSystemVersionString | 获取操作系统版本 |
-(void)setProcessName:(NSString *)name | 将进程名称设定为name |
下面看看 如何编写 copy这个程序:
#import <Foundation/NSArray.h>
#import <Foundation/NSFileManager.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSPathUtilities.h>
#import <Foundation/NSProcessInfo.h>
int main ( int argc , const char * argv [ ] )
{
NSAutoreleasePool *pool = [ [ NSAutoreleasePool alloc ] init ] ;
NSFileManager *myFileManager ;
NSString *source , *destination ;
BOOL isDirectory ;
NSProcessInfo *myProcessInfo = [ NSProcessInfo processInfo ] ;
NSArray *myArguments = [ myProcessInfo arguments ] ;
myFileManager = [ NSFileManager defaultManager ] ;
if ( [ myArguments count ] != 3 )
{
NSLog ( @"用法:%@ 来源 目标" , [ myProcessInfo processName ] ) ;
return 1 ;
}
source = [ myArguments objectAtIndex : 1 ] ;
destination = [ myArguments objectAtIndex : 2 ] ;
if ( [ myFileManager isReadableFileAtPath : source ] == NO )
{
NSLog ( @"无法读取来源文件%@。" , source ) ;
return 2 ;
}
[ myFileManager fileExistsAtPath : destination isDirectory : & isDirectory ] ;
if ( isDirectory == YES )
{
destination = [ destination stringByAppendingPathComponent : [ source lastPathComponent ] ] ;
}
[ myFileManager removeFileAtPath : destination handler : nil ] ;
if ( [ myFileManager copyPath : source toPath : destination handler : nil ] == NO )
{
NSLog ( @"复制失败!" ) ;
return 3 ;
}
NSLog ( @"将%@复制到%@成功!" , source , destination ) ;
[ pool drain ] ;
return 0 ;
}
为了能够 在命令行中 使用 copy这个命令,我们 需要将 可执行文件copy 复制 到/usr/bin当中。在命令行中 输入 open “/usr/bin”,就可以打开 /usr/bin这个目录了。我们 先试试执行 copy这项命令,同时 不附带 来源文件名称 和 目标文件名称:
koeris-Mac-mini:~ Zijin$ copy
2012-05-17 16:03:23.113 copy[8534:707] 用法:copy 来源 目标
再来 复制 一个不存在的文件:
koeris-Mac-mini:~ Zijin$ copy file1 file2
2012-05-17 16:04:22.088 copy[8536:707] 无法读取来源文件file1。
接着 复制 一个存在的文件
koeris-Mac-mini:~ Zijin$ copy /Users/Zijin/Downloads/gnucash-2.4.10.tar.bz2 /Users/Zijin/Desktop
2012-05-17 16:17:26.425 copy[8557:707] 将/Users/Zijin/Downloads/gnucash-2.4.10.tar.bz2复制到/Users/Zijin/Desktop/gnucash-2.4.10.tar.bz2成功!
在这个程序的
NSArray *myArguments = [ myProcessInfo arguments ] ;
这行语句当中,我 对 myProcessInfo这个物件 采取了 arguments这项措施,arguments这项措施 会将 包含着字符串物件的数组物件 存储 在myArguments当中。myArguments这个数组物件中的第零个物件 是 进程名称,剩余的物件 是 命令行中输入的参数。
if ( [ myArguments count ] != 3 )
{
NSLog ( @"用法:%@ 来源 目标" , [ myProcessInfo processName ] ) ;
return 1 ;
}
这几行语句 先检查 myArguments这个数组物件 是否包含了 三个元素,也就是 检查 用户 是否输入了 两个参数。如果 用户 并没有输入 两个参数,也就是 myArguments这个数组物件 并不包含 三个元素,那么 程序 就提醒 用户 copy这项命令的用法,并且 终止 程序,将 整数值1 传递 给系统。
如果 用户 确实 输入了 两个参数,那么 继续执行 下面的语句:
source = [ myArguments objectAtIndex : 1 ] ;
destination = [ myArguments objectAtIndex : 2 ] ;
对 myArguments这个物件 采取 objectAtIndex:这项措施,分别 将 1 和 2 用作 参数,从而 将 myArguments这个数组物件中的第1个 和 第2个物件(也就是 用户在命令行中输入的来源文件名称 和 目标文件名称)读取出来,并且 分别 存储 在source 和 destination。接着
[ myFileManager fileExistsAtPath : destination isDirectory : & amp ; isDirectory ] ;
这行语句 会判断 目标文件 是否是 目录。如果 目标文件 是 目录,那么 就将 变量isDirectory的值 设定为 YES。
接着
if ( isDirectory == YES )
{
destination = [ destination stringByAppendingPathComponent : [ source lastPathComponent ] ] ;
}
会判断 变量isDirectory的值 是否为 YES。如果 变量isDirectory的值 为 YES,那么 表明 目标文件 是 目录。所以 就会执行
destination = [ destination stringByAppendingPathComponent : [ source lastPathComponent ] ] ;
这行语句。其中
[ source lastPathComponent ]
将 来源文件中最后一个组成部分(也就是 文件名) 读取出来。比如 /Users/Zijin/Downloads/gnucash-2.4.10.tar.bz2的最后一个组成部分 就是 gnucash-2.4.10.tar.bz2。
stringByAppendingPathComponent:这项措施的实施对象destination 是 目标文件(或者 目标路径),其参数 为 不包含路径的来源文件名称。stringByAppendingPathComponent:这项措施 将 不包含路径的来源文件名称 附加 在目标路径的末尾处,从而 构成 完整的目标文件。
由于 如果 目标文件 已经存在,而 copyPath:toPath:handler:这项措施 无法用 来源文件 覆盖 已有的目标文件,所以 为了避免 错误,我们 可以采取 removeFileAtPath:handler:这项措施 将 已存在的目标文件 删除,就像 这样:
[ myFileManager removeFileAtPath : destination handler : nil ] ;
程序剩余的部分 其实 已经不言自明了,这里 就不再 赘述了。