可以将tr命令链接起来以避免流水线中有多个tr进程吗?


11

我有一堆txt文件,我想将它们输出为小写字母,只有字母和每行一个字,我可以tr在管道中使用多个命令来完成此操作,如下所示:

tr -d '[:punct:]' <doyle_sherlock_holmes.txt | tr '[:upper:]' '[:lower:]' | tr ' ' '\n'

可以一次扫描吗?我可以写一个C程序要做到这一点,但我觉得有一种方法使用去做trsedawkperl


您正在使用什么操作系统?您可以使用GNU工具吗?
terdon

Answers:


9

您可以合并多个翻译(复杂情况涉及重叠的依赖于语言环境的集合除外),但是不能将删除与翻译合并。

<doyle_sherlock_holmes.txt tr -d '[:punct:]' | tr '[:upper:] ' '[:lower:]\n'

两次调用tr可能比一次调用更复杂的工具快,但这很大程度上取决于输入的大小,不同字符的比例,tr工具的实现和竞争工具,操作系统,数量。核心等


我不确定是否要重新组合tr -s '[:upper:] [:punct:]' '[:lower:]\n' <doyle_sherlock_holmes.txt
Costas 2015年

1
@Costas会将标点符号转换为换行符。对于此特定应用程序可能没问题,但是输出与原始应用程序不同。
吉尔斯(Gillles)“所以-别再邪恶了”

@Costas-虽然换行符在这里可以使用,但我认为不应该压缩大写字符。例如:printf 'A.AAAA,A' | tr -s '[:upper:] [:punct:]' '[:lower:][\n*]'gets a\na\na'和,for的转换... '[:lower:]\n'可能不一定要做任何事情'[:punct:]'-有些trs会将set1截断为匹配2,有些将隐式[\n*]。最好只是在那里使用范围。
mikeserv

4

以下是几种方法:

  • GNU greptr:查找所有单词并使它们变为小写

    grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
  • GNU grep和perl:如上所述,但是perl可以处理小写转换

    grep -Po '\w+' file | perl -lne 'print lc()'
  • perl:查找所有字母字符并用小写字母打印(感谢@steeldriver):

    perl -lne 'print lc for /[a-z]+/ig' file
  • sed:删除所有非字母或空格的字符,用小写字母替换所有字母,并用换行符替换所有空格。请注意,这假设所有空格都是空格,没有制表符。

    sed 's/[^a-zA-Z ]\+//g;s/[a-zA-Z]\+/\L&/g; s/ \+/\n/g' file

2
这样的事情perl -lne 'print lc for /[[:alpha:]]+/g'也可以吗?还是风格不佳?(我是perl的新手,正在尝试学习!)
steeldriver 2015年

@steeldriver是的,不错!如果您正在学习Perl,我敢肯定您遇到过它的座右铭:TMTOWTDI :)谢谢,我将添加它。
terdon

3
使用新版本(> 4.2.1)sed -z 's/\W*\(\w\+\)\W*/\L\1\n/g'
Costas

@Costas啊,现在sed可以\w吗?凉!
terdon

@terdon-这样做已经有一段时间了,但是,因为Costas没有提到它,所以我认为上述注释中最有趣的是GNU sed-zero分隔开关-它遍历\0NULs而不是换行符。当您执行类似操作时,效果非常好tar -c . | tr -s \\0 | sed -z ...-但速度很慢。
mikeserv

4

是。您可以tr在ASCII语言环境中执行此操作(对于GNU而言tr,这只是其唯一的权限)。您可以使用POSIX类,也可以通过八进制数字引用每个字符的字节值。您也可以将其转换划分为多个范围。

LC_ALL=C tr '[:upper:]\0-\101\133-140\173-\377' '[:lower:][\n*]' <input

上面的命令会将所有大写字符转换为小写字母,完全忽略小写字符,并将所有其他字符转换为换行符。当然,然后您会遇到大量空白行。该tr -squeeze重复开关可能是在这种情况下是有用的,但是如果你使用它的旁边[:upper:][:lower:]转型,那么你拉闸挤压大写字符为好。这样,它仍然需要第二个过滤器,例如...

LC... tr ... | tr -s \\n

...要么...

LC... tr ... | grep .

...因此它比做起来要方便得多...

LC_ALL=C tr -sc '[:alpha:]' \\n <input | tr '[:upper:]' '[:lower:]'

... -c按顺序将整齐的字母字符压缩成单个换行符,然后在管道的另一侧进行从上到下的转换。

这并不是说那种范围是没有用的。像这样的东西:

tr '\0-\377' '[1*25][2*25][3*25][4*25][5*25][6*25][7*25][8*25][9*25][0*]' </dev/random

...非常方便,因为它可以将输入字节转换为值范围内的所有数字。不要浪费,不要浪费,你知道的。

进行转换的另一种方法可能涉及dd

tr '\0-\377' '[A*64][B*64][C*64][D*64]' </dev/urandom |
dd bs=32 cbs=8 conv=unblock,lcase count=1

dadbbdbd
ddaaddab
ddbadbaa
bdbdcadd

由于dd可以同时unblock进行lcase转换和转换,因此甚至有可能将大量工作转嫁给它。但这仅在您可以准确预测每个单词的字节数时才真正有用-或至少可以在每个单词之前用空格填充到可预测的字节数,因为unblock在每个块的末尾都吃了尾随空格。


+2 dd参与的奖励积分:)
tlehman 2015年

@TobiLehman-我很高兴您批准。
mikeserv
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.