修剪一行的最后3个字符而不使用sed或perl等

我有一个shell脚本输出这样的数据:

1234567890 * 1234567891 * 

我需要删除只是最后三个字符“*”。 我知道我可以通过

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

但我不想使用sed来达到速度的目的。 它将始终是最后3个字符。

任何快速清理输出的方法?

假设所有的数据都像你的例子那样格式化,使用' cut '来获得第一列。

 cat $file | cut -d ' ' -f 1 

或得到前10个字符。

 cat $file | cut -c 1-10 

这是一个老式的unix技巧,用于删除不使用sed或awk的行中的最后3个字符。

 > echo 987654321 | rev | cut -c 4- | rev 987654 

与之前使用“剪切”的示例不同,这不需要知道行长。

我可以向你保证, bash本身不会比sed更快。 在bash启动外部进程通常是一个糟糕的主意,但只有当你做了很多事情。

所以,如果你为每一行input启动一个sed进程,我会担心的。 但你不是。 你只需要启动一个 sed来完成所有的工作。

你可能会发现下面的sed会比你的版本快一点:

 (whatever) | sed 's/...$//' 

所有这些都是删除每行上的最后三个字符,而不是用一个较短的版本replace整行。 现在也许更现代的引擎可以优化你的命令,但为什么要冒这个风险。

说实话,我能想到的唯一方法就是更快地手工制作自己的基于C的filter程序。 唯一的原因可能sed更快,因为你可以利用你对处理需求的额外知识( sed必须允许广义游戏,所以可能会因此而变慢)。

不要忘记优化的口头禅: “措施,不要猜测!”


如果你真的想在bash一次做这一行(我仍然认为这是一个坏主意),你可以使用:

 pax> line=123456789abc pax> line2=${line%%???} pax> echo ${line2} 123456789 pax> _ 

你也可能想调查你是否真的需要提高速度。 如果你将这些行加工成一个大块,你会发现sed速度很快。 input以下内容:

 #!/usr/bin/bash echo This is a pretty chunky line with three bad characters at the end.XXX >qq1 for i in 4 16 64 256 1024 4096 16384 65536 ; do cat qq1 qq1 >qq2 cat qq2 qq2 >qq1 done head -20000l qq1 >qq2 wc -l qq2 date time sed 's/...$//' qq2 >qq1 date head -3l qq1 

并运行它。 这里是我的(不是很快)R40笔记本电脑的输出:

 pax> ./chk.sh 20000 qq2 Sat Jul 24 13:09:15 WAST 2010 real 0m0.851s user 0m0.781s sys 0m0.050s Sat Jul 24 13:09:16 WAST 2010 This is a pretty chunky line with three bad characters at the end. This is a pretty chunky line with three bad characters at the end. This is a pretty chunky line with three bad characters at the end. 

这是一秒钟内的2万行,对于每小时只能完成的事情来说是相当不错的。

 $ x="can_haz" $ echo "${x%???}" can_ 

awksed都很快,但是如果你认为它很重要,可以使用下面的一种方法:

如果您要删除的字符总是在string的末尾

 echo '1234567890 *' | tr -d ' *' 

如果他们可以出现在string的任何地方,你只想在最后删除

 echo '1234567890 *' | rev | cut -c 4- | rev 

所有命令的手册页将解释发生了什么事情。

不过,我认为你应该使用sed

注意:这个答案有点意思是一个笑话,但它确实工作…

 #!/bin/bash outfile="/tmp/$RANDOM" cfile="$outfile.c" echo '#include <stdio.h> int main(void){int e=1;char c;while((c=getc(stdin))!=-1){if(c==10)e=1;if(c==32)e=0;if(e)putc(c,stdout);}}' >> "$cfile" gcc -o "$outfile" "$cfile" rm "$cfile" cat somedata.txt | "$outfile" rm "$outfile" 

你可以用不同的命令replacecat somedata.txt

你可以试试

 (whatever) | while read line; do echo $line | head --bytes -3; done; 

因为没有正则expression式或者分隔符匹配,所以head本身应该比sed或者更快,但是为每一行分别调用可能会超过这个。

如果脚本始终输出10个字符的行,然后是3个额外的(换句话说,您只需要前10个字符),则可以使用

 script | cut -c 1-10 

如果输出不确定数量的非空格字符,然后input一个空格,然后输出另外两个字符(换句话说,您只需要第一个字段),则可以使用

 script | cut -d ' ' -f 1 

……就像之前的majhool评论。 根据你的平台,你也可能有colrm,如果这些行是固定长度的话,它也可以工作:

 script | colrm 11 

另一个答案依赖于倒数第三个字符是一个空格。 这将与(几乎)在该位置的任何字符,并做到“没有使用sed,或perl等”:

 while read -r line do echo ${line:0:${#line}-3} done 

如果您的线路长度固定,则将echo改为:

 echo ${line:0:9} 

要么

 printf "%.10s\n" "$line" 

但是其中的每一个肯定sed慢得多。

你可以使用awk来打印第一个'field',如果没有空格(或者如果有的话,改变分隔符)。

我把上面的字段放到一个文件中,然后做到这一点

 awk '{ print $1 }' < test.txt 1234567890 1234567891 

我不知道这是否更好。

你是什​​么意思不想使用sed / awk来达到速度的目的? sed / awk比读取循环处理文件时的shell速度更快。

 $ sed 's/[ \t]*\*$//' file 1234567890 1234567891 $ sed 's/..\*$//' file 1234567890 1234567891 

与bashshell

 while read -rab do echo $a done <file 

不需要剪切或魔术,在bash中你可以像这样剪切一个string:

  ORGSTRING="123456" CUTSTRING=${ORGSTRING:0:-3} echo "The original string: $ORGSTRING" echo "The new, shorter and faster string: $CUTSTRING" 

请参阅http://tldp.org/LDP/abs/html/string-manipulation.html