Top

D18:常见任务和基本工具之八

格式化输出

目录:

简单格式化工具

nl —— 号码行

用于给行编号,类似于cat -n
[me@linuxbox ~]$ nl distros.txt | head
  1  SUSE    10.2   12/07/2006
  2  Fedora  10     11/25/2008
  3  SUSE    11.0   06/19/2008
  4  Ubuntu  8.04   04/24/2008
  5  Fedora  8      11/08/2007
  6  SUSE    10.3   10/04/2007
  7  Ubuntu  6.10   10/26/2006
  8  Fedora  7      05/31/2007
  9  Ubuntu  7.10   10/18/2007
 10  Ubuntu  7.04   04/19/2007	
cat一样,nl可以接受多个文件作为命令行参数或标准输入。但nl有更多选项,并支持原始的标记形式,以允许更复杂的编号类型。
nl支持“逻辑页面”的概念,可以在编号时重置数字序列。
使用选项,可以将起始编号设置为特定值,并在有限的范围内设置其格式。
逻辑页面进一步细分为页眉、正文、页脚。在每一部分行号可能会被重置和/或指定不同的样式。
如果nl有多个文件,它会将它们视为单个文本流。文本流中的部分由添加到文本中的一些看起来很奇怪的标记表示。
标记 含义
\:\:\: 逻辑页眉的开头
\:\: 逻辑页正文的开头
\: 逻辑页脚的开头
上表列出的每个标记元素必须单独出现在自己的行中。在处理标记元素后,nl将其从文本流中删除。
nl常用选项如下表:
选项 含义
-b style 将主体编号设置为stylestyle可以时以下选项之一:
  • a = 行号
  • t = 仅为非空行号,此为默认值。
  • n = 空
  • pregexp = 仅限与基本正则表达式regexp匹配的行数
  • -f style 将页脚编号设置为style,同上。
    -h style 将页眉编号设置为style,同上。
    -i number 将页码增量设置为number,默认为1。
    -n format 将主体编号设置为formatformat可以时以下选项之一:
  • ln = 左对齐,不带前导零。
  • rn = 右对齐,不带前导零。此为默认值。
  • rz = 右对齐,带前导零。
  • -p 不要在每个逻辑页面的开头重置页面编号。
    -s string 在每个行号的末尾添加string以创建分隔符。默认为单个制表符。
    -v number 将每个逻辑页的第一行号设置为number。默认值为1。
    -w width 将行号字段的宽度设置为width。默认值为6。
    以下是脚本distors-nl.sed的内容:
    # sed script to produce Linux distributions report
    1 i\
    \\:\\:\\:\
    \
    Linux Distributions Report\
    \
    Name Ver. Released\
    ---- ---- --------\
    \\:\\:
    s/\([0-9]\{2\}\)\/\([0-9]\{2\}\)\/\([0-9]\{4\}\)$/\3-\1-\2/
    $ a\
    \\:\
    \
    End Of Report
    
    这个脚本加入了nl逻辑页标记和页脚。注意,标记中的反斜杠通常会被sed解释为转义字符。
    接下来,结合sortsednl生成增强的报告:
    [me@linuxbox ~]$ sort -k 1,1 -k 2n distros.txt | sed -f distros-nl.sed | nl
    
        Linux Distributions Report
        
        Name   Ver.  Released
        ----   ----  --------
        
      1 Fedora 5     2006-03-20
      2 Fedora 6     2006-10-24
      3 Fedora 7     2007-05-31
      4 Fedora 8     2007-11-08
      5 Fedora 9     2008-05-13
      6 Fedora 10    2008-11-25
      7 SUSE   10.1  2006-05-11
      8 SUSE   10.2  2006-12-07
      9 SUSE   10.3  2007-10-04
     10 SUSE   11.0  2008-06-19
     11 Ubuntu 6.06  2006-06-01
     12 Ubuntu 6.10  2006-10-26
     13 Ubuntu 7.04  2007-04-19
     14 Ubuntu 7.10  2007-10-18
     15 Ubuntu 8.04  2008-04-24
     16 Ubuntu 8.10  2008-10-30
     
    
       End Of Report
    
    以上示例,首先按照发行版名称和版本(字段1、2)排序,然后用sed处理结果,添加报告标题和页脚。最后使用nl处理结果,默认情况下,对文本流中属于主体的部分进行编号。
    对命令行最后的nl可以添加不同的选项以获得不同的观感:
    nl -n rz
    或者:
    nl -w 3 -s ' '

    fold —— 将每行包装到指定长度

    folding是指在指定的宽度处打断文本行的过程。
    与其他命令一样,fold接受一个或多个文本文件或标准输入。
    下面示例发送一个简单的文本流给fold
    [me@linuxbox ~]$ echo "The quick brown fox jumped over the lazy dog." | fold -w 12
    The quick br
    own fox jump
    ed over the
    lazy dog.	
    
    -w用于指定每行的字符数作为宽度,默认宽度为80个字符。如果不加其他选项,则会折断单词。
    使用-s则可避免折断单词(但如果单词的长度超过-w指定的值,还是会被折断):
    [me@linuxbox ~]$ echo "The quick brown fox jumped over the lazy dog." | fold -w 12 -s
    The quick
    brown fox
    jumped over
    the lazy
    dog.	
    
    [me@linuxbox ~]$ echo "The quick brown fox jumped over the lazy dog." | fold -w 4 -s
    The
    quic
    k
    brow
    n
    fox
    jump
    ed
    over
    
    the
    lazy
    
    dog.
    

    fmt —— 一个简单的文本格式化程序

    fmt程序可以折叠文本,还有其他功能。
    它接受文件或标准输入,并在文本流上执行段落格式设置。
    基本上它在保留空行和缩进的同时填充和连接文本中的行。

    此命令的使用方法:
    fmt -w 50 fmt-info.txt
    -w 50指定显示的宽度,默认为75。fmt在分行的时候不会切单词。
    以下是一些常用的选项:
    选项 说明
    -c 在顶部边缘(crown margin)模式下运行。这样可以保留段落前两行的缩进。后续行与第二行的缩进对齐。
    -p string 仅格式化以前缀string开头的行。格式化后,string的内容将作为前缀添加到么个重新格式化的行中。
    此选项可用于格式化源代码注释中的文本。例如,任何使用“#”字符来描述注释的编程语言或配置文件都可以通过指定-p '# '来格式化,以便只格式化注释。
    -s 仅拆分模式。在此模式下,行只会被拆分以适合指定的列宽。短线不会连接到填充线。
    当格式化文本(如不需要连接的代码)时,此模式很有用。
    -u 执行均匀间隔。类似传统的“打字机风格”格式。
    这意味着单词之间只有一个空格,句子之间有两个空格。
    这种模式对于删除“对齐”非常有用,文本中填充了空格,以强制左右边距对齐。
    -w width 设置文本格式,使其适合width。默认值为75。
    实际上会比指定宽度稍短,以实现行平衡。
    下面是-p选项的示例:
    [me@linuxbox ~]$ cat > fmt-code.txt
    # This file contains code with comments.
    
    # This line is a comment.
    # Followed by another comment line.
    # And another.
    
    This, on the other hand, is a line of code.
    And another line of code.
    And another.	
    
    [me@linuxbox ~]$ fmt -w 50 -p '# ' fmt-code.txt
    # This file contains code with comments.
    
    # This line is a comment. Followed by another
    # comment line. And another.
    
    This, on the other hand, is a line of code.
    And another line of code.
    And another.
    
    注意相邻的注释行是连接的,而空白行和不以指定前缀开头的行被保留。

    pr —— 为打印设置文本格式

    pr程序用于对文本进行分页。打印文本时,通常要用几行空格分隔输出页,以便在每页提供上边距和下边距。空白也可用于插入页眉页脚。
    以下例子将distros.txt文件格式化为一系列短页面:
    [me@linuxbox ~]$ pr -l 15 -w 65 distros.txt
    
    2016-12-11 18:27 distros.txt Page 1
    
    SUSE   10.2 12/07/2006
    Fedora 10   11/25/2008
    SUSE   11.0 06/19/2008
    Ubuntu 8.04 04/24/2008
    Fedora 8    11/08/2007
    
    
    2016-12-11 18:27 distros.txt Page 2
    
    SUSE   10.3 10/04/2007
    Ubuntu 6.10 10/26/2006
    Fedora 7    05/31/2007
    Ubuntu 7.10 10/18/2007
    Ubuntu 7.04 04/19/2007	
    ......
    
    上面例子中-l指定页面长度为15,-w指定页面宽度65。更多选项会在后面“打印”章节中再介绍。

    printf —— 格式化和打印数据

    printf命令不用于管道(它不接受标准输入),也不常直接在命令行上使用,主要用于脚本。
    printf是print formatted的缩写。最初是为C语言开发的,已经在包括shell在内的许多编程语言中实现。其使用格式为:
    printf "format" arguments
    示例:
    [me@linuxbox ~]$ printf "I formatted the string: %s\n" foo
    I formatted the string: foo	
    
    格式字符串可能包含文字文本(例如上面例子中的“I formatted the string:”)、转义序列(如上例中的换行符)和以%开头的序列。这些被称为转换规范。
    上面例子中%s用于格式化字符串foo,并将其放入命令的输出中。
    示例:
    [me@linuxbox ~]$ printf "I formatted '%s' as a string.\n" foo
    I formatted 'foo' as a string.	
    
    以上例子中,“foo”取代了%s的位置。
    下表列出常用的数据类型:
    说明符 含义
    d 将数字格式化为有符号的十进制整数。
    f 格式化并输出一个浮点数。
    o 将整数格式化为八进制数。
    s 格式化字符串。
    x 在需要时,使用小写a到f将整数格式为十六进制数。
    X 与x相同,但使用大写字母。
    % 打印文字%符号,即指定%%
    示例:
    [me@linuxbox ~]$ printf "%d, %f, %o, %s, %x, %X\n" 380 380 380 380 380 380
    380, 380.000000, 574, 380, 17c, 17C	
    
    由于指定了六个转换说明符,就必须为printf提供六个参数。

    可以向转换说明符添加几个可选组件以调整其输出。完整的转换规范可能包括以下内容:
    %[flags][width][.precision]conversion_specification
    使用多个可选组件时,必须按照上面的顺序显示,才能正确解释。下表描述了每种情况:
    组件 描述
    flags 共有五种不同的徽标:
  • #
    使用“替代格式”进行输出。因数据类型而异。
    对于o(八进制数)转换,输出的前缀为0;
    对于x和X(十六进制数)转换,输出分别以0x和0X作为前缀。
  • 0
    用零填充输出。这意味着该字段将填充前导零,例如000380。
  • -
    左对齐输出。默认情况下printf右对齐输出。
  • ' '(空格)
    为正数生成前导空格。
  • +
    签署正数。默认情况下printf只对负数进行签署。
  • width 指定最小字段宽度的数字。
    .precision 对于浮点数,指定小数点后要输出的精度位数。
    对于字符串转换,precision指定要输出的字符数。
    下表举例说明:
    源数值 格式 结果 说明
    380 "%d" 380 整数的简单格式
    380 "%#x" 0x17c 使用“替代格式”标志将整数格式化为十六进制数
    380 "%05d" 00380 带前导零(填充)和最小字段宽度为五个字符的整数格式。
    380 "%05.5f" 380.00000 格式化为带填充和小数点后5位精度的浮点数的数字。
    由于指定的最小字段宽度(5)小于格式化数字的实际宽度,因此填充无效。
    380 "%010.5f" 0380.00000 通过将最小字段宽度增加到10,填充现在可见。
    380 "%+d" +380 在正数前面加正号。
    380 "%-d" 380 在负数前面加负号。
    abcdefghijk "%5s" abcedfghijk 以最小字段宽度格式化的字符串。
    abcdefghijk "%.5s" abcde 通过对字符串应用精度,它将被截断。
    printf主要用于脚本中,用于格式化表格数据,而不是直接在命令行中使用。
    下面例子输出一些由制表符(\t)分隔的字段:
    [me@linuxbox ~]$ printf "%s\t%s\t%s\n" str1 str2 str3
    str1   str2 str3	
    
    下面例子格式化简洁的数字:
    [me@linuxbox ~]$ printf "Line: %05d %15.3f Result: %+15d\n" 1071 3.14156295 32589
    Line: 01071    3.142 Result:    +32589	
    

    文件格式化系统

    groff —— 文档格式化系统

    groff是一套包含troff的GNU实现的程序,它还包括一个脚本,用于模拟nroffroff家族的其他成员。

    示例:
    手册页以gzip压缩文本文件的形式保存在/usr/share/man目录中。文件的内容大致如下:
    [me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | head
    .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3.
    .TH LS "1" "January 2018" "GNU coreutils 8.28" "User Commands"
    .SH NAME
    ls \- list directory contents
    .SH SYNOPSIS
    .B ls
    [\fI\,OPTION\/\fR]... [\fI\,FILE\/\fR]...
    .SH DESCRIPTION
    .\" Add any additional description here
    .PP	
    
    与正常展示的手册页相比,可以看到标记语言与其结果之间的相关性:
    [me@linuxbox ~]$ man ls | head
    LS(1)    User Commands     LS(1)
    
    NAME
         ls - list directory contents
         
    SYNOPSIS
    ls [OPTION]... [FILE]...
    
    手册页是由groff使用mandoc宏包呈现的。可以通过使用以下管道模拟man命令:
    [me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | groff -mandoc -T ascii | head
    LS(1)    User Commands    LS(1)
    
    NAME
          ls - list directory contents
    SYNOPSIS
          ls [OPTION]... [FILE]...	
    
    此处我们使用groff程序以及选项设置特定的mandoc宏包输出ASCII。
    groff可以处理几种格式的输出。如果不指定格式,则默认使用PostScript。如下所示:
    [me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | groff -mandoc | head
    %!PS-Adobe-3.0
    %%Creator: groff version 1.18.1
    %%CreationDate: Thu Feb 5 13:44:37 2009
    %%DocumentNeededResources: font Times-Roman
    %%+ font Times-Bold
    %%+ font Times-Italic
    %%DocumentSuppliedResources: procset grops 1.18 1
    %%Pages: 4
    %%PageOrder: Ascend
    %%Orientation: Portrait
    
    PostScript是一种页面描述语言,用于向类似于排字机的设备描述打印页面的内容。

    后面一堆不想看了,似乎没啥用 P355