Linux下面有很多经典的非常有用的命令,其中处理文本的命令就有很多。这些小工具经过了几十年时间的洗礼,现在已经变成了经典,已经变成了Linux下面的标准,其实它们一直是遵循着Linux的标准。下面就让我们一起看看这些经典的Linux文本处理命令。
Linux下面有很多经典的非常有用的命令,其中处理文本的命令就有很多。这些小工具经过了几十年时间的洗礼,现在已经变成了经典,已经变成了Linux下面的标准,其实它们一直是遵循着Linux的标准。下面就让我们一起看看这些经典的Linux文本处理命令。
一. sort
文件排序, 通常用在管道中当过滤器来使用. 这个命令可以依据指定的关键字或指定的字符位置, 对文件行进行排序. 使用-m选项, 它将会合并预排序的输入文件. 想了解这个命令的全部参数请参考这个命令的info页.
二. tsort
拓扑排序, 读取以空格分隔的有序对, 并且依靠输入模式进行排序.
三. uniq
这个过滤器将会删除一个已排序文件中的重复行. 这个命令经常出现在sort命令的管道后边.
四. expand, unexpand
expand命令将会把每个tab转化为一个空格. 这个命令经常用在管道中.
unexpand命令将会把每个空格转化为一个tab. 效果与expand命令相反.
五. cut
一个从文件中提取特定域的工具. 这个命令与awk中使用的print $N命令很相似, 但是更受限. 在脚本中使用cut命令会比使用awk命令来得容易一些. 最重要的选项就是-d(字段定界符)和-f(域分隔符)选项.
六. paste
将多个文件, 以每个文件一列的形式合并到一个文件中, 合并后文件中的每一列就是原来的一个文件. 与cut结合使用, 经常用于创建系统log文件.
七. join
这个命令与paste命令属于同类命令. 但是它能够完成某些特殊的目地. 这个强力工具能够以一种特殊的形式来合并两个文件, 这种特殊的形式本质上就是一个关联数据库的简单版本.
join命令只能够操作两个文件. 它可以将那些具有特定标记域(通常是一个数字标签)的行合并起来, 并且将结果输出到stdout. 被加入的文件应该事先根据标记域进行排序以便于能够正确的匹配.
八. head
把文件的头部内容打印到stdout上(默认为10行, 可以自己修改). 这个命令有一些比较有趣的选项.
九. tail
将一个文件结尾部分的内容输出到stdout中(默认为10行). 通常用来跟踪一个系统logfile的修改情况, 如果使用-f选项的话, 这个命令将会继续显示添加到文件中的行.
十. wc
wc可以统计文件或I/O流中的"单词数量":
十一. fold
将输入按照指定宽度进行折行. 这里有一个非常有用的选项-s, 这个选项可以使用空格进行断行(译者: 事实上只有外文才需要使用空格断行, 中文是不需要的)(请参考例子 12-23和例子 A-1).
十二. fmt
一个简单的文件格式器, 通常用在管道中, 将一个比较长的文本行输出进行"折行".
十三. col
这个命令用来滤除标准输入的反向换行符号. 这个工具还可以将空白用等价的tab来替换. col工具最主要的应用还是从特定的文本处理工具中过滤输出, 比如groff和tbl. (译者: 主要用来将man页转化为文本.)
十四. column
列格式化工具. 通过在合适的位置插入tab, 这个过滤工具会将列类型的文本转化为"易于打印"的表格式进行输出.
十五. colrm
列删除过滤器. 这个工具将会从文件中删除指定的列(列中的字符串)并且写到文件中, 如果指定的列不存在, 那么就回到stdout. colrm 2 4 <filename将会删除filename文件中每行的第2到第4列之间的所有字符.
Caution: 如果这个文件包含tab和不可打印字符, 那将会引起不可预期的行为. 在这种情况下, 应该通过管道的手段使用expand和unexpand来预处理colrm.
十六. nl
计算行号过滤器. nl filename将会把filename文件的所有内容都输出到stdout上, 但是会在每个非空行的前面加上连续的行号. 如果没有filename参数, 那么就操作stdin.
nl命令的输出与cat -n非常相似, 然而, 默认情况下nl不会列出空行.
十七. pr
格式化打印过滤器. 这个命令会将文件(或stdout)分页, 将它们分成合适的小块以便于硬拷贝打印或者在屏幕上浏览. 使用这个命令的不同的参数可以完成好多任务, 比如对行和列的操作, 加入行, 设置页边, 计算行号, 添加页眉, 合并文件等等. pr命令集合了许多命令的功能, 比如nl, paste, fold, column, 和expand.
pr -o 5 –width=65 fileZZZ | more 这个命令对fileZZZ进行了比较好的分页, 并且打印到屏幕上. 文件的缩进被设置为5, 总宽度设置为65.
一个非常有用的选项-d, 强制隔行打印(与sed -G效果相同).
十八. gettext
GNU gettext包是专门用来将程序的输出翻译或者本地化为不同国家语言的工具集. 在最开始的时候仅仅支持C语言, 现在已经支持了相当数量的其它程序语言和脚本语言.
想要查看gettext程序如何在shell脚本中使用. 请参考info页.
十九. msgfmt
一个产生二进制消息目录的程序. 这个命令主要用来本地化.
二十. iconv
一个可以将文件转化为不同编码格式(字符集)的工具. 这个命令主要用来本地化.
二十一. recode
可以认为这个命令是上边iconv命令的专业版本. 这个非常灵活的并可以把整个文件都转换为不同编码格式的工具并不是Linux标准安装的一部分.
二十二. TeX, gs
TeX和Postscript都是文本标记语言, 用来对打印和格式化的视频显示进行预拷贝.
TeX是Donald Knuth精心制作的排版系统. 通常情况下, 通过编写脚本的手段来把所有的选项和参数封装起来一起传到标记语言中是一件很方便的事情.
Ghostscript (gs) 是一个 遵循GPL的Postscript解释器.
二十三. enscript
将纯文本文件转换为PostScript的工具
比如, enscript filename.txt -p filename.ps 产生一个 PostScript 输出文件filename.ps.
二十四. groff, tbl, eqn
另一种文本标记和显示格式化语言是groff. 这是一个对传统UNIX roff/troff显示和排版包的GNU增强版本. Man页使用的就是groff.
tbl表处理工具可以认为是groff的一部分, 它的功能就是将表标记转化到groff命令中.
eqn等式处理工具也是groff的一部分, 它的功能是将等式标记转化到groff命令中.
二十五. lex, yacc
lex是用于模式匹配的词汇分析产生程序. 在Linux系统上这个命令已经被flex取代了.
yacc工具基于一系列的语法规范, 产生一个语法分析器. 在Linux系统上这个命令已经被bison取代了.
二十六. tr
字符转换过滤器.
例:8-1
1. #ls -l /var/www/html #显示指定目录的内容列表
2. #ls -lut /var/www/html #以最近访问的日期顺序显示指定目录的内容列表
3. #ls -lut /var/www/html | head -5 #以最近访问的日期顺序显示指定目录的内容列表的前5项
4. #ls -lt /var/spool/mail #按最后修改的时间顺序显示/var/spool/mail下的内容列表
5. #ls -lt /var/spool/mail | tail -5 #按最后修改的时间顺序显示/var/spool/mail下的内容列表后5项
6. #tail -f /var/log/httpd/access_log #监视Apache的访问日志
7. #grep -i unix chapt5.txt #不区分大小写
8. #grep -v "#" /etc/hosts #不显示包含#的行
9. #grep -n pipe chap02.txt chap06.txt #显示行号
10. #grep -l sharp /project/* #只显示包含sharp的文件名称
11. #grep -n pipe chap02.txt chap06.txt #显示包含pipe内容的行,同时显示行号
12. #echo "abcd2ef1" | tr ‘[:alpha:]’ –
13. #tr ‘!?":;\[\]{}(),.\t\n’ ‘ ‘ < chapt5.txt | tr ‘A-Z’ ‘a-z’ | tr -s ‘ ‘ | tr ‘ ‘ ‘\n’ | sort
14. #tr ‘!?":;\[\]{}(),.\t\n’ ‘ ‘ < chapt5.txt | tr ‘A-Z’ ‘a-z’ | tr -s ‘ ‘ | tr ‘ ‘ ‘\n’ | sort | uniq -c | sort -rn | head
15. #tr ‘[:punct:]’ ‘ ‘ < chapt5.txt | tr ‘A-Z’ ‘a-z’ | tr ‘[:space:] ‘ ‘ ‘ | tr -s ‘ ‘| tr ‘ ‘ ‘\n’ | sort | uniq -c | sort -rn | head
16. #sort -rn -k 2,2 chapt5.txt
17. #echo "abcd2ef1" | tr ‘[:alpha:]’ –
18. #echo "abcdef" | tr -d b-d # 结果为aef,tr的 -d 选项删除指定范围的字符.
19. #tr -d 0-9 <filename # 删除"filename"中所有的数字.
例子 8-2. 转换大写: 把一个文件的内容全部转换为大写.
#!/bin/bash
# 把一个文件的内容全部转换为大写.
E_BADARGS=65
if [ -z "$1" ];then # 检查命令行参数.
echo "Usage: `basename $0` filename"
exit $E_BADARGS
fi
tr a-z A-Z <"$1"
# 与上边的作用相同, 但是使用了POSIX字符集标记方法:
# tr ‘[:lower:]’ ‘[:upper:]’ <"$1"
exit 0
例子 8-3. 转换小写: 将当前目录下的所有文件名全部转换为小写.
#!/bin/bash
#
# 将当前目录下的所有文件名全部转换为小写.
#
for filename in * # 遍历当前目录下的所有文件.
do
fname=`basename $filename`
n=`echo $fname | tr A-Z a-z` # 将名字修改为小写.
if [ "$fname" != "$n" ] # 只对那些文件名不是小写的文件进行重命名.
then
mv $fname $n
fi
done
exit $?
# 下边的代码将不会被执行, 因为上边的"exit".
#——————————————-#
# 删除上边的内容, 来运行下边的内容.
# 对于那些文件名中包含空白和新行的文件, 上边的脚本就不能工作了.
# Stephane Chazelas因此建议使用下边的方法:
for filename in * # 不必非得使用basename命令, 因为"*"不会返回任何包含"/"的文件.
do
n=`echo "$filename/" | tr ‘[:upper:]’ ‘[:lower:]’`
# POSIX 字符集标记法. 添加的斜线是为了在文件名结尾换行不会被命令替换删掉.
# 变量替换:
n=${n%/} # 从文件名中将上边添加在结尾的斜线删除掉.
[[ $filename == $n ]] || mv "$filename" "$n"
# 检查文件名是否已经是小写.
done
exit $?
例子 10-9. 在目录的所有文件中查找源字串
#!/bin/bash
# findstring.sh:
# 在一个指定目录的所有文件中查找一个特定的字符串.
directory=/usr/bin/
fstring="Free Software Foundation" # 查看哪个文件中包含FSF.
for file in $( find $directory -type f -name ‘*’ | sort )
do
strings -f $file | grep "$fstring" | sed -e "s%$directory%%"
# 在"sed"表达式中, 我们必须替换掉正常的替换分隔符"/", 因为"/"碰巧是我们需要过滤的字符串之一.
# 如果不用"%"代替"/"作为分隔符,那么这个操作将失败,并给出一个错误消息.(试一试).
done
exit 0
例子 8-4. 列出目录中所有的符号链接
#!/bin/bash
# symlinks.sh: 列出目录中所有的符号链接文件.
directory=${1-`pwd`}
# 如果没有其他特殊的指定, 默认为当前工作目录.
# 下边的代码块, 和上边这句等价.
# ———————————————————-
# ARGS=1 # 需要一个命令行参数.
#
# if [ $# -ne "$ARGS" ] # 如果不是单个参数的话…
# then
# directory=`pwd` # 当前工作目录
# else
# directory=$1
# fi
# ———————————————————-
echo "symbolic links in directory \"$directory\""
for file in "$( find $directory -type l )" # -type l = 符号链接
do
echo "$file"
done | sort # 否则的话, 列出的文件都是未经排序的.
# 严格意义上说, 这里并不一定非要一个循环不可. 因为"find"命令的输出将被扩展成一个单词. 然而, 这种方式很容易理解也很容易说明.
# 就像Dominik ‘Aeneas’ Schnitzer所指出的, 如果没将$( find $directory -type l )用""引用起来的话, 那么将会把一个带有空白部分的文件名拆分成以空白分隔的两部分(文件名允许有空白).即使这里只会取出每个参数的第一个域.
exit 0
# Jean Helou建议采用下边的方法:
echo "symbolic links in directory \"$directory\""
# 当前IFS的备份. 要小心使用这个值.
OLDIFS=$IFS
IFS=:
for file in $(find $directory -type l -printf "%p$IFS")
do # ^^^^^^^^^^^^^^^^
echo "$file"
done|sort
例子 8-5. 将目录中所有符号链接文件的名字保存到一个文件中
#!/bin/bash
# symlinks.sh: 列出目录中所有的符号链接文件.
OUTFILE=symlinks.list # 保存符号链接文件名的文件
directory=${1-`pwd`}
# 如果没有其他特殊的指定,
#+ 默认为当前工作目录.
echo "symbolic links in directory \"$directory\"" > "$OUTFILE"
echo "—————————" >> "$OUTFILE"
for file in "$( find $directory -type l )" # -type l = 符号链接
do
echo "$file"
done | sort >> "$OUTFILE" # 循环的stdout
# ^^^^^^^^^^^^^ 重定向到一个文件中.
exit 0
练习:
- 重写例8-2脚本, 通过选项可以控制脚本或者转换为大写或者转换为小写.
- 转换例8-3这个脚本, 用命令行参数代替内部用的$directory和$fstring.