简体   繁体   English

使用Swift中的命令行工具更新当前行

[英]Update current line with command line tool in Swift

I built a OS X command line tool in Swift (same problem in Objective-C) for downloading certain files. 我在Swift中构建了一个OS X命令行工具(Objective-C中的同样问题),用于下载某些文件。 I am trying to update the command line with download progress. 我正在尝试使用下载进度更新命令行。 Unfortunately, I cannot prevent the print statement to jump to the next line. 不幸的是,我无法阻止print语句跳转到下一行。

According to my research, the carriage return \\r should jump to the beginning of the same line (while \\n would insert a new line). 根据我的研究,回车\\r应该跳到同一行的开头(而\\n会插入一个新行)。

All tests have been performed in the OS X Terminal app, not the Xcode console. 所有测试都在OS X终端应用程序中执行,而不是在Xcode控制台上执行。

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

Still, the display inserts a new line. 仍然,显示器插入一个新行。 How to prevent this? 怎么预防这个?

Note: This won't work in Xcode's debugger window because it's not a real terminal emulator and doesn't fully support escape sequences. 注意:这在Xcode的调试器窗口中不起作用,因为它不是真正的终端仿真器,并且不完全支持转义序列。 So, to test things, you have to compile it and then manually run it in a Terminal window. 因此,要进行测试,您必须编译它,然后在终端窗口中手动运行它。

\\r should work to move to the beginning of the current line in most terminals, but you should also take a look at VT100 Terminal Control Escape Sequences . \\r应该可以在大多数终端中移动到当前行的开头,但是你还应该看看VT100终端控制转义序列 They work by sending an escape character, \\u{1B} in Swift, and then a command. 他们通过在Swift中发送转义字符\\u{1B} ,然后发送命令来工作。 (Warning: they make some pretty ugly string definitions) (警告:他们制作一些非常难看的字符串定义)

One that you'll probably need is \\u{1B}[K which clears the line from the current cursor position to the end. 你可能需要的是\\u{1B}[K ,它清除从当前光标位置到结尾的行。 If you don't do this and your progress output varies in length at all, you'll get artifacts left over from previous print statements. 如果你不这样做,你的进度输出长度不一样,你会得到以前的print语句留下的文物。

Some other useful ones are: 其他一些有用的是:

  • Move to any (x, y) position: \\u{1B}[\\(y);\\(x)H Note: x and y are Int s inserted with string interpolation. 移动到任何(x,y)位置: \\u{1B}[\\(y);\\(x)H 注意: xy是用字符串插值插入的Int
  • Save cursor state and position: \\u{1B}7 保存光标状态和位置: \\u{1B}7
  • Restore cursor state and position: \\u{1B}8 恢复光标状态和位置: \\u{1B}8
  • Clear screen: \\u{1B}[2J 清除屏幕: \\u{1B}[2J

You can also do interesting things like set text foreground and background colors. 您还可以执行有趣的操作,例如设置文本前景色和背景色。

If for some reason you can't get \\r to work, you could work around it by saving the cursor state/position just before you print your logString and then restoring it after: 如果由于某种原因你无法使\\r工作,你可以通过在打印logString之前保存光标状态/位置然后在以下情况之后恢复它来解决它:

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

Or by moving to a pre-defined (x, y) position, before printing it: 或者在打印之前移动到预定义的(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)

Your example will only work on the actual command line, not in the debugger console. 您的示例仅适用于实际命令行,而不适用于调试器控制台。 And you also need to flush stdout for every iteration, like this: 而且你还需要为每次迭代刷新stdout,如下所示:

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

Here's an example assuming some async task is taking place with callbacks that pass a Progress type. 下面是一个示例,假设某些异步任务正在进行,并且回调会传递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)%")
}

The key part is \\u{1B}[1A\\u{1B}[K and then whatever you want to print out to the screen following. 关键部分是\\u{1B}[1A\\u{1B}[K然后你要打印到屏幕后面的任何内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM