首页 自己做个命令行工具
文章
取消

自己做个命令行工具

开发中一直在使用 cocoapods , git, class-dump, cycript 等强大的命令行工具. 今天就来自己尝试做一个简单了解一下这些工具的工作原理.

开始前先讲个故事:

UI : iOS 的 icon 都需要那些尺寸 ◔ ‸◔?? 我 : 给我一张 1024 的就好 (-_-#). (思路被打断正不爽).

于是后来每次我都只能拿到一张 1024 * 1024 的 icon, 不得不每次都自己用 预览 来导出各种尺寸的 icon. 毕竟自作自受 ("▔□▔)/ , 那今天就写个生成各种尺寸 icon 的工具吧.

0x00 如何开始

  1. 首先使用 Xcode 创建一个 Command Line 工程. 没错, 就是那种只有一个 .m 文件, run 了之后只会 printfNSLog 的那种工程, 我的项目名称就叫 appicon, 后面会提到.

  2. 导入头文件 #import <Cocoa/Cocoa.h>, 因为是基于 macOS 的, 所以导入这个. 不然 NSFileManager 都用不了, 大概…
  3. 先上代码, 下面再解释.
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
36
37
38
39
40
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *input = nil;
        NSString *output = nil;
        
        int ch;
        // 读取参数
        while ((ch = getopt(argc, argv, "i:o:")) != -1) {
            switch (ch) {
                case 'o':
                    output = [NSString stringWithUTF8String:optarg];
                    break;
                case 'i':
                    input = [NSString stringWithUTF8String:optarg];
                    break;
            }
        }
        
        BOOL isDir = NO;
        // 判断文件路径, 以及输出目标路径
        if (input && output && [[NSFileManager defaultManager] fileExistsAtPath:output isDirectory:&isDir]) {
            NSImage *image = [[NSImage alloc] initWithContentsOfFile:input];
            if (image == nil) {
                printf("File Load Failed At %s", input.UTF8String);
                exit(0);
            }
            if (!isDir) {
                output = [output stringByAppendingPathComponent:@"icons"];
            }
            writeImage(image, output, 180);
            writeImage(image, output, 120);
            writeImage(image, output, 80);
            writeImage(image, output, 60);
            writeImage(image, output, 40);
        } else {
            printf("Path Error!\n");
        }
    }
    return 0;
}

也没多少代码, 看起来也没多难, 应该都能看懂, 除了这段:

1
2
3
4
5
6
7
8
9
10
11
int ch;
while ((ch = getopt(argc, argv, "i:o:")) != -1) {
switch (ch) {
    case 'o':
        output = [NSString stringWithUTF8String:optarg];
        break;
    case 'i':
        input = [NSString stringWithUTF8String:optarg];
        break;
    }
}

首先要注意一个函数 getopt, 这个就是获取命令行输入参数的关键.

1
2
// 函数返回 -1 表示参数解析完毕
int getopt(int argc, char * const argv[], const char *optstring);

看函数声明有没有熟悉的东西? 我们的main 函数 int main(int argc, const char * argv[]) 里面有一模一样的 2 个参数.

argcmain() 函数传递过来的参数的个数 argvmain() 函数传递过来的参数的字符串指针数组 optstring:选项字符串,告知 getopt() 可以处理哪个选项以及哪个选项需要参数

argcargv 直接放进去就可以了, optstring 此处传入的是 "i:o:". char* optstring = "ab:c::"; | 字符 | 意义 | 格式 | | —————— | ————————- | ———————————– | | 单个字符 a | 表示选项a没有参数 | 格式:-a 即可,不加参数 | | 单字符加冒号 b: | 表示选项b有且必须加参数 | 格式:-b 100 或 -b100 ,但 -b=100 错 | | 单字符加2冒号 c: : | 表示选项c可以有,也可以无 | 格式:-c200,其它格式错误 |

因此此处的 "i:o:", 表示有 2 个参数 -i-o, 分别代表 inputoutput, 可以看成是参数名. 使用一个while 循环来获取所有的参数, 此处的 optarg 就是获取后的参数值.

以下写过 iOS 的稍微转换一下就可以了.都是常见的功能. 压缩图片尺寸:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 压缩图片到指定尺寸

 @param image 图片对象
 @param width 目标尺寸
 @return 压缩结果
 */
NSData * compressImage(NSImage *image, float width) {
    NSData *data = image.TIFFRepresentation;
    if (data == nil) {
        return nil;
    }
    CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)data, NULL);
    CGImageRef cgimage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
    
    NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:cgimage];
    rep.size = CGSizeMake(width, width);
    NSData *pngData = [rep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
    return pngData;
}

把 image 写入指定的位置.

1
2
3
4
5
6
7
8
9
10
11
/**
 处理图片并把结果写入磁盘

 @param image 被处理的图片对象
 @param path  写入路径
 @param size  目标图片尺寸
 */
void writeImage(NSImage *image, NSString *path, float size) {
    path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"%.0f.png", size]];
    [compressImage(image, size) writeToFile:path atomically:NO];
}

0x01 如何使用

  1. 项目编译之后会生成一个 appicon , Product 文件夹下的 Mach-O 文件.
  2. 复制 appiconusr/local/bin, 放在此目录下, 在任何文件夹下都能够找到命令, 不然还要指定路径.
  3. 使用命令:
1
$ appicon -i [输入文件路径] -o [输出目录路径]

例如:

1
$ appicon -i ~/Desktop/1024.png -o ~/Desktop

或者

1
2
$ cd ~/Desktop/
$ appicon -i ./1024.png -o ./

而上文中 inputoutput 分别就对应 ~/Desktop/1024.png~/Desktop.

Github 源码地址

本文由作者按照 CC BY 4.0 进行授权