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


30

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

如果我的字符串中的字符数可变,那么如何仅显示字符串的后三个字符。例如。

所需的“无限”输出为“ ted”
所需的“ 987654”输出为“ 654”
所需的“ 123456789”输出为“ 789”

Answers:


52

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

sed 's/.*\(...\)/\1/'

……或不太明显

grep -o '...$'

诚然,第二个缺点是,少于三个字符的行消失了。但问题并未明确定义此案例的行为。


6
grep -o '.\{3\}$'
Avinash Raj 2014年

3
echo "unlimited" | python -c "print raw_input()[-3:]"
Kiro 2014年

8
@Kiro或"echo unlimited" | java -jar EnterpriseWordTrimmer.jar,但是我认为并不需要引入更重的语言来进行字符操作。
wchargin 2014年

11
@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 ...
HJK 2014年

6
grep -o -P '.{0,3}$'即使该行少于3个字符,也将打印最后3个字符。-P避免必须逃脱括号。
拉格·多达

43

保持简单-尾巴

我们不应该只需要一个正则表达式或一个以上的过程来计算字符。
该命令tail通常用于显示文件的最后几行,它具有一个选项-c--bytes),它似乎恰恰是实现此目的的正确工具:

$ printf 123456789 | tail -c 3
789

(在外壳程序中时,使用类似mikeserv的方法是有意义的,因为这样可以省去启动的过程tail。)

真正的Unicode字符?

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

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

如果您使用通用UTF-8格式的Unicode输入,则结果是错误的:

$ printf 123αβγ | tail -c 3
�γ

在此示例中,使用UTF-8,希腊字母alpha,beta和gamma是两个字节长:

$ printf 123αβγ | wc -c  
9

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

printf 123αβγ | wc -m
6

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

$ printf 123αβγ | tail -c 6
αβγ

因此,tail它不支持处理常规字符,甚至不尝试(请参见下文):它处理大小可变的行,但不处理大小可变的字符。

让我们这样说:tail对于要解决的问题的结构来说是正确的,而对于数据的类型则是错误的。

GNU coreutils

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

只有-m/ --chars时,作为版本
cut (GNU coreutils) 8.21,2013年,
没有实现!

来自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.


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


2
实际上,只要当前语言环境指定UTF-8编码,其他大多数答案似乎就可以很好地处理Unicode。cut似乎只有您和glenn jackman的解决方案似乎没有。
Ilmari Karonen 2014年

@IlmariKaronen对,谢谢。我已经编辑,还有一些其他细节。
Volker Siegel 2014年

1
请注意,POSIX明确指定tail应该处理字节而不是字符。我曾经打过补丁,添加了一个新的选项来选择字符,但是我相信从来没有合并过:-/
Martin Tournoij 2015年

无法在文件模式下工作,例如tail -c3 -n10 /var/log/syslog
Suncatcher,

我尝试了@Suncatcher,它奏效了。您看到什么问题?您的命令tail -c3 -n10 /var/log/syslog要求最后10行,这对我有用。您使用该选项-c3,然后使用冲突的选项-n10。后面的选项优先。
Volker Siegel '18

36

如果您的文本是在被称为shell变量STRING,你可以在一个做到这一点bashzshmksh外壳:

printf '%s\n' "${STRING:(-3)}"

要么

printf '%s\n' "${STRING: -3}"

这也可以与语法来自的ksh93一起使用。

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

zshyashshell中的等效语法为:

printf '%s\n' "${STRING[-3,-1]}"

2
这样调用的语法/运算符是什么,以便我可以搜索更多信息?
图兰斯·科尔多瓦

6
这称为Substring Expansion。这是一种参数扩展。通用格式为$ {parameter:offset:length},但是length字段是可选的(如您所见,在上面的答案中已将其省略)。DopeGhoti也可能已经写了${STRING:(-3):3}(指定长度字段),${STRING: -3}(在:和之间有一个空格-)或${STRING: -3:3}
G-Man说'Resstate Monica''Apr

在这种情况下,指定的长度3有点麻烦,因为它要求“从最后一个字符起第三个字符,包括最后一个字符,这三个字符”实际上与“从最后一个字符起第三个字符开始的所有字符”相同, 包括的”。
DopeGhoti

13

使用awk

awk '{ print substr( $0, length($0) - 2, length($0) ) }' file
ted
654
789

11

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

printf %s\\n "${var#"${var%???}"}"

这样会从$varlike 的值中去除最后三个字符:

${var%???}

...然后从$var所有东西的头部剥离,只是像这样剥离:

${var#"${var%???}"}

这种方法有其优点和缺点。从好的方面来说,它是完全POSIX便携式的,并且可以在任何现代外壳中使用。另外,如果$var不包含至少三个字符没什么,但后\n打印ewline。再一次,如果您希望在这种情况下将其打印出来,则需要执行以下附加步骤:

last3=${var#"${var%???}"}
printf %s\\n "${last3:-$var}"

这样,$last3如果$var包含3个或更少的字节,则永远为空。并且$var仅替换为$last3if $last3为空或unset-并且我们知道不是unset因为我们只是设置了它而已。


+1非常整洁。除了:您不引用printf格式字符串的任何原因是什么?
2014年

为什么不仅仅使用${VARNAME:(-3)}(假定bash)?
DopeGhoti 2014年

1
感谢您的澄清;是有道理的,即使(在我看来)有点奇怪...
jasonwryan 2014年

1
@DopeGhoti-仅仅因为这是我几乎从未做过的假设。这与bash声称POSIX可映射性的任何其他外壳一样有效。
mikeserv

3
@odyssey -问题是,csh不是跻身现代,POSIX兼容的壳我在这里提到,很遗憾。POSIX-shell规范是根据建模的ksh,该规范是在结合csh了传统的Bourne风格的外壳之后进行建模的。ksh结合了csh出色的作业控制功能和旧的Bourne风格的I / O重定向。它还增加了一些东西-例如我上面演示的字符串操作概念。csh我很遗憾地说,就我所知,这不可能在任何传统中都可行。
mikeserv

7

您可以这样做,但这有点...过多:

for s in unlimited 987654 123456789; do
    rev <<< $s | cut -c 1-3 | rev
done 
ted
654
789

3

utf-8字符串的防弹解决方案:

utf8_str=$'\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82' # привет

last_three_chars=$(perl -CAO -e 'print substr($ARGV[0], -3)' "$utf8_str")

或使用:

last_three_chars=$(perl -MEncode -CO -e '
  print substr(decode("UTF-8", $ARGV[0], Encode::FB_CROAK), -3)
' "$utf8_str")

防止格式错误的数据处理。

例:

perl -MEncode -CO -e '
  print substr(decode("UTF-8", $ARGV[0], Encode::FB_CROAK), -3)
' $'\xd0\xd2\xc9\xd7\xc5\xd4' # koi8-r привет

输出如下所示:

utf8 "\xD0" does not map to Unicode at /usr/lib/x86_64-linux-gnu/perl/5.20/Encode.pm line 175.

不依赖于语言环境设置(即与配合使用LC_ALL=C)。Bashsedgrepawkrev要求是这样的:LC_ALL=en_US.UTF-8

常见解决方案:

  • 接收字节
  • 检测编码
  • 解码字节为字符
  • 提取字符
  • 将字符编码为字节

您可以使用uchardet检测编码。另请参阅相关项目

您可以使用Perl中的Encode和Python 2.7中的编解码器进行解码/编码

范例

从utf-16le字符串中提取最后三个字符,并将这些字符转换为utf-8

utf16_le_str=$'\xff\xfe\x3f\x04\x40\x04\x38\x04\x32\x04\x35\x04\x42\x04' # привет

chardet <<<"$utf16_le_str"  # outputs <stdin>: UTF-16LE with confidence 1.0

last_three_utf8_chars=$(perl -MEncode -e '
    my $chars = decode("utf-16le", $ARGV[0]);
    my $last_three_chars = substr($chars, -3);
    my $bytes = encode("utf-8", $last_three_chars);
    print $bytes;
  ' "$utf16_le_str"
)

另请参阅:perlunitutPython 2 Unicode HOWTO


echo是您的防弹源吗?
mikeserv

@mikeserv,decode/encode是我的防弹源。整理我的答案。
Evgeny Vereshchagin 2015年

这也取决于语言环境设置以确保其正常工作,因为一组字节可能会在不同的字符集中反映不同的字符。它“作品”的LC_ALL=C,因为这是一个非常“愚蠢”的设置,但是当你试图将一个UTF-8字符串传递给SHIFT-5它可能会破坏或SHIFT-5串KOI8等
马丁Tournoij

@Carpetsmoker,谢谢。您能解释一下您的评论吗?我想那perl -CAO -e 'print substr($ARGV[0], -3)'很好。A@ARGV元素应为以UTF-8编码的字符串,OSTDOUT将为UTF-8。
Evgeny Vereshchagin 2015年

似乎您在说要分配给utf8_str
Evgeny Vereshchagin

1

使用“ expr”或“ rev”怎么样?

@ G-Man提供的答案类似:expr "$yourstring" : '.*\(...\)$' 它具有与grep解决方案相同的缺点。

一个众所周知的技巧是将“ cut”与“ rev”结合使用: echo "$yourstring" | rev | cut -n 1-3 | rev


rev解决方案看起来很像格伦
·

您说得对@Jeff_Schaller:我想念glenn的一个:-(
gildux 2015年

0

使用以下命令获取字符串的大小:

size=${#STRING}

然后获取最后n个字符的子字符串:

echo ${STRING:size-n:size}

例如:

STRING=123456789
n=3
size=${#STRING}
echo ${STRING:size-n:size}

会给:

789


-1

如果字符串中包含空格,printf将不起作用。

下面的代码带空格

str="Welcome to Linux"
echo -n $str | tail -c 3

努克斯


嗯,如果printf不工作,那么你正在做的事情非常错误的。
库沙兰丹

1
@Kusalananda:根据Saurabh显示的命令,他们尝试了printf $str(而不是printf "$str"or printf '%s' "$str")。而且,是的,这printf $str非常错误的。(echo -n $str不是好多了。)
G-Man说“恢复莫妮卡”
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.