【问题标题】:Update current line with command line tool in Swift在 Swift 中使用命令行工具更新当前行
【发布时间】:2014-08-25 09:53:51
【问题描述】:

我在 Swift 中构建了一个 OS X 命令行工具(在 Objective-C 中也存在同样的问题),用于下载某些文件。我正在尝试使用下载进度更新命令行。不幸的是,我无法阻止 print 语句跳转到下一行。

根据我的研究,回车\r 应该跳转到same 行的开头(而\n 会插入一个新行)。

所有测试都在 OS X 终端应用程序中执行,而不是 Xcode 控制台。

let logString = String(format: "%2i%% %.2fM \r", percentage, megaBytes)
print(logString)

不过,显示屏会插入一个新行。如何预防?

【问题讨论】:

    标签: objective-c macos cocoa swift newline


    【解决方案1】:

    注意:这在 Xcode 的调试器窗口中不起作用,因为它不是真正的终端仿真器并且不完全支持转义序列。因此,要进行测试,您必须对其进行编译,然后在终端窗口中手动运行它。

    \r 在大多数终端中应该可以移动到当前行的开头,但您也应该看看VT100 Terminal Control Escape Sequences。它们通过在 Swift 中发送一个转义字符 \u{1B},然后是一个命令来工作。 (警告:他们做了一些非常丑陋的字符串定义)

    您可能需要\u{1B}[K,它会清除从当前光标位置到结尾的行。如果您不这样做并且您的进度输出的长度完全不同,您将得到以前打印语句遗留下来的工件。

    其他一些有用的有:

    • 移动到任意 (x, y) 位置:\u{1B}[\(y);\(x)H 注意:xy 通过字符串插值插入 Ints。
    • 保存光标状态和位置:\u{1B}7
    • 恢复光标状态和位置:\u{1B}8
    • 清屏:\u{1B}[2J

    您还可以做一些有趣的事情,例如设置文本前景色和背景色。

    如果由于某种原因您无法让 \r 工作,您可以通过在打印 logString 之前保存光标状态/位置然后在之后恢复它来解决它:

    let logString = String(format: "\u{1B}7%2i%% %.2fM \u{1B}8", percentage, megaBytes)
    print(logString)
    

    或者在打印之前移动到预定义的 (x, y) 位置:

    let x = 0
    let y = 1 // Rows typically start at 1 not 0, but it depends on the terminal and shell
    let logString = String(format: "\u{1B}[\(y);\(x)H%2i%% %.2fM ", percentage, megaBytes)
    print(logString)
    

    【讨论】:

    • 这很有趣 - 谢谢。但是,它并没有解决核心问题,而且\u{1B} 命令也不起作用(没有输出)。
    • 感谢您对控制字符的详细描述。但是,您的方案只能快速运行。在 ObjC 或 C 中,编译器会发出错误!!! (不是警告)在格式字符串上,声称“\u 之后没有十六进制数字”。您知道如何将这些控制字符转换为 ObjC NSString 常量吗?
    【解决方案2】:

    这是一个示例,假设某些异步任务正在使用传递 Progress 类型的回调进行。

    // Print an empty string first otherwise whichever line is above the printed out progress will be removed
    print("")
    
    let someProgressCallback: ExampleAsyncCallbackType = { progress in
      let percentage = Int(progress.fractionCompleted * 100)
    
      print("\u{1B}[1A\u{1B}[KDownloaded: \(percentage)%")
    }
    

    关键部分是\u{1B}[1A\u{1B}[K,然后是您想要打印到屏幕上的任何内容。

    【讨论】:

      【解决方案3】:

      您的示例仅适用于实际命令行,而不适用于调试器控制台。而且您还需要为每次迭代刷新标准输出,如下所示:

      var logString = String(format: "%2i%% %.2fM \r", 10, 5)
      print(logString)
      fflush(__stdoutp)
      

      【讨论】:

      • 感谢您指出fflush(__stdoutp)。这是对问题的正确分析。
      • 不适合我。 \rfflush(__stdoutp) 不会产生任何结果。也许是因为我使用了ohmyzsh?
      猜你喜欢
      • 1970-01-01
      • 2017-08-16
      • 1970-01-01
      • 1970-01-01
      • 2015-03-04
      • 2017-07-21
      • 1970-01-01
      • 1970-01-01
      • 2015-01-25
      相关资源
      最近更新 更多