如何让'cut'命令将相同的连续分隔符视为一个处理?
最近,我对于`cut`命令的限制感到非常沮丧,因此我自己写了一个替代品,称为“cuts”,可以看作是增强版的`cut`命令。`cuts`提供了对于这个问题以及其他许多相关的切割/粘贴问题的最简单的解决方案。
一个例子就是解决这个特定问题:
$ cat text.txt 0 1 2 3 0 1 2 3 4 $ cuts 2 text.txt 2 2
`cuts`支持以下功能:
- 自动检测文件中最常见的字段分隔符(也可以手动设置)
- 多字符、混合字符和正则表达式匹配的分隔符
- 从具有不同分隔符的多个文件中提取列
- 支持从行尾(使用负数)和行首提取列
- 自动将列并排粘贴在一起(无需单独调用`paste`命令)
- 支持字段重新排序
- 用户可以通过配置文件更改个人偏好设置
- 非常注重用户友好性和最小化输入命令的要求
`cuts`提供的功能远远超过了标准的`cut`命令。
可以参考以下链接了解更多信息:
- [https://stackoverflow.com/a/24543231/1296044](https://stackoverflow.com/a/24543231/1296044)
- 代码和文档(免费软件):[http://arielf.github.io/cuts/](http://arielf.github.io/cuts/)
如您在问题中所评论的,awk确实是最好的选择。使用cut结合tr -s来压缩空格也是一种可能的方法,正如kev的回答所示。然而,让我通过所有可能的组合来为未来的读者解释一下。下面是测试部分的解释。
tr | cut
$ tr -s ' ' < file | cut -d' ' -f4
awk
$ awk '{print $4}' file
bash
$ while read -r _ _ _ myfield _
do
echo "forth field: $myfield"
done < file
sed
$ sed -r 's/^([^ ]*[ ]*){3}([^ ]*).*/\2/' file
测试
给定以下文件,我们来测试一下命令:
$ cat a
this is line 1 more text
this is line 2 more text
this is line 3 more text
this is line 4 more text
tr | cut
$ cut -d' ' -f4 a
is
# 它没有显示我们想要的内容!
$ tr -s ' ' < a | cut -d' ' -f4
1
2 # 这样可以!
awk
$ awk '{print $4}' a
1
2
3
4
bash
这个命令按顺序读取字段。通过使用_来表示这是一个丢弃的变量,我们忽略这些字段。通过这种方式,我们把文件的第四个字段存储为$myfield,无论它们之间有多少个空格。
$ while read -r _ _ _ a _; do echo "4th field: $a"; done < a
4th field: 1
4th field: 2
4th field: 3
4th field: 4
sed
这个命令使用([^ ]*[ ]*){3}来捕获三个空格组和没有空格。然后,它捕获直到空格的任何内容作为第四个字段,并最终用\2打印出来。
$ sed -r 's/^([^ ]*[ ]*){3}([^ ]*).*/\2/' a
1
2
3
4
awk不仅简洁而且简单,而且它还包含在VMware ESXi中,而tr则不包含在内。这是使用awk的另一个原因!我从Greg的How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?中学到的,他说:有些人使用丢弃变量_作为"垃圾变量"来忽略字段。它(或者实际上是任何变量)也可以在单个read命令中使用多次,如果我们不关心它存储的内容。它可以是任何东西,只是因为它在某种程度上成为了标准,而不是junk_var或者whatever :)在JavaScript中,它也表示不打算使用的函数参数。
问题出现的原因是cut命令无法将连续的分隔符视为一个处理单元,而是将其视为多个分隔符。解决方法是使用tr命令将连续的分隔符替换为一个分隔符,然后再使用cut命令进行处理。
解决方法如下:
tr -s ' '
根据tr命令的man页面,使用"-s"参数可以将重复的字符序列替换为单个字符。因此,通过将连续的分隔符替换为一个分隔符,我们可以使cut命令正常处理。
不需要使用cat命令,可以直接将"
如果要合并多个字符并将其转换为制表符,也可以不使用cut命令的"-d"参数直接使用tr命令。例如:
who am i | tr -s ' ()' '\t' | cut -f5
这种方法不会删除开头和结尾的空格(可能是需要的,但通常不需要),与awk解决方法不同。与awk解决方法相比,这种方法更易读且更简洁。
需要注意的是,有一位用户指出这种方法并不等同于将连续的分隔符视为一个处理单元。比较以下两个例子:
echo "a b c" | cut -d " " -f2- echo "a b c" | tr -s " " | cut -d " " -f2-
第一个例子中,使用cut命令的"-f2-"参数,结果为4个字段:'a','b',''和'c'。而在第二个例子中,使用tr和cut命令的结果只有3个字段:'a','b'和'c'。
然而,另一位用户指出这两个例子是等价的。通过将"-f2-"参数改为"-f3-",可以看出在只使用cut命令的情况下,有4个字段:'a','b',''和'c',而在使用tr和cut命令的情况下,只有3个字段:'a','b'和'c'。
因此,可以得出结论,使用tr命令将连续的分隔符替换为一个分隔符,可以实现将连续的分隔符视为一个处理单元的效果。