内容格式化
常用符号解读
信息传递
重定向
在shell脚本中有两类常见的重定向符号:
- 覆盖式重定向,> 表示将符号左侧的内容,以覆盖的方式输入到右侧文件中;< 表示将符号右侧的内容,以覆盖的方式输入到左侧文件中
- 追加式重定向,>> 表示将符号左侧的内容,以追加的方式输入到右侧文件的末尾行中;<< 表示将符号右侧的内容,以追加的方式输入到左侧文件的末尾行中
# 覆盖式重定向
echo nihao > file.txt
cat file.txt
nihao
# 使用重定向符号给文件中增加内容
echo "file1.txt" > file.txt
cat file.txt
file1.txt
# 追加式重定向
# 使用重定向符号给文件中增加内容
echo "file2.txt" >> file.txt
cat file.txt
file1.txt
file2.txt
管道符
|这个就是管道符,常用于将两个命令隔开,然后命令间(从左向右)传递信息使用的
使用格式,命令1 | 命令2,管道符左侧命令1 执行后的结果,传递给管道符右侧的命令2使用
# 查看当前系统中的全局变量SHELL
env | grep SHELL
SHELL=/bin/bash
终端输出
后台执行
&就是将一个命令从前台转到后台执行,使用格式如下:命令 &
# 前台执行休眠命令,界面卡住4秒后消失
sleep 4
# 后台执行休眠命令
sleep 10 &
[1] 3522
ps aux | grep sleep
root 3522 0.0 0.0 108052 360 pts/1 S 15:08 0:00 sleep 10
root 3524 0.0 0.0 112808 972 pts/1 S+ 15:08 0:00 grep --color=auto sleep
信息符号
- 1 表示正确输出的信息
- 2 表示错误输出的信息
- 2>&1 代表所有输出的信息,也可以简写为 "&>"
# 标准正确输出重定向到zhengque文件
cat file.txt 1>> zhengque
cat zhengque
file1.txt
file2.txt
# 标准错误输出重定向到errfile文件
dsfadsfadsfa 2>> errfile
cat errfile
-bash: dsfadsfadsfa: command not found
cat > ceshi.sh << "EOF"
#!/bin/bash
# 输出正确信息
echo '下一条错误命令'
# 执行错误命令,输出错误信息
dsfsafsafdsa
EOF
/bin/bash ceshi.sh
下一条错误命令
ceshi.sh: line 5: dsfsafsafdsa: command not found
/bin/bash ceshi.sh 1>> ceshi-ok 2>> ceshi-err
cat ceshi-ok
下一条错误命令
cat ceshi-err
ceshi.sh: line 5: dsfsafsafdsa: command not found
/bin/bash ceshi.sh >> ceshi-all 2>&1
cat ceshi-all
下一条错误命令
ceshi.sh: line 5: dsfsafsafdsa: command not found
rm -f file.txt zhengque errfile ceshi*
输入格式化
EOF原理
场景需求:在运维岗位中,有非常多的场景需要我们在脚本中编写应用软件的配置文件。在这些应用软件的配置文件中,经常携带大量的格式,尤其是携带空格的层级格式,如果我们一个一个的编写好标准的配置文件,但是一旦涉及到场景信息的动态化调整,固定好的配置文件就不太适合了,所以我们需要一种方法能够实现整个动态的格式化需求
解决方法:在shell编程中,"EOF”通常与"<<”结合使用,"<<EOF”表示后续的输入作为子命令或子shell的输入,直到遇到"EOF”,再次返回到主调用shell,可将其理解为分界符(delimiter)。所谓的 EOF,就是End of file的缩写,它是一种自定义的文件内容终止符。既然是分界符,那么形式自然不是固定的,这里可以将”EOF"可以进行自定义,但是前后的”EOF"必须成对出现且不能和shell命令冲突。第一个EOF如果用双引号引起来,这样EOF之间的内容则被当成纯字符串不会被提前解析,否则会提前解析里面的内容(例如解析变量)
语法格式
交互式程序 << EOF
command1
command2
...
EOF
注意:最后的"EOF"必须单独占一行,而且必须顶格写,如果不想受到如此限制的话,使用<<-符号。<<- 的作用是自动去除最后一个EOF前面的制表符\t【注意,对于空格无效】。前后两个EOF可以是任意一个字符,比如aaa,只要前后两个边界内容一致即可。
"EOF"中间的内容将以标准输入的形式输入到”交互式程序",当shell看到”<<"知道其后面输入的分界符,当shell再次看到分界符时,两个分界符中间的部分将作为标准输入。
# 终端方式接收多行信息,然后交给一个命令
cat << EOF
> Dear Wang:
> jian dao ni hen gaoxing.
> danshi wo bu xihuan ni.
> zaijian.
> zhaoliu
> 2100-11-11
> EOF
Dear Wang:
jian dao ni hen gaoxing.
danshi wo bu xihuan ni.
zaijian.
zhaoliu
2100-11-11
脚本中实践EOF
编写请假条脚本qingjiatiao.sh,内容如下
#!/bin/bash
# EOF演示请假条示例
cat << EOF
请假条
王老师:
我因患急性空腹病,现在需要到火锅理疗,不能到学校上课,
请准假一天。恳请批准!
请假人:李四
6月1日
EOF
# 执行脚本示例
/bin/bash qingjiatiao.sh
请假条
王老师:
我因患急性空腹病,现在需要到火锅理疗,不能到学校上课,
请准假一天。恳请批准!
请假人:李四
6月1日
# 末尾EOF前面有制表符
sed -i '$ s/EOF/\tEOF/' qingjiatiao.sh
# 执行脚本后效果
/bin/bash qingjiatiao.sh
qingjiatiao.sh: line 11: warning: here-document at line 4 delimited by end-of-file (wanted `EOF')
# 将<< 替换为 <<-
sed -i 's/<</<<-/' qingjiatiao.sh
# 脚本执行后的效果
/bin/bash qingjiatiao.sh
请假条
王老师:
我因患急性空腹病,现在需要到火锅理疗,不能到学校上课,
请准假一天。恳请批准!
请假人:李四
6月1日
rm -f qingjiatiao.sh
文本输入
cat实践
# 实现多行文件的输入
cat > file << EOF
...
EOF
# 定制主机解析名信息
cat >> hosts << EOF
10.0.0.13 k8s-master
10.0.0.14 k8s-node1
10.0.0.15 k8s-node2
EOF
cat hosts
10.0.0.13 k8s-master
10.0.0.14 k8s-node1
10.0.0.15 k8s-node2
定制nginx配置文件的脚本define-nginx-conf.sh,内容如下
#!/bin/bash
# 定制nginx的配置文件
# 定制配置文件目录
NGINX_DIR='/data/server/conf'
NGINX_CONF='nginx.conf'
# 创建基本目录
mkdir -p $NGINX_DIR
# 定制nginx配置文件
cat > $NGINX_DIR/$NGINX_CONF << EOF
server {
listen 80;
server_name www.example.com
listen / {
proxy_pass http://10.0.0.12:10086;
}
}
EOF
# 执行文件后的效果
/bin/bash define-nginx-conf.sh
cat /data/server/conf/nginx.conf
server {
listen 80;
server_name www.example.com
listen / {
proxy_pass http://10.0.0.12:10086;
}
}
rm -rf /data/server
rm -f hosts define-nginx-conf.sh
tee实践
tee命令读取标准输入,把这些内容同时输出到标准输出和(多个)文件中,tee命令可以重定向标准输出到多个文件。要注意的是:在使用管道符时,前一个命令的标准错误输出不会被tee读取
命令格式
- 样式1,只输出到标准输出,
tee - 样式2,输出到标准输出的同时,保存到文件file中,
tee file - 样式3,输出到标准输出的同时,追加到文件file中。如果文件不存在则创建;如果文件存在则追加。
tee -a file,tee host2 <<- EOF ... EOF - 样式4,输出到标准输出两次,
tee - - 样式5,输出到标准输出两次,同时保存到file1和file2中,
tee file1 file2 -
# 实践1 - 仅输出到当前屏幕
echo tee-test | tee
# 实践2 - 同时输出到屏幕和文件
echo tee-test | tee tee-file
cat tee-file
tee-test
# 实践3 - 追加到对应的文件中
echo tee-test1 | tee tee-file
cat tee-file
tee-test1
echo tee-test2 | tee -a tee-file
cat tee-file
tee-test1
tee-test2
# 实践4 - 输出多次信息
echo tee-test | tee
tee-test
echo tee-test | tee -
tee-test
tee-test
echo tee-test | tee - -
tee-test
tee-test
tee-test
echo tee-test | tee - - -
tee-test
tee-test
tee-test
tee-test
# 实践5 - 保存到多个文件
echo tee-test | tee file-1 file-2 file-3
cat file-1
tee-test
cat file-2
tee-test
cat file-3
tee-test
# 实践6 - 接收命令行多行信息,同时在文件和当前终端显示
tee host2 << EOF
> 10.0.0.13 k8s-master
> 10.0.0.14 k8s-node1
> 10.0.0.15 k8s-node2
> EOF
10.0.0.13 k8s-master
10.0.0.14 k8s-node1
10.0.0.15 k8s-node2
cat host2
10.0.0.13 k8s-master
10.0.0.14 k8s-node1
10.0.0.15 k8s-node2
rm -f tee-file host2 file-*
案例实践 - kubernetes参数配置
# 定制kubernetes的网络模块加载,将EOF中间的内容重定向为tee的标准输入
cat << EOF | tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
br_netfilter
# 定制kubernetes的数据包内核参数
cat << EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
sysctl --system
rm -f /etc/modules-load.d/k8s.conf /etc/sysctl.d/k8s.conf
输出格式化
echo解读
echo [SHORT-OPTION]... [STRING]...echo命令的功能是将内容输出到默认显示设备,一般起到一个提示的作用
| 选项 | 说明 |
|---|---|
| -n | 不要在最后自动换行 |
| -e | 启用反斜杠转义的解释 |
| -E | 禁用反斜杠转义的解释(默认) |
如果-e生效,则识别以下转义字符
| 转义字符 | 说明 |
|---|---|
\\ | 反斜杠字符 |
| \a | 发出警告声 |
| \b | 删除前一个字符 |
| \c | 最后不加上换行符号 |
| \e | 可以用于设置颜色 |
| \f | 换行但光标仍旧停留在原来的位置,换页(formfeed) |
| \n | 换行且光标移至行首 |
| \r | 光标移至行首,但不换行 |
| \t | 水平制表符 |
| \v | 垂直制表符 |
| \0NNN | 打印NNN(八进制)所代表的ASCII字符 |
| \xHH | 打印NN(十六进制)所代表的ASCII字符 |
# 信息的输出
# echo后边用单引号包围要添加的内容
echo 'hello world' > hello.txt
cat hello.txt
hello world
# 引号信息输出
echo "I'm a king of the world."
I'm a king of the world.
# 特殊符号的输出
# 使用 -e 选项启用转义字符的解析
echo -e "The 2021 State of DevOps Report\n\t- is here"
The 2021 State of DevOps Report
- is here
# 内容的拼接
# 使用 -n 选项启用信息输出不换行
echo -n hello;echo world
helloworld
rm -f hello.txt
字体颜色
场景需求
echo本质上是将信息内容输出到当前的屏幕终端,如果只是一种颜色的话,可能导致视觉疲劳。所以,一般情况下,我们在显示信息的时候,往往会通过颜色的方式实现特定内容的颜色高亮显示。echo命令可以修改字体类型,字体背景色以及字体颜色,转义序列\033可以用于改变字体属性
格式解读
# 格式如下:echo -e "\033[字背景颜色;文字颜色m字符串\033[0m"
echo -e "\033[41;36m 显示的内容 \033[0m"
颜色分类
| 色彩 | 黑 | 红 | 绿 | 黄 | 蓝 | 紫 | 青 | 灰 |
|---|---|---|---|---|---|---|---|---|
| 字体色 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
| 背景色 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
结束控制符
最后面控制选项说明 \033[0m 关闭所有属性 \033[1m 设置高亮度 \033[4m 下划线 \033[5m 闪烁 \033[7m 反显 \033[8m 消隐 注意: \033 是八进制的ESCAPE字符,我们可以用 \e 来代替
最常用的是字体颜色红绿蓝(31,32,33)、开始符号\033[、分隔符m、结束符\033[0m
# 字体颜色示例
echo -e "\033[30m 黑色字 \033[31m 红色字 \033[32m 绿色字 \033[33m 黄色字 \033[0m"
echo -e "\033[34m 蓝色字 \033[35m 紫色字 \033[36m 天蓝字 \033[37m 白色字 \033[0m"
# 背景颜色示例
echo -e "\033[40;37m 黑底白字 \033[41;37m 红底白字 \033[42;37m 绿底白字 \033[0m"
echo -e "\033[43;37m 黄底白字 \033[44;37m 蓝底白字 \033[45;37m 紫底白字 \033[0m"
echo -e "\033[46;37m 天蓝底白字 \033[47;30m 白底黑字 \033[0m"
信息颜色显示
# 定制堡垒机的测试页面脚本
cat > simple_jumpserver.sh << "EOF"
#!/bin/bash
# 功能:定制堡垒机的展示页面
# 作者:zhang
# 版本:V0.1
# 联系:zhang.com
echo -e "\e[31m \t\t 欢迎使用堡垒机"
echo -e "\e[32m
-----------请选择你要登录的远程主机-----------
1: 10.0.0.14 (nginx)
2: 10.0.0.15 (tomcat)
3: 10.0.0.19 (apache)
q: 使用本地主机
----------------------------------------------
"'\033[0m'
echo -e "请输入您要选择的远程主机编号: "
EOF
/bin/bash simple_jumpserver.sh
rm -f simple_jumpserver.sh
printf格式化
场景需求
虽然我们能够通过echo的方式实现信息某种程度的格式化输出,但是很多信息的输出偏重于手工的干预,效率太慢。我们需要一种功能更强大、效率更高的格式化手段
printf简介
printf 命令模仿 C 程序库(library)里的 printf() 程序。由于 printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以指定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n
基本语法
printf [-v var] format [arguments], -v var表示将输出赋值给shell变量VAR,而不是显示在标准输出上
| 格式化替换符 | 说明 |
|---|---|
| %s | 字符串 |
| %d,%i | 十进制整数 |
| %f | 浮点格式,默认展示小数点后6位,可以通过%.2f(表示小数点后2位)这种格式指定展示小数点后几位 |
| %c | ASCII字符,即显示对应参数的第一个字符 |
| %b | 相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义 |
| %o | 八进制值 |
| %u | 不带正负号的十进制值 |
| %x | 十六进制值(a-f) |
| %X | 十六进制值(A-F) |
| %% | 表示%本身 |
| 格式化转义符号 | 说明 |
|---|---|
\" | 双引号 |
\\ | 一个字面上的反斜杠字符 |
| \a | 警告字符,通常为ASCII的BEL字符 |
| \b | 后退 |
| \c | 抑制(不显示)输出结果中任何结尾的换行字符 |
| \e | 可以用于设置颜色 |
| \f | 换页(formfeed) |
| \n | 换行 |
| \r | 回车(Carriage return) |
| \t | 水平制表符 |
| \v | 垂直制表符 |
| \NNN | 八进制NNN字节(1到3位数字) |
| \xHH | 十六进制HH字节(1 ~ 2位) |
| \uHHHH | Unicode (ISO/IEC 10646)字符,十六进制值为HHHH(4位数字) |
| \UHHHHHHHH | 十六进制值为HHHHHHHH的Unicode字符(8位) |
格式化字符解释
-将字段里已格式化的值向左对齐空格在正值前置一个空格,在负值前置一个负号+总是在数值之前放置一个正号或负号,即便是正值也是#下列形式选择其一- %o有一个前置的o;%x与%X分别前置的0x与0X
- %e,%E与%f总是在结果中有一个小数点;%g与%G为没有结尾的零
0以零填补输出,而非空白,这仅发生在字段宽度大于转换后的情况- 格式化时%后面可以添加一个数字,表示列宽,默认是右对齐,再添加一个减号表示左对齐;格式化整数时,还可以在列宽前面加个0表示用零填补输出,默认是空白(只有右对齐时才可以补0,左对齐时不会,因为这样会引起歧义)
简单实践
# 实践1 - 命令演示
# 普通echo命令演示换行信息和非换行信息
echo "Hello, Shell Base"
echo -n "Hello, Shell Base"
# printf命令演示换行信息和非换行信息
printf "Hello, Shell Base\n"
printf "Hello, Shell Base"
# 实践2 - 替换符号演示
# 基本信息演示 - 单引号和双引号效果一样
# 注意:相关信息会按照顺序依次替换到格式化的替换符位置
printf "姓名:%s, 语文:%d, 数学:%d\n" "张三" 89 98
姓名:张三, 语文:89, 数学:98
printf '姓名:%s, 语文:%d, 数学:%d\n' "张三" 89 98
姓名:张三, 语文:89, 数学:98
# 数字格式展示
# 注意:通过 %.nf,n代表小数点后面的可显示的位数
printf '姓名:%s, 身高:%f米, 体重:%f公斤\n' "张三" 1.7 66
姓名:张三, 身高:1.700000米, 体重:66.000000公斤
printf '姓名:%s, 身高:%.2f米, 体重:%.1f公斤\n' "张三" 1.7 66
姓名:张三, 身高:1.70米, 体重:66.0公斤
# 实践3 - 简单格式化
# () 用于信息的批量化显示
printf "(%d %s)\n" 1 张三 2 李四 3 王五
(1 张三)
(2 李四)
(3 王五)
# printf默认不携带最后的换行
printf "(%d %s) " 1 张三 2 李四 3 王五
(1 张三) (2 李四) (3 王五)
# 借助于echo的功能实现换行的效果
printf "(%d %s) " 1 张三 2 李四 3 王五; echo
(1 张三) (2 李四) (3 王五)
# 实践4 - 格式化补充
# %s字符串格式化,注意:%-10s,代表信息后面携带10个空格(不加-是前面携带10个空格),便于格式化
printf '姓名:%-10s语文:%d, 数学:%d\n' "张三" 89 98 "李四" 87 97
姓名:张三 语文:89, 数学:98
姓名:李四 语文:87, 数学:97
# %d数字格式补全,注意:0的作用就是不用空格补全,而是用0填充,实现格式化
printf "%5d %s\n" 1 张三
1 张三
printf "%05d %s\n" 1 张三
00001 张三
printf "%05d %s\n" 100 张四
00100 张四
# 实践5 - 环境变量的使用
# 定制环境变量
VAR1=hello; VAR2=shell
# 颜色显示
printf "\033[32m%s \033[31m%s\033[0m\n" $VAR1 $VAR2
hello shell
综合案例
这一节,我们从 java部署、脚本定制,两个方面来学习
java部署
# 创建目录
mkdir /data/{softs,server} -p
cd /data/softs
# 下载java或者上传java
wget https://builds.openlogic.com/downloadJDK/openlogic-openjdk/8u392-b08/openlogic-openjdk-8u392-b08-linux-x64.tar.gz
tar xf openlogic-openjdk-8u392-b08-linux-x64.tar.gz -C /data/server
cd /data/server/
ln -s openlogic-openjdk-8u392-b08-linux-x64 java
# 配置java环境变量
echo 'export JAVA_HOME=/data/server/java' >> /etc/profile.d/java.sh
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> /etc/profile.d/java.sh
source /etc/profile.d/java.sh
# 检查效果
java -version
# 手工清理环境
rm -f /etc/profile.d/java.sh
rm -rf /data/server/java /data/server/openlogic-openjdk-8u392-b08-linux-x64
定制java环境部署脚本
mkdir /data/scripts
/data/scripts/java_install.sh 脚本内容如下
#!/bin/bash
# 功能: java环境的定制
# 版本:v0.1
# 作者:zhang
# 联系:www.zhang.com
# 定制基本信息
JAVA_SOFT='openlogic-openjdk-8u392-b08-linux-x64.tar.gz'
JAVA_DIR='openlogic-openjdk-8u392-b08-linux-x64'
JAVA_NAME='java'
SERVER_HOME='/data/server'
SOFTS_HOME='/data/softs'
RED="echo -e \E[1;31m"
END="\E[0m"
# 安装软件
tar xf $SOFTS_HOME/$JAVA_SOFT -C $SERVER_HOME
ln -s $SERVER_HOME/$JAVA_DIR $SERVER_HOME/$JAVA_NAME
# 定制环境变量
cat > /etc/profile.d/java.sh << EOF
export JAVA_HOME=$SERVER_HOME/$JAVA_NAME
export PATH=\$JAVA_HOME/bin:\$PATH
EOF
# 加载java环境变量文件
source /etc/profile.d/java.sh
# 检查效果
java -version > /tmp/txt 2>&1
java_version=$(grep version /tmp/txt | cut -d '"' -f2)
runtime_version=$(grep Runtime /tmp/txt | cut -d ' ' -f5 | cut -d ")" -f1)
jvm_type=$(grep VM /tmp/txt | cut -d " " -f7)
# 信息显示
$RED---------$JAVA_NAME软件运行情况---------$END
printf "\033[32m%s:\033[33m%s\033[0m\n" "java软件版本" $java_version
printf "\033[32m%s:\033[33m%s\033[0m\n" "java运行时环境版本" $runtime_version
printf "\033[32m%s:\033[33m%s\033[0m\n" "jvm运行模式" $jvm_type
$RED----------------------------------$END
cd /data/scripts
/bin/bash java_install.sh
---------java软件运行情况---------
java软件版本:1.8.0_392-392
java运行时环境版本:1.8.0_392-392-b08
jvm运行模式:mixed
----------------------------------