如何使用 cut 指定更多空格作为分隔符
1. 概述
cut 命令 擅长处理基于列的文本。但是,它只支持一个字符作为分隔符。
在本教程中,我们将讨论在使用cut时如何处理由多个空格分隔的基于列的输入数据。
2. 问题介绍
像往常一样,让我们通过一个例子来理解这个问题。我们先来看看我们的输入文件:
$ cat orders.txt
Order-id Date Cost(USD) Details
1 2022-02-20 200 Orange 100kg
2 2022-02-21 300 Apple 250kg
3 2022-02-22 250 Apple 100kg and Orange 100kg
orders.txt文件 包含四列:Order-id、Date、Cost和Details。两列之间的分隔符是三个空格字符。
现在,假设我们想从输入中提取日期和成本 数据。也就是说,我们需要字段 2 和 3。那么,我们先尝试使用cut命令获取它:
$ cut -d" " -f2,3 orders.txt
cut: the delimiter must be a single character
Try 'cut --help' for more information.
我们尝试在上面的命令中设置三个空格字符作为字段分隔符。不幸的是,输出显示它没有按预期工作,错误消息很清楚:cut only allows one character to be the delimiter。
接下来我们看看如何解决这个问题,得到我们预期的数据。
3. 使用 tr命令压缩空格
我们了解到cut命令只接受一个字符作为字段分隔符。因此,解决该问题的一个想法是将“三个空格分隔值”格式转换为“一个空格分隔值”格式。
tr 实用程序 可以从标准输入 (Stdin) 读取字节流,转换或删除字符,然后将结果写入标准输出 (Stdout)。此外,** tr可以使用*-s*选项压缩重复字符 **。
我们可以“挤压”连续的空格字符,将三个空格变成一个空格:
$ tr -s " " <orders.txt
Order-id Date Cost(USD) Details
1 2022-02-20 200 Orange 100kg
2 2022-02-21 300 Apple 250kg
3 2022-02-22 250 Apples 100kg and Oranges 100kg
如上面的命令所示,我们用一个单独的字符替换了三个连续的空格字符。
值得一提的是,我们已经在命令中将orders.txt文件重定向到 Stdin,因为tr不能直接读取文件——它只能从 Stdin 读取输入。
接下来,让我们将此输出通过管道传递给 cut命令并提取两个必填字段:
$ tr -s " " <orders.txt | cut -d " " -f 2,3
Date Cost(USD)
2022-02-20 200
2022-02-21 300
2022-02-22 250
很好,上面的输出表明我们已经解决了问题。但值得一提的是**,如果其中一列包含空格,这个解决方案可能会有问题**。我们将在后面的部分中仔细研究这个问题。
正如我们所见,cut命令擅长处理基于列的输入。接下来,我们将介绍另一种解决方案,它具有比 cut 更强大的命令行实用程序。
4. 使用 awk命令
awk 是处理文本的好工具,尤其是在输入是基于列的情况下。此外, awk涵盖了cut可以执行的所有功能 。
默认情况下,awk将正则表达式*[ \t\n]+作为字段分隔符(FS)。换句话说,** awk默认将连续的空白字符视为FS*。**
因此,我们可以一次性解决问题:
$ awk '{ print $2, $3 }' orders.txt
Date Cost(USD)
2022-02-20 200
2022-02-21 300
2022-02-22 250
上面的命令非常简洁明了。awk使用默认的FS来解析输入并打印出第二个和第三个字段。
接下来,让我们稍微扩展一下需求,感受一下awk的强大。
5. awk比cut更灵活、更强大
到目前为止,我们已经使用cut和 awk解决了这个问题。现在,假设我们想再提取一个字段,即 “详细信息”列,以了解成本是多少。
首先,让我们使用我们的 tr | cut方法解决它 :
$ tr -s " " <orders.txt | cut -d " " -f 2,3,4
Date Cost(USD) Details
2022-02-20 200 Orange
2022-02-21 300 Apple
2022-02-22 250 Apples
正如我们在上面的命令中看到的,我们在 cut命令中添加了一个*“4”来提取第四个字段。有效。Details列出现在*输出中。然而,如果我们仔细检查输出,我们会发现“details”值被截断了。这是因为值包含空格字符。即使我们将三个空格“压缩”为一个,cut命令仍然无法判断空格是字段分隔符还是字段值中的字符。
另一方面,** awk命令可以通过小的调整轻松处理这种情况**:
$ awk -F" " '{print $2,$3,$4}' orders.txt
Date Cost(USD) Details
2022-02-20 200 Orange 100kg
2022-02-21 300 Apple 250kg
2022-02-22 250 Apples 100kg and Oranges 100kg
我们没有在上面的awk命令中使用默认的FS值。相反,我们将这三个空间设置为FS。正如我们所看到的,详细信息列已被完整打印。
当然,awk可以做的远不止这些。
接下来,让我们看几个简单的例子来适应不同的需求。
如果我们回顾一下我们得到的输出,虽然我们已经解决了问题,但输出不再是“三个空格分隔值”的格式。所以,首先,让我们在输出中保留该格式:
$ awk 'BEGIN{ FS=OFS=" "}{print $2,$3,$4}' orders.txt
Date Cost(USD) Details
2022-02-20 200 Orange 100kg
2022-02-21 300 Apple 250kg
2022-02-22 250 Apples 100kg and Oranges 100kg
现在,我们通过为FS和OFS变量设置三个空格来保留输出中的原始字段分隔符。
其次,假设我们仍想提取这三列,但我们想将Cost列放在最前面:
$ awk 'BEGIN{ FS=OFS=" "}{print $3, $2, $4}' orders.txt
Cost(USD) Date Details
200 2022-02-20 Orange 100kg
300 2022-02-21 Apple 250kg
250 2022-02-22 Apples 100kg and Oranges 100kg
最后,让我们仅在Cost值大于200时才打印订单 :
$ awk 'BEGIN{ FS=OFS=" "}NR==1 || $3>200 {print $3, $2, $4}' orders.txt
Cost(USD) Date Details
300 2022-02-21 Apple 250kg
250 2022-02-22 Apples 100kg and Oranges 100kg
从上面的例子我们可以看出,awk可以灵活的控制处理逻辑和输出。