![Linux Shell核心编程指南](https://wfqqreader-1252317822.image.myqcloud.com/cover/237/27741237/b_27741237.jpg)
1.3 如何在脚本文件中实现数据的输入与输出
在Linux系统中使用echo命令和printf命令都可以实现信息的输出功能,下面我们分别看这两个命令的应用案例。
1)使用echo命令创建一个脚本文件菜单
功能描述:echo命令主要用来显示字符串信息。
echo命令的语法格式如下。
![](https://epubservercos.yuewen.com/B53532/15937388204513606/epubprivate/OEBPS/Images/figure_0028_0002.jpg?sign=1738892202-vgZ8SSBajQG8KyfjQivPQxjoYix6WbYQ-0-b422b273504e14dae69ba25333b4bf4e)
从上面的脚本文件中可以看到,echo命令可以实现任意字符串消息的输出,可以使用多个echo命令输出多条信息,也可以使用一个echo命令,利用引号将多条信息一起输出。但这些输出信息都使用默认的黑色字体,也无法居中显示,而当我们需要醒目地显示信息以提示用户注意时,这种输出可能略显单调。echo命令支持-e选项,使用该选项可以让echo命令识别\后面的转义符号含义,常见转义符号如表1-1所示。其中\033或\e后面可以跟终端编码,终端编码可以用于定义终端的字体颜色、背景颜色、定位光标等。
表1-1 常见转义符号
![](https://epubservercos.yuewen.com/B53532/15937388204513606/epubprivate/OEBPS/Images/figure_0029_0002.jpg?sign=1738892202-jzvUiUMtivqxvae3B4RjP931yu6wcSDL-0-416c66685862cfcf89ed1dd28dba93cb)
应用案例:
[root@centos7~]# echo "\t"
因为没有-e选项,不支持\字符,所以屏幕会将原始内容\t直接输出。
[root@centos7~]# echo -e "hello\tworld"
输出hello后,再输出Tab缩进,最后输出world,最终结果是hello world。
[root@centos7~]# echo -e "helle\bo world"
输出helle,然后将光标左移1位,接着输出o world,原有的字母e被新的字母o替代,所以最终输出结果是hello world。注意:-e选项和后面需要输出的内容之间至少有一个空格!
[root@centos7~]# echo -e "helle\b\bo world"
与上面的案例类似,左移2位,最终输出结果是helo world。
[root@centos7~]# echo -e "hello\fworld" hello world
输出hello,换行但光标仍旧停留在原来的位置,也就是字母o后面的这个位置,然后输出world。
[root@centos7~]# echo -e "hello\nworld" #输出hello,换行后,输出world hello world [root@centos7~]# echo -e "hello\rworld" # \r会让光标返回行首 world [root@centos7~]# echo -e "\\" #输出一个\符号 \ [root@centos7~]# echo -e "\033[1mOK\033[0m" OK
加粗显示OK, \033或\e后面跟不同的代码可以设置不同的终端属性,1m是让终端粗体显示字符串,后面的OK就是需要显示的字符串内容,最后\033[0m是在加粗输出OK后,关闭终端的属性设置。如果最后没有使用0m关闭属性设置,则之后终端中所有的字符串都使用粗体显示。执行下面这条命令后,会发现除了OK加粗显示,后面在终端中输出的所有字符串都加粗显示。
[root@centos7~]# echo -e "\033[1mOK" #加粗显示OK后没关闭属性设置 [root@centos7~]# echo -e "\e[1mOK\e[0m" #使用\e和\033的效果相同 [root@centos7~]# echo -e "\e[4mOK\e[0m" #加下画线后输出OK OK [root@centos7~]# echo -e "\e[5mOK\e[0m" #闪烁显示OK
[root@centos7~]# echo -e "\e[30mOK\e[0m" #黑色显示OK [root@centos7~]# echo -e "\e[31mOK\e[0m" #红色显示OK [root@centos7~]# echo -e "\e[32mOK\e[0m" #绿色显示OK [root@centos7~]# echo -e "\e[33mOK\e[0m" #棕色显示OK [root@centos7~]# echo -e "\e[34mOK\e[0m" #蓝色显示OK [root@centos7~]# echo -e "\e[35mOK\e[0m" #紫色显示OK [root@centos7~]# echo -e "\e[36mOK\e[0m" #蓝绿色显示OK [root@centos7~]# echo -e "\e[37mOK\e[0m" #亮灰色显示OK [root@centos7~]# echo -e "\e[1;33mOK\e[0m" #亮黄色显示OK [root@centos7~]# echo -e "\e[42mOK\e[0m" #绿色背景显示OK [root@centos7~]# echo -e "\e[44mOK\e[0m" #蓝色背景显示OK [root@centos7~]# echo -e "\e[32;44mOK\e[0m" #绿色字体,蓝色背景显示OK
试一试
还有其他颜色吗?如92m?大家可以自己试一试!
除了可以定义终端的字体颜色、样式、背景,还可以使用H定义位置属性。例如,可以通过下面的命令在屏幕的第3行、第10列显示OK。
[root@centos7~]# echo -e "\033[3;10HOK" [root@centos7~]# echo -e "\033[3HOK" #在第3行开头位置显示OK
最后,我们使用echo命令编写一个更有趣的脚本文件菜单!下面这个脚本文件,首先使用clear命令将整个屏幕清空,然后使用echo命令设置终端属性,打印了一个有颜色、有排版的个性化菜单。至于具体的颜色搭配,各位读者可以根据自己的需求进行个性化设计。
![](https://epubservercos.yuewen.com/B53532/15937388204513606/epubprivate/OEBPS/Images/figure_0031_0001.jpg?sign=1738892202-pFShmVRld8GabY3nMWiRYYg7s0o7z0JU-0-87a5fb60899c28f1d6631109f2766ed5)
2)扩展知识,使用printf命令创建一个脚本菜单
Linux系统中除了echo命令可以输出信息,还可以使用printf命令实现相同的效果。
功能描述:printf命令可以格式化输出数据。
printf命令的语法格式如下。
printf [格式] 参数
备注:一般printf命令的参数就是需要输出的内容。
常用的格式字符串及功能描述如表1-2所示。
表1-2 常用的格式字符串及功能描述
![](https://epubservercos.yuewen.com/B53532/15937388204513606/epubprivate/OEBPS/Images/figure_0032_0001.jpg?sign=1738892202-h1C0SU9B25Fb9XfwgGKSLTf6rJttDEgi-0-cb1cab6163e44589828148926b3c3875)
应用案例:
[root@centos7~]# printf "%d" 12 #屏幕显示整数12 12 [root@centos7~]# printf "%d" jacob #jacob不是整数,所以会报错 -bash: printf: jacob: 无效数字 [root@centos7~]# printf "%5d" 12 12
该命令的格式%5d设置了打印宽度为5,以右对齐的方式显示整数12。注意,该命令的输出信息12前面有3个空格。3个空格+2个数字一起是5个字符的宽度。如果需要左对齐,则可以使用%-5d实现效果,比如下面这条命令。
[root@centos7~]# printf "%-5d" 12 12 [root@centos7 ~]#
注意,printf命令输出信息后,默认是不换行的!如果需要换行则可以使用\n命令符。为了更好地观察左右对齐的效果,下面的案例中会打印两个确定位置的符号。
[root@centos7~]# printf "|%-10d|\n" 12 |12 |
左对齐输出12,输出的内容占用10个字符宽度,12占用2个字符宽度,后面跟了8个空格位置。默认printf命令输出内容后不会换行,使用\n命令符可以在输出内容后换行。
[root@centos7~]# printf "|%10d|\n" 12 #右对齐输出12,字符宽度为10 | 12| [root@centos7~]# printf "%o\n" 10 12
显示10的八进制值,八进制12转换为十进制正好是10。
[root@centos7~]# printf "%x\n" 10 #显示10的十六进制值 a [root@centos7~]# printf "%d\n" 0x11
0x11表示的是十六进制的11, printf命令将十六进制的11转换为十进制整数输出(17)。
[root@centos7~]# printf "%d\n" 011
011表示的是八进制的11, printf命令将八进制的11转换为十进制整数输出(9)。
[root@centos7~]# printf "%d\n" 9223372036854775808 -bash: printf: 警告:9223372036854775808: 数值结果超出范围 9223372036854775807
当使用\d打印比较大的整数时,系统提示超出范围,并提示可以打印的最大数是9223372036854775807。如果需要打印这样的大整数,则需要使用\u命令符,但\u命令符也有最大的显示值(18446744073709551615),当大于最大值时则无法打印。
[root@centos7~]# printf "%u\n" 9223372036854775808 9223372036854775808 [root@centos7~]# printf "%f\n" 3.88 #打印小数 3.880000 [root@centos7~]# printf "%.3f\n" 3.88 #小数点后保留3位 3.880 [root@centos7~]# printf "|%8.3f|\n" 3.88 #右对齐,占用8位宽度 | 3.880| [root@centos7~]# printf "|%-8.3f|\n" 3.88 #左对齐,占用8位宽度 |3.880 | [root@centos7~]# printf "%s\n" "hello" #打印字符串hello hello [root@centos7~]# printf "|%10s|\n" "hello" #右对齐,占用10位宽度 | hello| [root@centos7~]# printf "|%-10s|\n" "hello" #左对齐,占用10位宽度 |hello | [root@centos7~]#printf "%s\t\t%s\n" "hello" "world" #打印2个字符串和tab hello world
下面我们看看使用printf命令输出一个菜单的脚本案例。
![](https://epubservercos.yuewen.com/B53532/15937388204513606/epubprivate/OEBPS/Images/figure_0034_0001.jpg?sign=1738892202-Q3b9927yZUQv1ZU2s8WYd6RyXaNl1wAJ-0-30813e987952b5ff7b55305fb9d95e42)
3)使用read命令读取用户的输入信息
前面我们学习了在Shell脚本中实现输出数据的方法,接下来探讨如何解决输入的问题,在Shell脚本中允许使用read命令实现数据的输入功能。
功能描述:read命令可以从标准输入读取一行数据。
read命令的语法格式如下。
read [选项] [变量名]
如果未指定变量名,则默认变量名称为REPLY。
read命令常用的选项如表1-3所示。
表1-3 read命令常用的选项
![](https://epubservercos.yuewen.com/B53532/15937388204513606/epubprivate/OEBPS/Images/figure_0035_0001.jpg?sign=1738892202-DLU7HQuG8apcNQJziQufo2LhZBR5gzNr-0-274bea5f556b2c0a2f7d8a622350e3be)
[root@centos7~]# read key1 123
从标准输入中读取数据,这里通过键盘输入了123, read命令则从标准输入读取这个123,并将该字符串赋值给变量key1,对于key1这个变量,我们可以使用echo $key1显示该变量的值。
应用案例:
[root@centos7~]# echo $key1 [root@centos7~]# read key1 key2 key3 #从标准输入读取3组字符串 11 22 33 [root@centos7~]# echo $key1 11 [root@centos7~]# echo $key2
22 [root@centos7~]# echo $key3 33 [root@centos7~]# read -p "请输入用户名:" user #设置一个提示信息 请输入用户名:jacob [root@centos7~]# echo $user jacob [root@centos7~]# read -t 3 -p "请输入用户名:" user 使用-t设置超时时间,3秒后read命令自动退出。 [root@centos7~]# read -n1 -p "按任意键:" key #仅读取一个字符 [root@centos7~]# read key #默认read命令不支持\ \ABC [root@centos7~]# echo $key #所以key的值没有\字符 ABC [root@centos7~]# read -r key #设置read命令支持读取\ \ABC [root@centos7~]# echo $key #查看结果,\被保留 \ABC [root@centos7~]# read -p "请输入密码:" pass 请输入密码:123
注意,这里提示输入密码后,当用户输入密码123时,计算机将密码的明文显示在屏幕上,这不是我们想看到的效果!怎么办?read命令支持-s选项,这个选项可以让用户输入的任何数据都不显示,但read命令依然可以读取用户输入的数据,只是数据不显示而已。
[root@centos7~]# read -s -p "请输入密码:" pass
下面我们看一个使用read命令编写的脚本案例。
![](https://epubservercos.yuewen.com/B53532/15937388204513606/epubprivate/OEBPS/Images/figure_0036_0001.jpg?sign=1738892202-o9amNP7KR1nt828yLk9AZJ4fe5dn3SYI-0-f509da72e903e887fc2da91bf9f24f8a)
这个脚本通过read命令读取用户输入的用户名和密码,并且在读取用户输入的密码时,不直接在屏幕上显示密码的内容,这样更安全。用户输入的用户名和密码分别保存在user和pass这两个变量中,下面就通过$调用变量中的值,使用useradd命令创建一个系统账户,使用passwd命令给用户配置密码。直接使用passwd修改密码默认采用人机交互的方式配置密码,需要人为手动输入密码,并且要重复输入两次。这里我们使用了一个|符号,这个符号就像管道,它的作用是将前一个命令的输出结果,通过管道传给后一个命令,作为后一个命令的输入。
有时候,在Linux系统中我们需要完成一个复杂的任务,但是某一个命令可能无法完成这个任务,此时,我们就需要使用管道把两个或多个命令组合在一起来完成这样的任务。
如图1-1所示,类似于传输水的管道,Linux系统的管道,可以将命令1的输出结果(数据),存储到管道中,然后让命令2从管道中读取数据,并对数据做进一步的处理。
![](https://epubservercos.yuewen.com/B53532/15937388204513606/epubprivate/OEBPS/Images/figure_0037_0001.jpg?sign=1738892202-zhcH4x8rxSxfp0eos63wkx72nQuoWEIW-0-82e3bb712c2f522965c6d7c5697ad63e)
图1-1 管道
下面我们看几个管道的案例。
[root@centos7~]# who root pts/1 2018-07-14 08:59 (:1) root pts/2 2018-07-14 09:00 (:1) root pts/5 2018-07-23 21:28 (117.101.192.169)
who这条命令,可以帮助我们查看有哪些账户在什么时间登录了计算机。但是,当计算机的登录信息非常多时,需要人为记录登录的数量就很不方便,而Linux系统中的wc命令可以统计行数,但wc命令是需要数据的,给wc若干行数据,这个命令就可以自动统计数据的行数。我们可以使用管道将who和wc命令结合在一起使用。
[root@centos7~]# who | wc -l 3
再比如,ss命令可以查看Linux系统中所有服务监听的端口列表。但是ss命令自身没有灵活的过滤功能,而grep命令有比较强大灵活的过滤功能,这样的话也可以通过管道将这两个命令结合在一起使用。
![](https://epubservercos.yuewen.com/B53532/15937388204513606/epubprivate/OEBPS/Images/figure_0038_0001.jpg?sign=1738892202-TTN52b0XgoHNltwBWLmP3pNC9LpGPuhG-0-61e9d2ec4576812353c8d3fce2c6fb9e)
很明显,没有使用grep命令过滤的数据量比较多,看起来不够清晰,而ss命令把自己输出的数据存入管道后,grep命令再从管道中读取数据,在众多数据中过滤出包含sshd的数据行,最后输出结果就只有两行数据。这样能比较简单明了地看到我们需要的数据。
有些命令比较特殊,比如前面我们用到的passwd命令,它是用来修改系统的账户密码的,但是该命令默认只能从键盘读取密码,如果希望命令从管道中读取数据后作为密码则需要使用--stdin选项。
[root@centos7~]# echo "123456" | passwd --stdin jacob
如图1-2所示,echo命令默认会把输出结果显示在屏幕上,而有了管道后,echo命令可以把输出的123456存储到管道中,passwd再从管道中读取123456,来修改系统账户jacob的密码。
![](https://epubservercos.yuewen.com/B53532/15937388204513606/epubprivate/OEBPS/Images/figure_0038_0002.jpg?sign=1738892202-0GuxSvqvfXdLC0Ej75jgDxAirhJkd4pW-0-937026b4b187d80d55935dc24b4b2235)
图1-2 echo命令