从Linux中的文件中读取特定行
1. 概述
当我们使用 Linux 命令行时,读取文本文件是一种常见的操作。有时,我们知道文件中的第 X 行包含有趣的数据,我们只想读取第 X 行。
在本快速教程中,我们将了解从文件中读取特定行的不同方法。
2. 问题介绍
问题很简单。让我们通过一个例子得到一个更清晰的画面。 例如,我们有一个名为input.txt的文件:
$ nl input.txt
1 I am line 1, I don't have any interesting data.
2 I am line 2, I don't have any interesting data.
3 I am line 3, I don't have any interesting data.
4 I am line 4, I don't have any interesting data.
5 I am line 5, interesting data: Linux is awesome!
6 I am line 6, I don't have any interesting data.
7 I am line 7, I don't have any interesting data.
如上面的输出所示,我们使用nl 命令打印带有行号的文件内容。
我们知道input.txt文件在第五行包含了一些有趣的信息。因此,我们只想阅读第五行。
在 Linux 命令行中有很多方法可以做到这一点。在本教程中,我们将探讨四种方法:
接下来,让我们看看他们的行动。
3. 使用纯 Bash 命令
为了解决这个问题,让我们创建一个 shell 脚本getLine.sh:
$ cat getLine.sh
#!/bin/bash
FILE="$1"
LINE_NO=$2
i=0
while read line; do
i=$(( i + 1 )
test $i = $LINE_NO && echo "$line";
done <"$FILE"
上面的 shell 脚本看起来很简单。它接受两个参数:文件和目标行号。
基本上,它只包含一个循环。在循环中,我们增加一个计数器 变量*$i*。当它达到给定的目标行号时,我们输出该行。例如,如果我们使用input.txt文件运行脚本:
$ ./getLine.sh input.txt 5
I am line 5, interesting data: Linux is awesome!
输出显示预期的行已被打印。我们的脚本有效。
如果我们仔细阅读脚本,我们可能会发现它还有优化的空间。
**我们在循环中检查文件中的每一行,即使我们已经找到并打印了我们需要的行。**好吧,如果我们使用 input.txt运行这个脚本,这不是问题。毕竟,我们的示例输入文件只有七行。但是,在现实世界中,我们可能会处理 700 万行的文件。
因此,如果我们能在找到目标行之后打破循环就好了。所以,让我们稍微改变一下脚本:
$ cat getLine2.sh
#!/bin/bash
FILE="$1"
LINE_NO=$2
i=0
while read line; do
i=$(( i + 1 ))
case $i in $LINE_NO) echo "$line"; break;; esac
done <"$FILE"
**一旦找到我们需要的行,我们就使用case语句来中断循环。**让我们测试一下:
$ ./getLine2.sh input.txt 5
I am line 5, interesting data: Linux is awesome!
它也有效。所以,我们用一个小 Bash 脚本解决了这个问题。
4. 使用sed 命令
sed命令非常擅长解决这类问题。 让我们看看几个紧凑的sed one-liners 来完成这项工作:
$ sed '5!d' input.txt
I am line 5, interesting data: Linux is awesome!
$ sed -n '5p' input.txt
I am line 5, interesting data: Linux is awesome!
在第一个单行中,“ 5!d ”表示删除除第 5 行之外的所有行,而在第二个命令中,“ -n ‘5p’ ”表示仅打印第 5 行。
这两个单行代码按我们的预期工作。但是,与 Bash 脚本类似,它们将遍历整个输入文件。因此,如果输入文件很大,它们将花费不必要的长时间。
** sed提供了一个“ q ”命令,允许“退出”进一步的处理**。我们可以将 ’ q ’ 命令放在两个单行代码中:
$ sed '5!d;q' input.txt
I am line 5, interesting data: Linux is awesome!
$ sed -n '5{p;q}' input.txt
I am line 5, interesting data: Linux is awesome!
从输出中,我们看不出有任何区别。因此,让我们使用sedsed (一个sed调试实用程序)工具运行带和不带 ’ q ’ 的sed命令,看看 ’ q ’ 命令是如何工作的。
首先,让我们看一下没有 ’ q ’ 命令的版本:
$ sedsed -d '5!d' input.txt
PATT:I am line 1, I don't have any interesting data.$
HOLD:$
COMM:5 !d
PATT:I am line 2, I don't have any interesting data.$
...
I am line 5, interesting data: Linux is awesome!
PATT:I am line 6, I don't have any interesting data.$
HOLD:$
COMM:5 !d
PATT:I am line 7, I don't have any interesting data.$
HOLD:$
COMM:5 !d
然后,我们可以看到sed命令一直处理到最后一行(第 7 行)的文件。
接下来,我们将 使用 ’ q ’ 测试sed命令:
$ sedsed -d '5!d;q' input.txt
PATT:I am line 1, I don't have any interesting data.$
HOLD:$
COMM:5 !d
PATT:I am line 2, I don't have any interesting data.$
...
PATT:I am line 5, interesting data: Linux is awesome!$
HOLD:$
COMM:q
I am line 5, interesting data: Linux is awesome!
如调试输出所示, sed处理在第 5 行停止。
5. 使用 awk命令
awk命令是另一个强大的 文本处理工具。它还可以用一个紧凑的单行来解决问题:awk ‘NR==5’ input.txt。
然而,正如我们之前所讨论的,我们希望在打印第 5 行之后停止进一步的处理。
同样,awk具有退出当前处理的“ exit ”命令:
$ awk 'NR==5{ print; exit }' input.txt
I am line 5, interesting data: Linux is awesome!
因此,正如上面的输出所示,我们已经解决了这个问题。
6. 使用head和tail命令
使用head和tail命令,我们可以轻松获取文件的开头和结尾部分。
如果我们结合这两个命令,我们还可以读取特定的行。 假设我们要读取第 X 行。想法是:
- 首先,我们使用head命令获取第 1 到 X 行:head -n X input
- 然后,我们将第一步的结果通过管道传递给tail命令以获取最后一行:head -n X input | 尾-1
让我们测试一下这个想法是否适用于我们的示例:
$ head -n 5 input.txt | tail -1
I am line 5, interesting data: Linux is awesome!
伟大的!我们得到了预期的输出并解决了问题。