仅打印字符串的最后 3 个字符的命令

ody*_*sey 36 text-processing cut

我知道该cut命令可以打印n字符串的第一个字符,但如何选择最后一个n字符?

如果我有一个字符数可变的字符串,如何只打印字符串的最后三个字符。例如。

需要的“无限”输出是“ted”
需要的“987654”输出是“654”
“123456789”需要的输出是“789”

G-M*_*ca' 61

为什么没有人给出明确的答案?

sed 's/.*\(...\)/\1/'
Run Code Online (Sandbox Code Playgroud)

……或者稍微不那么明显的

grep -o '...$'
Run Code Online (Sandbox Code Playgroud)

诚然,第二个的缺点是少于三个字符的行会消失;但问题没有明确定义这种情况下的行为。

  • @WChargin 你忘了`java -server -Xms300M -Xmx3G -XX:+UseParallelGC -cp /path/to/all/the/jars/ -Dinput.interactive=false -Dinput.pipe=true -Dconfig.file=/path/ to/config/last-three-letters.cfg -jar ...` (13认同)
  • @Kiro 或`"回声无限制" | java -jar EnterpriseWordTrimmer.jar`,但我认为没有必要为字符操作引入更重的语言。 (10认同)
  • `grep -o -P '.{0,3}$'` 将打印最后 3 个字符,即使该行少于 3 个字符。`-P` 避免转义大括号。 (8认同)
  • 或 `grep -o '.\{3\}$'` (7认同)
  • 或`echo "无限" | python -c "p​​rint raw_input()[-3:]"` (3认同)
  • 没错,但是,在这种情况下,这种“省力的设备”需要更多的打字。:-) (2认同)
  • @hjk 我们应该审查内存要求;并尽量不要陷入使用哪些 GC 选项的斗争...... (2认同)

Vol*_*gel 54

保持简单 - 尾巴

我们不应该只需要一个正则表达式或多个进程来计算字符数。
命令tail,通常用于显示文件的最后几行,有一个选项-c( --bytes),这似乎是解决此问题的正确工具:

$ printf 123456789 | tail -c 3
789
Run Code Online (Sandbox Code Playgroud)

(当您在 shell 中时,使用 mikeserv 的回答中的方法是有意义的,因为它可以节省启动进程tail。)

真正的 Unicode 字符?

现在,您要求输入最后三个字符;这不是这个答案给你的:它输出最后三个字节

只要每个字符是一个字节,tail -c就可以工作。因此,如果字符集是或变体ASCII,则可以使用它ISO 8859-1

如果你有 Unicode 输入,就像普通UTF-8格式一样,结果是错误的:

$ printf 123??? | tail -c 3
??
Run Code Online (Sandbox Code Playgroud)

在这个例子中,使用UTF-8,希腊字符 alpha、beta 和 gamma 是两个字节长:

$ printf 123??? | wc -c  
9
Run Code Online (Sandbox Code Playgroud)

该选项-m至少可以计算真正的 unicode 字符:

printf 123??? | wc -m
6
Run Code Online (Sandbox Code Playgroud)

好的,所以最后 6 个字节将为我们提供最后 3 个字符:

$ printf 123??? | tail -c 6
???
Run Code Online (Sandbox Code Playgroud)

所以,tail不支持处理一般字符,它甚至不尝试(见下文):它处理可变大小的行,但不处理可变大小的字符。

这么说吧:tail对于要解决的问题的结构来说是正确的,但是对于数据的类型是错误的。

GNU核心工具

进一步看,事实证明,你的coreutils GNU的基本工具,收集喜欢的sedlstail并且cut,还没有完全国际化。这主要是关于支持Unicode。
例如,cut在这里使用而不是尾部来支持字符将是一个很好的候选者;它确实有处理字节或字符的选项,-c( --bytes) 和-m( --chars);

只有那个-m/--chars是,从
cut (GNU coreutils) 8.212013版本开始,
没有实现!

来自info cut

`-c CHARACTER-LIST'
`--characters=CHARACTER-LIST'
     Select for printing only the characters in positions listed in CHARACTER-LIST.  
     The same as `-b' for now, but internationalization will change that.
Run Code Online (Sandbox Code Playgroud)


另请参阅此回答无法用UTF-8使用'砍-c`(`--characters`)?.

  • 实际上,只要当前语言环境指定 UTF-8 编码,大多数其他答案似乎都能很好地处理 Unicode。只有你和格伦杰克曼的基于`cut` 的解决方案似乎没有。 (2认同)
  • 请注意,POSIX 明确指定 `tail` 应该处理字节,而不是字符。我曾经制作了一个补丁来添加一个新选项来选择字符,但我相信从未合并过:-/ (2认同)

Dop*_*oti 37

如果您的文本位于名为 的 shell 变量中STRING,您可以在bash,zshmkshshell 中执行此操作:

printf '%s\n' "${STRING:(-3)}"
Run Code Online (Sandbox Code Playgroud)

或者

printf '%s\n' "${STRING: -3}"
Run Code Online (Sandbox Code Playgroud)

这也有利于与该语法来自的 ksh93 一起使用。

重点是:必须与 分开-,否则它就成为${var:-default}Bourne shell的操作符。

zshyashshell 中的等效语法是:

printf '%s\n' "${STRING[-3,-1]}"
Run Code Online (Sandbox Code Playgroud)

  • 它被称为**子串扩展**。这是一种**参数扩展**。一般形式是 _${parameter:offset:length}_,但 _length_ 字段是可选的(并且,如您所见,在上面的答案中已被省略)。DopeGhoti 也可以写成 `${STRING:(-3):3}`(指定 _length_ 字段),`${STRING: -3}`(`:` 和 `-` 之间有一个空格),或`${STRING: -3:3}`。 (6认同)
  • 这种语法/操作称为什么,以便我可以搜索更多信息? (2认同)

jas*_*yan 14

使用awk

awk '{ print substr( $0, length($0) - 2, length($0) ) }' file
ted
654
789
Run Code Online (Sandbox Code Playgroud)


mik*_*erv 11

如果字符串在变量中,您可以执行以下操作:

printf %s\\n "${var#"${var%???}"}"
Run Code Online (Sandbox Code Playgroud)

$varlike的值中去除最后三个字符:

${var%???}
Run Code Online (Sandbox Code Playgroud)

...然后从$var所有东西的头部剥离,刚刚剥离的内容如下:

${var#"${var%???}"}
Run Code Online (Sandbox Code Playgroud)

这种方法有其优点和缺点。从好的方面来说,它是完全 POSIX 可移植的,应该可以在任何现代 shell 中工作。另外,如果$var不包含至少三个字符没什么,但后\n打印ewline。再说一次,如果你在这种情况下打印它,你需要一个额外的步骤,比如:

last3=${var#"${var%???}"}
printf %s\\n "${last3:-$var}"
Run Code Online (Sandbox Code Playgroud)

$last3只有在$var包含 3 个或更少字节时,这种方式才会为空。并且$var只替换$last3if$last3为空或unset- 我们知道这不是unset因为我们只是设置了它。

  • @DopeGhoti - 只是因为这是我几乎从未做过的假设。这在“bash”中与在任何其他声称具有 POSIX 兼容性的 shell 中一样有效。 (3认同)
  • @odyssey - 不幸的是,在我在这里提到的*现代的、兼容 POSIX 的* shell 中,问题是 `csh` 不是*。POSIX-shell 规范是根据 `ksh` 建模的,它是在结合了 `csh` 和传统的 Bourne 风格的 shell 之后建模的。`ksh` 结合了 `csh` 出色的作业控制功能和旧的 Bourne 风格的 i/o 重定向。它还添加了一些东西——比如我上面演示的字符串操作概念。据我所知,这在任何传统的 `csh` 中都行不通,我很抱歉地说。 (3认同)

gle*_*man 7

你可以这样做,但这有点......过度:

for s in unlimited 987654 123456789; do
    rev <<< $s | cut -c 1-3 | rev
done 
Run Code Online (Sandbox Code Playgroud)
ted
654
789
Run Code Online (Sandbox Code Playgroud)