如何在Swift中执行终端命令?

5

我是一名Swift新手。我该如何从Swift代码运行这个过程?

  1. 打开终端窗口
  2. 执行cd Desktop/firebase-mac
  3. 执行npm start

我实际上想要做的是,在Swift代码中点击后启动Node服务器。


我尝试了以下代码: task.launchPath = "/usr/bin/env" task.arguments = ["/Users/aharon/Desktop"] 但是我得到了“Permission denied”(权限被拒绝)的错误提示。 - aharo vishinsky
想要在终端中运行命令有很多原因,但从程序中打开终端来执行这些命令几乎总是错误的。如果用户想要在终端中运行程序,就让他们自己去运行。如果你不熟悉shell,相信我的话;通常你不需要在终端中运行程序,而当你需要时,那应该是调用用户的选择。 - tripleee
4个回答

12

完整示例:

  • 进入某个目录,比如说桌面
  • 创建一个名为swsh的文件,并将以下内容添加到其中(明文,不是rtf或doc格式)
#!/usr/bin/env xcrun swift

import Foundation

func shell(launchPath: String, arguments: [String]) -> String {

    let process = Process()
    process.launchPath = launchPath
    process.arguments = arguments

    let pipe = Pipe()
    process.standardOutput = pipe
    process.launch()

    let output_from_command = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: String.Encoding.utf8)!

    // remove the trailing new-line char
    if output_from_command.characters.count > 0 {
        let lastIndex = output_from_command.index(before: output_from_command.endIndex)
        return output_from_command[output_from_command.startIndex ..< lastIndex]
    }
    return output_from_command
}

let output = shell(launchPath: "/bin/date", arguments: [ ])
print(output)

保存,然后:

  • 打开终端
  • 输入cd ~/Desktop
  • 使用chmod 755 swsh
  • 运行你的swift脚本./swsh

你将得到如下输出:

Sat Mar 25 14:31:39 CET 2017

编辑你的swsh,并将shell(...行更改为:

let output = shell(launchPath: "/usr/bin/env", arguments: [ "date" ])

运行它,你将再次获得日期,但现在:

  • swsh 执行了 /usr/bin/env (带有参数 date)
  • /usr/bin/env 查找命令 date
  • 并执行它

现在,在 ~/Desktop 中创建另一个文件,并将其命名为 from_swift

将以下内容添加到其中:

echo "Today's date is $(date)"

将文件 swsh 中的 shell 行更改为:

let output = shell(launchPath: "./from_swift", arguments: [ ])

注意,./from_swift - 使用相对路径到.(我们在~/Desktop目录中)。运行swift程序:

./swsh

输出:

2017-03-25 14:42:20.176 swift[48479:638098] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'launch path not accessible'

当然,脚本from_swift目前还不能执行。因此,请执行以下操作:
chmod 755 from_swift
# and run
./swsh

再次出现错误:

2017-03-25 14:45:38.523 swift[48520:639486] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Couldn't posix_spawn: error 8'

这是因为from_swift是一个脚本(而不是编译后的二进制文件),所以操作系统需要知道哪个二进制文件应该解释脚本内容。由于这是一个shell脚本,请将from_swift脚本编辑为:
#!/bin/sh
echo "Today's date is $(date)"

请注意添加了"shebang"行:#!/bin/sh。运行swift ./swsh,将会得到:
Today's date is Sat Mar 25 14:50:23 CET 2017

恭喜,你已经成功地从 Swift 执行了你的第一个 Bash 脚本。 ;)

当然,你可以在 shebang 中使用 /usr/bin/env,现在修改 from_swift 的内容,例如:

#!/usr/bin/env perl

use strict;
use utf8;
use English;
binmode STDOUT, ":utf8";

printf "The $EXECUTABLE_NAME (ver:$PERL_VERSION) runs me: $0\n";
printf "I ❤️ perl!\n";

运行./swsh,会得到以下结果:
The /usr/bin/perl (ver:v5.18.2) runs me: ./from_swift
I ❤️ perl!

注意,我们在./swsh文件中没有做任何更改,只是修改了脚本文件./from_swift

以上所有操作均使用以下内容完成:

$ uname -a
Darwin nox.local 16.4.0 Darwin Kernel Version 16.4.0: Thu Dec 22 22:53:21 PST 2016; root:xnu-3789.41.3~3/RELEASE_X86_64 x86_64
$ swift --version
Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1)
Target: x86_64-apple-macosx10.9

因此,轻松创建和执行任何脚本。因此,您可以进入您的~/Desktop/from_swift文件夹。
#!/bin/sh
cd $HOME/Desktop/firebase-mac
npm start

可以直接从swsh中执行(Jens Meder提出的)方法,但使用此方法可以轻松地从给定的脚本文件执行任何内容。
只需记住:process.launch()执行以下操作之一:
  • 编译后的二进制文件
  • 或脚本文件,但脚本文件
    • 必须shebang
    • 并且必须使用chmod 755 /path/to/script.file可执行

3

您可以使用以下代码片段,例如:run("npm start")。它将在默认 shell /bin/sh 上执行命令并将输出作为 String 返回。

func run(_ cmd: String) -> String? {
    let pipe = Pipe()
    let process = Process()
    process.launchPath = "/bin/sh"
    process.arguments = ["-c", String(format:"%@", cmd)]
    process.standardOutput = pipe
    let fileHandle = pipe.fileHandleForReading
    process.launch()
    return String(data: fileHandle.readDataToEndOfFile(), encoding: .utf8)
}

如果您想使用另一个bash,只需替换launchPath,例如,对于Zsh,它将是/bin/zsh


对于 "/bin/sh",我得到了 "命令未找到"。 对于 "/bin/env",我得到了 "启动路径不可访问"。 - aharo vishinsky
1
你能在新的终端窗口中运行 echo $SHELL 并尝试输出吗?对我来说,它输出 /bin/zsh,因为我使用 Zsh 作为 shell。 - Jens Meder
结果是/bin/bash。 - aharo vishinsky
1
我创建了一个Shell脚本: #!/bin/bash cd /users/aharon/desktop/firebase-mac npm start 当我直接运行时,它能够正常工作, 但是如果我从代码中运行它,就会显示line 3: npm: command not found - aharo vishinsky
@aharovishinsky 应该是 /usr/bin/env 而不是 /bin/env - clt60
显示剩余2条评论

1
使用

swift-commands


import Commands

Commands.Bash.system("cd /Users/${name}/Desktop/firebase-mac && npm start")

0
启动一个传递一个参数的进程,你只需要一行代码:
Process.launchedProcess(launchPath: "/Users/aharon/Desktop/firebase-mac/npm", arguments: ["start"])

但请注意,如果应用程序被沙盒化,则字面路径将无法正常工作。


我创建了一个 shell 脚本 #!/bin/bash cd /users/aharon/desktop/firebase-mac npm start 当我直接运行它时,它可以正常工作, 但是如果我从代码中运行它,就会出现 line 3: npm: command not found 的错误提示。 - aharo vishinsky
你试过我的代码了吗?忘记额外的 cd 命令,你不需要它。只需传递完整路径 /users/aharon/desktop/firebase-mac/npm start - vadian
我收到了“启动路径不可访问”的错误信息。 - aharo vishinsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接