#终端 #shell #终端文本 #cli #文本 #no-std

no-std os_display

以安全且与平台相适应的方式显示字符串

4个版本

0.1.3 2022年1月22日
0.1.2 2021年11月8日
0.1.1 2021年10月14日
0.1.0 2021年10月13日

文本处理中排名第255

Download history 7054/week @ 2024-03-14 9682/week @ 2024-03-21 9999/week @ 2024-03-28 7691/week @ 2024-04-04 8293/week @ 2024-04-11 7409/week @ 2024-04-18 10577/week @ 2024-04-25 10025/week @ 2024-05-02 6528/week @ 2024-05-09 6743/week @ 2024-05-16 6117/week @ 2024-05-23 5961/week @ 2024-05-30 4654/week @ 2024-06-06 5094/week @ 2024-06-13 6772/week @ 2024-06-20 7439/week @ 2024-06-27

每月下载量24,798
130个Crate中使用(直接使用2个)

MIT许可证

57KB
1K SLoC

os_display

Crates.io API reference MSRV CI

打印字符串可能很棘手。它们可能包含会破坏消息或整个终端的控制代码。在Unix中,甚至文件名也可能包含这样的字符。

文件名也可能包含无效的Unicode,而这在Path::display中不会被保留。

最后,它们可能包含一些特殊字符,这些字符在没有引用或转义的情况下在命令中使用是不安全的。

此库允许您向文件名(以及其他字符串)添加引号,以便更安全、更有用地显示它们。目标是使它们能够以这种方式渲染,以便可以在不丢失信息的情况下将它们复制并粘贴回shell。

在Unix(和其他平台)上,使用bash/ksh语法进行引号处理,而在Windows上使用PowerShell语法。

何时使用此库?

此库最适合处理任意文件名或其他“脏”文本的命令行程序。例如,mv是您用于重命名具有问题名称的文件的工具,因此如果其消息能够很好地处理它们,那就很好。

不需要处理奇怪数据的程序不会得到太多好处。

输出是为了shell设计的,因此将其显示在例如GUI中可能没有意义。

大多数程序无需此库也能正常运行。您可能并不严格需要它,但它可能是一个很好的改进。

用法

导入Quotable特质

use os_display::Quotable;

此操作为常见的字符串类型(包括 OsStr)添加了两种方法:.quote().maybe_quote()。它们返回一个包装了自定义 Display 实现的 Quoted

.quote() 总是在文本周围添加引号

// Found file 'filename'
println!("Found file {}", "filename".quote());

// Found file "foo'bar"
println!("Found file {}", "foo'bar".quote());

// Unix: Found file $'foo\nbar'
// Windows: Found file "foo`nbar"
println!("Found file {}", "foo\nbar".quote());

.maybe_quote() 只有在需要(例如,由于空白或特殊字符)时才添加引号

// filename: Not found
println!("{}: Not found", "filename".maybe_quote());

// 'foo bar': Not found
println!("{}: Not found", "foo bar".maybe_quote());

// '*?$': Not found
println!("{}: Not found", "*?$".maybe_quote());

.quote() 最好用于较长的句子中,而 .maybe_quote() 可以用于已通过其他方式(如冒号)分隔的文本。

限制

  • Unicode 可以被引号包围,但只有控制字符会被转义。打印的文本可能仍然看起来很奇怪,一些(有缺陷的)终端会丢弃某些字符。
  • 该库不应该用于将文本插入到 shell 脚本中。它旨在提高可读性,而不是绝对的安全性。可以考虑使用 shell-escape crate(或者最好以其他方式传递值)。
  • 输出可能不与每个 shell 兼容。
  • PowerShell 在外部命令的参数中对待引号的方式不同。此库默认对内部命令let进行引号处理,这可能不是您想要的。使用 Quoted::external() 方法可以切换此行为。
  • 我不是 Unicode 专家。此 crate 的第一个版本存在多个疏忽,可能还有更多。

无效的 Unicode

在 Unix 上

use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;

// \xFF makes this invalid UTF-8, so to_str() would fail
let bad_string = OsStr::from_bytes(&[b'x', 0xFF, b'y']);
assert_eq!(bad_string.quote().to_string(), r#"$'x\xFFy'"#);

在 Windows 上

use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;

// 0xD800 is an unpaired surrogate, making this invalid UTF-16
let bad_string = OsString::from_wide(&[b'a' as u16, 0xD800, b'b' as u16]);
assert_eq!(bad_string.quote().to_string(), r#""a`u{D800}b""#);

零宽 Unicode

一些代码点宽度为零。它们可以使字符串变得不可见,或者使选择变得困难。GNU 工具难以处理这种情况

$ wc $'\u200b'
wc: ​: No such file or directory

在这种情况下,os_display 会添加引号

assert_eq!("\u{200B}".maybe_quote().to_string(), "'\u{200B}'");

当打印时,它仍然误导性地看起来像 '',但可以复制并粘贴它以获得正确的结果。

双向 Unicode

精心设计的字符串可以将它的一部分移动到行的末尾

$ wc $'filename\u202E\u2066 [This does not belong!]\u2069\u2066'
wc: 'filename': No such file or directory [This does not belong!]

这被称为 特洛伊源 攻击。它使用双向文本的控制代码。

如果它们没有正确终止,os_display 会转义这些控制代码。

功能标志

默认情况下,您只能使用当前平台引用的样式。这在大多数情况下是合适的。

windows/unix

可以启用 windowsunix 可选功能,以便为 Quoted 添加构造函数。

Quoted::unix("some string") 不论平台如何,都会使用 bash/ksh 语法进行引号处理,而 Quoted::windows("etc") 则使用 PowerShell 语法。

Quoted::unix_rawQuoted::windows_raw 分别接受 &[u8](用于不规则的 UTF-8)和 &[u16](用于不规则的 UTF-16)。

native

默认启用的 native 功能对于 Quotable 特性和 Quoted::native(&str)Quoted::native_raw(&OsStr) 构造函数是必需的。如果没有启用,则必须显式选择引号样式。

alloc/std

如果禁用了 alloc 和/或 std 功能,则此 crate 与 no_std 兼容。

要引号化 OsStr,需要 std 功能。要处理 Quoted::windows_raw,需要 alloc 功能。

备选构造函数

Quoted 不仅有特定的样式构造函数,还有 Quoted::native()Quoted::native_raw()。如果您更喜欢无聊的函数,则可以使用这些作为 Quotable 特性的替代。

默认情况下,总是添加引号。要获得类似 .maybe_quote() 的行为,请使用 .force() 方法。

println!("{}", Quoted::native(x).force(false));

测试

Unix 实现已针对 bash、zsh、mksh、ksh93 和 busybox 进行了模糊测试,以确保所有输出都被解释回原始字符串。它还针对 fish、dash、tcsh、posh 和 yash(不支持所有必需的语法)进行了更有限的模糊测试。

PowerShell 实现已针对在 Linux 上运行的 PowerShell Core 7.1.4 进行了模糊测试。

这两个实现都进行了模糊测试,以测试其针对特洛伊木马源攻击的保护。

致谢

此库是根据在 GNU coreutils 中看到的 Gnulib 中的引号方式构建的。但是,行为并不完全相同

  • GNU 使用八进制转义序列,如 \377 而不是 \xFF
  • GNU 会中途急切地切换引号样式,如 ''$'\n''xyz' 而不是 $'\nxyz'os_display 除非必要,否则避免这样做。
  • GNU 将未分配的代码点转义,而不是将其处理留给终端。
  • GNU 不会特别处理零宽度代码点。

此代码的第一版是为 uutils 项目 编写的。用户反馈以及在大型代码库中使用它的机会很有帮助。

依赖项

~370KB