在Linux中显示文件的第N个字符
1. 概述
在本教程中,我们将了解如何显示文件中的第一个字符。我们将使用大多数 Linux 发行版上已经提供的工具。此外,我们将为工具的默认行为提供一些解决方法。
最后,我们将更深入地讨论为这项工作选择哪个命令。
2.使用head命令
head 命令用于显示文件的第一行。默认情况下,head命令将只打印前 10 行。head命令附带coreutils 包,它可能已经安装在我们的机器上。
让我们打印一个 JSON 文件的前 10 行:
$ head package.json
{
"name": "in-your-pocket",
"version": "0.1.0",
"description": "",
"scripts": {
"watch": "webpack --config webpack/webpack.dev.js --watch",
"build": "webpack --config webpack/webpack.prod.js",
"clean": "rimraf dist",
"build:clean": "npm run clean && npm run build",
"test": "npx jest",
head命令还能够打印文件的前“n”个字节。在 ASCII 字符集中,每个字符占用一个字节。因此,我们可以通过提供–bytes或-c*选项来打印文件的前“n”个字符*:
$ head --bytes 100 package.json
{
"name": "in-your-pocket",
"version": "0.1.0",
"description": "",
"scripts": {
"watch":
**请注意,换行符、制表符和空格也算作字节。*或者,如果–bytes*选项的值为负数,head命令将打印除最后“n”个字符之外的所有字符。
假设我们有一个包含小写字母的文件,并且我们想要排除最后 10 个字符。然后我们可以提供*-10作为-c或–bytes*选项的参数:
$ head -c -10 alphabets
abcdefghijklmnopq
3. sed实用程序
sed 命令代表流编辑器。这是一个我们可以用来修改文本流的工具。不仅如此,它还能够执行其他操作,例如使用它打印文件的前“n”行或字符。
sed的一般语法非常简单:
$ sed [OPTIONS] [EXPR] <FILE>
我们可以给sed一个表达式来指示工具如何修改文本流。要打印前“n”个字符,我们将为sed提供一个表达式和我们的字母文件:
$ sed -z 's/^\(.\{12\}\).*/\1/' alphabets
abcdefghijkl
- -z选项将用空字符分隔行,从而防止sed单独对每一行进行操作。
- 脚本表达式修改文件的内容并将其显示在标准输出中。在我们的例子中,我们将文件的全部内容替换为前 12 个字符。
- 最后一个参数是我们的包含小写字符的字母表。
此外,我们可以将值 12 更改为我们需要打印的字符数。
通常,上面的表达式很难一遍又一遍地写。因此,我们可以编写另一个相对简单的表达式:
$ sed -z 's/.//6g' <<< $(cat alphabets)
abcde
在上面的命令中,我们将cat 命令的输出提供给sed命令。sed命令依次用点指定的所有字符替换文件中的前 5 个字符。我们应该注意,我们需要将要打印的字符数加 1。
4. 使用cut命令
cut 命令用于删除文本文件或文本行的部分内容。不仅如此,我们还可以使用cut命令从文件或字符串中提取部分文本。例如,如果我们想提取文件的第n个字符,我们可以使用cut:
$ cut -c 5 alphabets
e
** -c选项用于指定字符。但是,我们也可以指定要打印的字符或字节范围**:
$ cut -c 1-5 alphabets
abcde
正如我们所看到的,它工作得很好。当我们要打印包含换行符的文件的“n”个字符时,就会出现问题。与sed一样, cut中也有-z选项 ,我们可以使用它来将换行符视为NUL*字符。*
在下面的代码片段中,我们将使用一个字母数字文件来说明这一点,其中每组字母数字由换行符分隔:
$ cut -c 1-5 alphanumeric
abcde
ABCDE
12345
通过提供*-z或–zero-terminated*选项,我们可以覆盖这个默认行为:
$ cut -z -c 1-5 alphanumeric
abcde
5. 使用dd实用程序
dd 命令主要用于将字节或块从源复制到目标。 它是一个强大的实用程序,提供了多种选择。选项之一是bs选项。bs选项将一次读取的字节数作为参数。让我们看看它的实际效果:
$ dd bs=1 count=5 if=alphanumeric
abcde5+0 records in
5+0 records out
5 bytes copied, 0.000109751 s, 45.6 kB/s
- bs选项用于指定一次读取的字节数
- count选项指定要读取的总字节数
- if选项指定要读取的输入文件
在输出中,我们可以看到前 5 个字符,以及一些我们不需要的附加信息。幸运的是,dd命令有一个status选项,我们可以使用它来抑制 I/O 信息:
$ dd bs=1 count=5 if=alphanumeric status=none
abcde
或者,我们也可以将 I/O 信息重定向到*/dev/null*:
$ dd bs=1 count=5 if=alphanumeric 2> /dev/null
abcde
通常,我们可能需要指定要打印的字节范围。出于这个原因,我们可以使用skip选项来跳过前“n”个字节:
$ dd skip=5 bs=1 count=5 if=alphanumeric 2> /dev/null
fghij
6. 使用awk命令
awk 命令用于在文本文件中搜索模式并对其进行操作。它定义了自己的编程语言,相当容易使用。awk实用程序安装在大多数 Linux 发行版上。但是,如果我们还没有它,我们可以使用yum或apt 从官方存储库安装它。
安装后,我们可以通过以下方式进行验证:
$ awk --version
GNU Awk 5.1.0, API: 3.0 (GNU MPFR 4.1.0-p13, GNU MP 6.2.1)
现在,让我们打印字母文件中的前 5 个字符:
$ awk '{ print substr($0, 0, 5) }' alphabets
abcde
awk命令后跟我们要扫描和打印的模式。在我们的例子中,我们想要打印文本文件的子字符串。substr函数采用三个参数作为列、起始位置和包含的结束位置。**最后,我们将我们的字母文件指定给awk进行扫描。
从上面的代码片段可以看出,它适用于只有一行的文件,但对于多行的文件,输出是不同的。让我们看看它的外观:
$ awk '{ print substr($0, 0, 5) }' alphanumeric
abcde
ABCDE
01234
当然,我们没有预料到这一点。按照设计,awk单独处理文件中的每一行,而不是将整个文件视为字符串。出于这个原因,我们将使用echo和cat命令的简单解决方法:
$ echo $(cat alphanumeric)
abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUXWYZ 0123456789
**在上面的示例中,echo命令将cat命令的输出写入字符串,在此过程中将换行符转换为空格。**然后,我们可以将此输出通过管道传输到awk命令以打印前 5 个字符:
$ echo $(cat alphanumeric) | awk '{ print substr($0, 0, 5) }'
abcde
7. 使用哪一个?
在大多数情况下,我们应该可以使用head和cut命令,因为它们无处不在且使用迅速。但是,当我们的文件包含复杂的 Unicode 字符(如表情符号)时,就会出现问题。假设我们有一个文件,其中包含一个火焰表情符号,后跟一些文本:
🔥 This is a fire emoji.
现在,如果我们想使用 head 命令打印“🔥 This”,我们只需将文本从第一个字符切到第六个字符:
$ cut -c 1-6 text_with_emoji
🔥 T
好吧,这不是我们预期的行为。**我们知道,每个字符占用一个字节。这不适用于复杂的 Unicode 字符。例如,表情符号占用 4-6 个字节,具体取决于使用的表情符号。**在我们的例子中,火焰 emoji 需要 4 个字节。因此,如果我们使用 1-9 作为参数,它将打印我们想要的部分:
$ cut -c 1-9 text_with_emoji
🔥 This
现在,它按我们的预期工作。但是,**这不是解决问题的有效方法,因为我们可能希望在脚本文件中处理动态文本,而我们不确定使用的字符。**出于这个原因,我们可能想要使用更强大的解决方案:
$ string=$(cat < "text_with_emoji") && printf '%s\n' "${string:0:6}"
🔥 This
让我们分解一下:
- 我们创建了一个变量字符串,并使用**cat为它分配了我们文件的内容
- 我们使用内置的printf 命令来指定字符串的格式
- 在printf命令的第二个参数中,我们将字符串变量从 0 切片到 6
- printf命令会将每个字符视为真实字符,而不是将其视为单个字节
通过执行上面的命令,我们有一个更强大的解决方案,我们可以在我们的脚本中实现。更好的是,我们可以使用上述命令创建一个bash 脚本,并使用它来代替其他命令。