65655


Swiftスクリプトから `touch` コマンドを実行したいと考えています。

以下の質問のベストアンサーを参考に、Swiftスクリプトでターミナルコマンドを実行するところまではできました。  
<https://stackoverflow.com/questions/26971240/how-do-i-run-an-terminal-
command-in-a-swift-script-e-g-xcodebuild>

[code]

    #!/usr/bin/env swift
    
    import Foundation
    
    @discardableResult
    func shell(_ args: String...) -> Int32 {
      let task = Process()
      task.launchPath = "/usr/bin/env"
      task.arguments = args
      task.launch()
      task.waitUntilExit()
      return task.terminationStatus
    }
    
    shell("pwd")
    shell("ls")
    shell("touch -t 2001020304 '/Users/myname/sample.txt'") // sample.txtのタイムスタンプを変更する
    
[/code]

上記を `sample.swift` として保存し、ターミナルから `$ swift sample.swift` を実行すると  
`pwd` と `ls` のコマンドは正常に実行されるのですが、目的の `touch` コマンドのところで以下のようなエラーが出ます。

[code]

    env: touch -t 2001020304 '/Users/myname/sample.txt': No such file or directory
    
[/code]

ターミナルに直接このコマンドを入力すると、期待した通りにタイムスタンプが「2020年1月2日3時4分」に変更されるので、コマンド自体は間違っていません。

[code]

    $ touch -t 2001020304 '/Users/myname/sample.txt'
    
[/code]

ファイルのパスのシングルクォーテーションを外してみたりしましたが、結果は同じでした。  
どうすれば正常にコマンドを実行させられるでしょうか。  
環境は、macOS Mojave, Swift 5.1.3です。


CC BY-SA 4.0
2020-04-15T14:38:35.187
65654
2020-04-15T16:00:35.657
23029
question
swift macos
Swiftスクリプトからtouchコマンドを実行する方法について。

[code]

    shell("touch -t 2001020304 '/Users/myname/sample.txt'")
    
[/code]

引数を全部１つの文字列として渡してしまっているからですね。

[code]

    /usr/bin/env "touch -t 2001020304 '/Users/myname/sample.txt'"
    
[/code]

（実際にはシェルの展開が入るので厳密ではないですが）簡単にすると上記のように解釈されてしまっています。（touchから.txtまでが１つの引数になっている）

`shell(_: String...)`はStringの可変長引数を取るので最終的にStringの配列として`task.arguments`に渡されます。

`task.arguments`が配列を取るのは、引数１つ１つを区別できるようにするためです。

なので下記のようにコマンドも含めて引数１つ１つをそれぞれ別の文字列として渡します。  
この場合、引数の区切りは自明なのでディレクトリのパスをわざわざクオーテーションで囲う必要はありません（空白が含まれていても）。

[code]

    shell("touch", "-t", "2001020304", "/Users/katsumi/sample.txt")
    
[/code]


CC BY-SA 4.0
2020-04-15T16:00:35.657
65655
2020-04-15T16:00:35.657
5519
65654
answer