1、命令行工具的本质
2、制作命令行工具
2.1、创建iOS项目
创建一个iOS项目TestCL,去掉UI相关的文件和代码,main函数:
int main(int argc, char * argv[]) {
@autoreleasepool {
printf("TestCL--------\n");
}
return 0;
}
2.2、设置Release模式
Edit Scheme - Run - Build Configuration - Release
2.3、添加可执行文件
将编译后生成的可执行文件复制到iPhone的usr/bin目录下,在iPhone终端输入:
$ TestCL
会提示权限不足:
-sh: /usr/bin/TestCL: Permission denied
需要给可执行文件赋予“可执行的权限”
$ chmod +x /usr/bin/TestCL
再次运行:# TestCL
TestCL--------
3、判断Mach-O文件类型
Mach-O文件大致可分为通用(FAT)二进制文件、64bit架构文件、非64bit架构文件,在 xnu源码 中已经定义了对应的魔数:
#define FAT_MAGIC 0xcafebabe
#define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */
xnu/EXTERNAL_HEADERS/mach-o/fat.h
#define MH_MAGIC 0xfeedface /* the mach magic number */
#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */
/* Constant for the magic field of the mach_header_64 (64-bit architectures) */
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
xnu/EXTERNAL_HEADERS/mach-o/loader.h
以今日头条为例,判断它的可执行文件的类型:
#import <UIKit/UIKit.h>
#import <mach-o/fat.h>
#import <mach-o/loader.h>
int main(int argc, char * argv[]) {
@autoreleasepool {
// 今日头条可执行文件地址
NSString *path = @"/var/containers/Bundle/Application/CCB2D157-22E3-49EB-9914-582673D42676/News.app/News";
NSFileHandle* handle = [NSFileHandle fileHandleForReadingAtPath:path];
int length = sizeof(uint32_t);
// 读取前面的4个字节(用于标识文件类型)
NSData *magicData = [handle readDataOfLength:length];
// 把magicData里4个字节的数据存放到magicNumber中
uint32_t magicNumber;
[magicData getBytes:&magicNumber length:length];
// 判断文件类型(判断两个条件是为了兼容大小端)
if (magicNumber == FAT_MAGIC || magicNumber == FAT_CIGAM) {
printf("FAT文件\n");
} else if (magicNumber == MH_MAGIC || magicNumber == MH_CIGAM) {
printf("非64bit架构文件\n");
} else if (magicNumber == MH_MAGIC_64 || magicNumber == MH_CIGAM_64) {
printf("64bit架构文件\n");
}else {
printf("读取失败\n");
}
printf("magicNumber = 0x%x\n", magicNumber);
[handle closeFile];
}
return 0;
}
将编译后的可执行文件复制到iPhone的usr/bin目录下,在iPhone终端输入:
$ TestCL
64bit架构文件
magicNumber = 0xfeedfacf
4、参数
命令行工具可以接受参数,通过接收不同的参数进行不同的操作,命令行的参数也就是mian函数的参数:
int main(int argc, char * argv[]) {
@autoreleasepool {
}
return 0;
}
agrc:参数的个数 argv:存放参数的数组 argv[0]:当前可执行文件的路径
例如接收参数 -x 执行功能a,接收参数 -y 执行功能b:
int main(int argc, char * argv[]) {
@autoreleasepool {
if (argc == 1) { // 只有一个参数代表只有可执行文件路径
return 0;
}
if (strcmp(argv[1], "-x") == 0) {
printf("执行功能a");
return 0;
}
if (strcmp(argv[1], "-y") == 0) {
printf("执行功能b");
return 0;
}
}
return 0;
}
5、权限
在使用命令行工具时,可能会遇到权限不足的问题,可以通过ldid工具来查看可执行文件的权限信息:
$ ldid -e TestCL > TestCL.entitlements
:代表将内容覆盖文件
:代表将内容追加到文件
TestCL.entitlements文件的本质就是一个plist文件
将权限签到可执行文件:
$ ldid -STestCL.entitlements TestCL
一般情况下,可以找一个合适的可执行文件,导出它的权限信息,然后签到自定义的命令行工具中,比如导出SpringBoard的权限:
$ ldid -e SpringBoard > SpringBoard.entitlements
SpringBoard可执行文件路径:System/Library/CoreServices/SpringBoard.app/SpringBoard
将SpringBoard的权限信息签到TestCL中:
$ ldid -SSpringBoard.entitlements TestCL
这样TestCL就拥有了和SpringBoard一样的权限了。