SwiftでCatコマンドを作る

catコマンド

catコマンドとは、与えられたファイルパスの中身を標準出力に書き込むコマンドです。今回はそれをSwiftで再現してみたいと思います。

The cat utility reads files sequentially, writing them to the standard output. The file operands are processed in command-line order. If file is a single dash (‘-’) or absent, cat reads from the standard input. If file is a UNIX domain socket, cat connects to it and then reads it until EOF. This complements the UNIX domain binding capability available in inetd(8).

今回は、一つのファイルを読んで出力するところまでを再現します。

Swiftからシステムコールを呼ぶ

Swiftからreadwriteなどのシステムコールを呼ぶために、今回はapple/swift-system: Low-level system calls and types for SwiftというApple製のSwift Packageを利用することにしました。

1import SystemPackage

としてインポートすることができます。

コマンドを読み込む

Swiftでは以下のようにすることでターミナルから引数を読み込むことができます。

1let argv = CommandLine.arguments
2let argc = CommandLine.arguments.count

実行とテスト

swift run cat [ file ]

今回はcatというexecutableTargetを作成しているので上記のようにして実行することができます。またテストする際は以下のようにして行いました。

diff -a <(cat Sources/cat/cat.swift) <(swift run cat Sources/cat/cat.swift)

差分がなければ標準出力には何も表示されずに正常終了します。

Swiftコード全体

 1import SystemPackage
 2
 3@main
 4struct Cat {
 5    public static func main() throws {
 6        let argv = CommandLine.arguments
 7        let argc = CommandLine.arguments.count
 8
 9        let buf = UnsafeMutableRawBufferPointer.allocate(byteCount: 4096, alignment: 0)
10        var fd: FileDescriptor = FileDescriptor.standardInput
11
12        if argc != 2 {
13            fatalError("Usage: \(argv[0]) [ file ]")
14        } else {
15            do {
16                fd = try FileDescriptor.open(argv[1], .readOnly)
17            } catch {
18                fatalError("cannot open \(argv[1]): \(error)")
19            }
20
21            do {
22                let bytes = try fd.read(into: buf)
23                try fd.close()
24                let _ = try FileDescriptor.standardOutput.writeAll(buf[..<bytes])
25                buf.deallocate()
26            } catch {
27                fatalError("Error: \(error)")
28            }
29        }
30    }
31}

参考

  • Catの実装は以下の動画を参考にしました。

See Also