Contents

如何使用 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-idDateCostDetails两列之间的分隔符是三个空格字符。

现在,假设我们想从输入中提取日期成本 数据。也就是说,我们需要字段 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.  awkcut更灵活、更强大

到目前为止,我们已经使用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

现在,我们通过为FSOFS变量设置三个空格来保留输出中的原始字段分隔符。

其次,假设我们仍想提取这三列,但我们想将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可以灵活的控制处理逻辑和输出。