本文是学习Linux命令 (Learn linux command)系列文章的第二篇,在这里会介绍一些让大家平时都会经常用到的命令。注意,命令出现的顺序与重要程度无关。
echo - display a line of text
echo
命令是一个shell内置命令,但是你往往可以在系统上找到独立的echo
程序,例如我的系统上echo
位于/usr/bin下:
[kodango@devops ~]$ which echo /usr/bin/echo
但是这并不妨碍我们把它当成一个内置命令来对待。
echo
命令可以用于简单的文本打印:
[kodango@devops ~]$ echo "hello world" hello world
但是有一点需要小心得是,在打印的字符串之外最好用引号引起来,避免被shell展开,例如常见的星号会被用于文件名展开:
[kodango@devops ~]$ echo * helloworld share workspace [kodango@devops ~]$ echo "*" *
默认情况下,echo
在输出时会加上回车换行符,但是有时候我们可能不需要它,这时可以通过-n选项来避免\n的打印:
[kodango@devops ~]$ echo -n "hello world" hello world[kodango@devops ~]
有时候,你可能会习惯怀地在打印的字符串中添加\n等转义字符,但是会发现它们并没有在结果中被转义打印出来:
[kodango@devops ~]$ echo -n "hello world\n" hello world\n
唯一的解释是,在默认情况下,echo
命令并不会解释转义字符。实际上,你需要通过-e选项来主动打开这个功能:
[kodango@devops ~]$ echo -ne "hello world\n" hello world [kodango@devops ~]
通过转义字符,在echo
显示的时候还能加上颜色,例如:
[kodango@devops ~]$ echo -e "\e[0;32mhello, world\e[0m"
hello, world
echo
支持为数不多的转义字符,具体可以查看help的帮助,记住不要用echo --help
,而是help echo
或者man echo
。
cat - concatenate files and print on the standard output
cat
命令的作用是将文件或者标准输入的内容,拼在一起打印到标准输出。利用cat
命令和here document,我们还可以将多行内容写入到文件中:
[kodango@devops shell_temp]$ cat << EOF >> output.txt > line 1 > line 2 > EOF
这里的EOF是here document的起始和结束的标记,只要两者相同即可。
通过-n选项可以在输出文件内容时显示行号:
[kodango@devops shell_temp]$ cat output.txt -n 1 line 1 2 line 2
或者你也可以利用nl
命令来显示行号:
[kodango@devops shell_temp]$ cat output.txt | nl 1 line 1 2 line 2
默认情况下, cat
命令不会显示控制字符或者Tab字符等,导致终端上会有些肉眼下看不见的东西,有时候会出现莫名其妙的问题。
这个时候可以使用cat的几个选项来分别显示这些字符:-E(显示行末标记,$),-v(显示控制字符,但是不包括LFD和TAB),-T(显示Tab为^I)。同时cat命令还提供了-A选项用于显示所有不可见的字符,这个选项相当于-vET的全体:
[kodango@devops ~]$ ls --color | cat -A ^[[0m^[[01;34mhelloworld^[[0m$ ^[[01;34mshare^[[0m$ ^[[01;34mworkspace^[[0m$
printf - format and print data
printf
命令和echo
命令类似,它们都是用于打印字符串。但是相比之下,printf
命令还可以用于格式化输出的内容,同其它大多数编程语言中的printf/print
功能类似,比较符号码农的思维。
熟悉标准printf
用法的同学,不会对它陌生。它的用法也是很简单:printf [-v var] format [arguments]
。例如:
[kodango@devops ~]$ printf "%s, %s\n" hello world hello, world
上面第一个参数是格式化字符串,后面的参数是占位符(如%s)对应的实际参数。
printf
唯一的命令行选项-v,它后面指定一个变量名,作用是将printf
的输出就保存到变量中。例如,在简洁的Bash编程技巧一文中介绍过的,利用printf命令将数字转换成其十六进制形式:
[kodango@devops ~]$ printf -v var '%x' 111 [kodango@devops ~]$ echo $var 6f
在logdotsh中也大量用到了printf命令来格式化日志输出,有兴趣的同学可以看看。
sort - sort lines of text files
在校期间,接触最多的算法应该是排序算法了,例如冒泡排序、插入排序、堆排序等等。在Linux系统下,你就不必为排序发愁了,因为它默认就提供了sort
命令用于排序,而且大多数情况下,它的性能应该比你自己实现的要好。
我们从最简单的sort
命令开始,我们有一个例子文件,它的内容如下所示:
[kodango@devops ~]$ cat /tmp/line userage user_name user_id USER_ID
现在用sort
命令来排序:
[kodango@devops ~]$ sort /tmp/line userage user_id USER_ID user_name
上面的排序有问题吗?细心的同学会发现,字母a的ascii值为97,而字符'_'的ascii值为95,按照字节顺序排序的话,userage理应排在user_id的后面。还有USER_ID和user_id的顺序也有问题,在ascii表中大写字母是排在小写字母之前的,所以USER_ID排序的结果应该在user_id之前。
为什么会出现这样的问题呢?原因是排序的过程会受到系统locale的影响,不同的locale排序的结果是不一样的,这点在sort
命令的帮助最后也是有提及的:
*** WARNING *** The locale specified by the environment affects sort order. Set LC_ALL=C to get the traditional sort order that uses native byte values.
好我们按照帮助的提示,使用LC_ALL=C
来明确表示按照严格的字符顺序排序,结果就正常了:
[kodango@devops ~]$ LC_ALL=C sort /tmp/line USER_ID user_id user_name userage
在locale中,影响排序的环境变量是LC_COLLATE
,但是如果系统设置了LC_ALL
的话,会覆盖单独的LC_*环境变量设置,所以最好还是使用LC_ALL
来限制。
默认情况下,sort
排序过程中大小写是区分出来的,这一点从上面的例子中也可以看出。我们可以利用--ignore-case(-f)选项来不区分大小写,比较下面两个命令之间的区别:
[kodango@devops ~]$ echo -e "d\nD\nc\nb\nB\na" | LC_COLLATE=C sort B D a b c d [kodango@devops ~]$ echo -e "d\nD\nc\nb\nB\na" | LC_COLLATE=C sort --ignore-case a B b c D d
从第二个结果中,可以看出默认情况下sort
命令的排序是不稳定的,何谓稳定?稳定的定义是指,相同的记录在排序前后彼此的顺序保持一致。在上例的第二个结果中,d和D的顺序是不一致的。
如果要获得稳定的排序,可以加上--stable选项:
[kodango@devops ~]$ echo -e "d\nD\nc\nb\nB\na" | LC_COLLATE=C sort --ignore-case --stable a b B c d D
sort
命令默认的排序规则是按照字典顺序(选项-d)从小到大排序。如果针对数字排序,可以指定选项-n;如果针对浮点数排序,可以指定选项-g;如果要从大到小排序,可以指定选项-r。例如:
[kodango@devops ~]$ echo -e "1.2\n1.1" | sort -gr 1.2 1.1
sort
命令还可以进一步地按照字段排序,字段之间的分隔符可以通过选项-t指定,默认是空格。排序的字段选择可以通过-k, --key=start[,stop]选项控制,表示排序的key是从字段start开始一直到字段stop结束之间的内容,如果不指定stop,则默认到记录的结尾。start和stop的值形式为F[.C][OPTS],其中F表示字段的序号,C表示字段中字符的位置,合起来就是第F个字段的第C个字符,两者的值都从1开始计算。OPTS表示排序的开关,即上面介绍的-n/-d/-g/-r等等,它们会覆盖全局的选项设置。
例如对/etc/passwd按照用户名排序,用户名是第一个字段,字段分隔符为冒号(:),命令如下所示:
[kodango@devops ~]$ sort -t: -k1,1 /etc/passwd | head -2 avahi:x:84:84:avahi:/:/bin/false bin:x:1:1:bin:/bin:/bin/false
按照用户id的大小逆序排序:
[kodango@devops ~]$ sort -t: -k3nr,3 /etc/passwd | head -2 kodango:x:1000:1000::/home/kodango:/bin/bash git:x:999:998:git daemon user:/:/bin/bash
sort
命令可以利用-u选项去除重复行:
[kodango@devops ~]$ echo -ne '1\n1\n2\n' | sort -u 1 2
最后说一下sort
命令使用上一个比较容易出错的地方,有时候我们会希望将文件排序的结果写到相同的文件中,自然而然就想到利用重定向功能:
[kodango@devops ~]$ sort line > line [kodango@devops ~]$ cat line [kodango@devops ~]$
但是结果却发现文件变成空了,这是因为在排序之前,首先会打开重定向的文件,输出重定向会打开文件并清空原有的内容,所以导致line文件变成空了。
幸运地是,sort
命令提供了-o选项用于in-place排序,这样就可以让排序文件与输出文件是同一个了:
[kodango@devops ~]$ sort -o line line [kodango@devops ~]$ cat line 1 2 3 4 5
paste - merge lines of files
paste
命令用于合并多个文件的行,即将每个文件的同一行按照特定的分隔符合并成一行。例如:
[kodango@devops ~]$ cat name name kodango [kodango@devops ~]$ cat age age 99 [kodango@devops ~]$ paste name age name age kodango 99
默认使用Tab分隔,可以通过-d选项指定其它分隔符:
[kodango@devops ~]$ paste -d'|' name age name|age kodango|99
paste
命令还可以合并单个文件的行,这个用法很新奇:
[kodango@devops ~]$ paste -sd, name name,kodango
是不是给你一种新的拼接文件行的思路呢。
cut - print selected parts of lines
cut
命令用于去除一行中的某些字段,仅打印剩余的字段,或者说剩余的几列。字段之间的分隔符可以由-d选项指定,例如,打印/etc/passwd文件的第一列:
[kodango@devops ~]$ cut -d: -f1 /etc/passwd | head -2 root bin
字段的选择可以通过-f选项控制,它的取值可以为:
- N: 指定第N个字段;
- N-: 指定从第N个字段开始到最后一个字段;
- N-M: 指定从第N个字段开始到第M个字段结束;
- -M: 指定从第1个字段开始到第M个字段结束;
字段的序号从1开始计算,例如:
[kodango@devops ~]$ cut -d: -f1-3 /etc/passwd | head -1 root:x:0 [kodango@devops ~]$ cut -d: -f-3 /etc/passwd | head -1 root:x:0 [kodango@devops ~]$ cut -d: -f4- /etc/passwd | head -1 0:root:/root:/bin/bash
除按字段控制之外,cut还支持以字符为单位来选择,这是通过-c选项来完成的:
[kodango@devops ~]$ cut -c1-2 /etc/passwd | head -1 ro
wc - print newline, word, and byte counts for each file
wc
命令用于统计文件的行数、单词个数、字符数以及字节数等数据。
a. 显示文件/etc/vimrc的行数:
[kodango@devops ~]$ wc -l /etc/vimrc 16 /etc/vimrc
输出结果为行数,后面跟着文件名。如果只想获得行数可以用cat /etc/vimrc | wc -l
。
这里有一点要注意的是,行数的统计是以回车\n的个数为准的,所以有时候统计的结果可能与你认为的不一样:
[kodango@devops ~]$ echo -ne "line1\nline2" | wc -l 1
b. 统计/etc/vimrc文件出现的单词个数
[kodango@devops ~]$ wc -w /etc/vimrc 131 /etc/vimrc
单词之间使用空格分隔。
c. 统计/etc/vimrc文件的字符数
[kodango@devops ~]$ wc -m /etc/vimrc 841 /etc/vimrc
或者
[kodango@devops ~]$ wc -c /etc/vimrc 841 /etc/vimrc
head - output the first part of files
head
命令用于显示文件的头n个字节(-c)或者头n行(-n)。
例如显示文件的前24个字节:
[kodango@devops ~]$ head -c 24 ~/.bashrc # # Kodango's ~/.bashrc
显示文件的前3行:
[kodango@devops ~]$ head -n 3 ~/.bashrc # # Kodango's ~/.bashrc #
tail - output the last part of files
tail
命令与head
命令是一对,它们的作用是相反的,用法也大致差不多。例如通过-n 3选项显示最后3行:
[kodango@devops ~]$ sudo tail -n 3 /var/log/php-fpm.log [03-Jan-2013 00:47:46] NOTICE: exiting, bye-bye! [05-Jan-2013 20:57:13] NOTICE: fpm is running, pid 257 [05-Jan-2013 20:57:13] NOTICE: ready to handle connections
但是tail
最实用的选项是-f,当一个文件的内容增长时,可以实时地显示文件结尾处的内容,在观察日志时非常有用:
[kodango@devops ~]$ tail -f xxx.log
comm - compare two sorted files line by line
comm
用于比较两个排序好的文件,返回的结果有三列,第一列是只有在文件A中有的行,第二列是只有在文件B中有的行,第三列则是两个文件共有的行:
$ comm test.txt test2.txt line 1 line 11 line 2
要得到最初要求的结果,则只需要取相应的列就可以了。comm命令非常人性化地考虑到这个需求:
$ comm test.txt test2.txt -1 -2 line 2 $ comm test.txt test2.txt -2 -3 line 1 $ comm test.txt test2.txt -1 -3 line 11
其中,-1, -2与-3这个参数分别表示不输出第1、2或者3列。
学习一下