Shell基础
Shell基础
一、Shell 分类
常用sh
和bash
/etc/shells
文件来查询Linux
支持的Shell
<root@localhost ~># cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/usr/bin/tmux
/bin/tmux
/usr/bin/zsh
/bin/zsh
二、 echo 命令
echo [选项] [输出内容
选项:
-e
: 支持反斜线控制的字符转换(具体参见下面)
-n
: 取消输出后行末的换行符号(就是内容输出后不换行)
在echo
命令中如果使用了-e
选项,则可以支持控制字符
控制字符 | 作 用 |
---|---|
\\ |
输出\本身 |
\a | 输出警告音 |
\b |
退格键,也就是向左删除键 |
\c |
取消输出行末的换行符。和“-n”选项一致 |
\e | ESCAPE 键 |
\f | 换页符 |
\n |
换行符 |
\r | 回车键 |
\t |
制表符,也就是Tab 键 |
\0nnn | 按照八进制ASCII 码表输出字符。其中0 为数字零,nnn 是三位八进制数 |
\xhh | 按照十六进制ASCII 码表输出字符。其中hh 是两位十六进制数 |
<root@localhost ~># echo -e "ab\bc"
ac
<root@localhost ~># echo -e "\\"
\
<root@localhost ~># echo -e "a\tb\tc"
a b c
<root@localhost ~># echo -e "a\tb\tc\nd\te\tf"
a b c
d e f
#解释下这个命令\e[1 是标准格式,代表颜色输出开始,\e[0m
echo -e "\e[1;31m abcd \e[0m"
#代表颜色输出结束,31m 定义字体颜色是红色。echo 能够识别的颜色如下:
30m=黑色,31m=红色,32m=绿色,33m=黄色,34m=蓝色,35m=洋红,36m=青色,37m=白色。
#这条命令会给abcd 加入一个绿色的背景。echo 可以使用的背景颜色如下:40m=黑色,41m=红色,
42m=绿色,43m=黄色,44m=蓝色,45m=洋红,46m=青色,47m=白色。
<root@centos8 ~># cat color.sh
#!/bin/bash
echo -e "\033[0m none \033[0m"
echo -e "\033[30m black \033[0m"
echo -e "\033[1;30m dark_gray \033[0m"
echo -e "\033[0;34m blue \033[0m"
echo -e "\033[1;34m light_blue \033[0m"
echo -e "\033[0;32m green \033[0m"
echo -e "\033[1;32m light_green \033[0m"
echo -e "\033[0;36m cyan \033[0m"
echo -e "\033[1;36m light_cyan \033[0m"
echo -e "\033[0;31m red \033[0m"
echo -e "\033[1;31m light_red \033[0m"
echo -e "\033[0;35m purple \033[0m"
echo -e "\033[1;35m light_purple \033[0m"
echo -e "\033[0;33m brown \033[0m"
echo -e "\033[1;33m yellow \033[0m"
echo -e "\033[0;37m light_gray \033[0m"
echo -e "\033[1;37m white \033[0m"
echo -e "\033[0m none \033[0m"
echo -e "\033[0m none \033[0m"
echo -e "\033[0m none \033[0m"
echo -e "\033[0m none \033[0m"
echo -e "\033[0m none \033[0m"
echo -e "\033[40;37m 黑底白字 \033[0m"
echo -e "\033[41;30m 红底黑字 \033[0m"
echo -e "\033[42;34m 绿底蓝字 \033[0m"
echo -e "\033[43;34m 黄底蓝字 \033[0m"
echo -e "\033[44;30m 蓝底黑字 \033[0m"
echo -e "\033[45;30m 紫底黑字 \033[0m"
echo -e "\033[46;30m 天蓝底黑字 \033[0m"
echo -e "\033[47;34m 白底蓝字 \033[0m"
echo -e "\033[4;31m 下划线红字 \033[0m"
echo -e "\033[5;31m 红字在闪烁 \033[0m"
echo -e "\033[8m 消隐 \033[0m "
三、Shell 脚本的执行
Linux 中脚本的执行主要有这样两种种方法
- 赋予执行权限,直接运行
chmod 755 hello.sh
chmod +x abc.sh
./hello.sh
- 通过Bash 调用执行脚本
bash hello.sh
sourch hell0.sh
<root@localhost ~># cat <<EOF >>hool.sh
> #!/bin/bash
> echo `date`
> EOF
<root@localhost ~># . hool.sh
2021年 12月 02日 星期四 03:06:10 CST
<root@localhost ~># source hool.sh
2021年 12月 02日 星期四 03:06:10 CST
<root@localhost ~># bash hool.sh
2021年 12月 02日 星期四 03:06:10 CST
<root@localhost ~># sh hool.sh
2021年 12月 02日 星期四 03:06:10 CS
四、Bash 的基本功能
1. 历史命令
history [选项] [历史命令保存文件]
选项:
-c
: 清空历史命令
-w
: 把缓存中的历史命令写入历史命令保存文件。如果不手工指定历史命令保存文 件,则放入默认历史命令保存文件~/.bash_history
中
历史命令的查看
vi /etc/profile
...省略部分输出...
HISTSIZE=1000
...省略部分输出...
history -w
#把缓存中的历史命令直接写入~/.bash_history
history -c
#清空历史命令
注意:直接关闭终端并不会保存历史命令,如果想保存的话,要手动 history -w 或 logout
历史命令的调用
如果想要使用原先的历史命令有这样几种方法:
- 使用上、下箭头调用以前的历史命令
- 使用
!n
重复执行第n 条历史命令 - 使用
!!
重复执行上一条命令 - 使用
!字串
重复执行最后一条以该字串开头的命令 - 使用
!$
重复上一条命令的最后一个参数,同alt + .
2.命令别名
alias 别名='原命令'
alias vi='vim'
# 查询系统中已经定义好的别名
# alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias xzegrep='xzegrep --color=auto'
alias xzfgrep='xzfgrep --color=auto'
alias xzgrep='xzgrep --color=auto'
alias zegrep='zegrep --color=auto'
alias zfgrep='zfgrep --color=auto'
alias zgrep='zgrep --color=auto'
永久生效:别名写入环境变量配置文件 ~/.bashrc
# vi /root/.bashrc
3. Bash 常用快捷键
快捷键 | 作 用 |
---|---|
ctrl+A |
把光标移动到命令行开头,相当于HOME |
ctrl+E |
把光标移动到命令行结尾,相当于END |
ctrl+C |
强制终止当前的命令 |
ctrl+L |
清屏,相当于clear 命令。 |
ctrl+U |
删除或剪切光标之前的命令 |
ctrl+K |
删除或剪切光标之后的内容。 |
ctrl+Y |
粘贴ctrl+U 或ctrl+K 剪切的内容 |
ctrl+R |
历史命令中搜索 |
ctrl+D |
退出当前终端 |
ctrl+Z |
暂停,并放入后台 |
ctrl+S |
暂停屏幕输出 |
ctrl+Q |
恢复屏幕输出 |
ctrl+左右键 |
在单词之间跳转 |
Ctrl+w |
删除光标前面的单词的字符 |
4. 输入输出重定向
1️⃣Bash 的标准输入输出
设备 | 设备文件名 | 文件描述符 | 类型 |
---|---|---|---|
键盘 | /dev/stdin | 0 | 标准输入 |
显示器 | /dev/stdout | 1 | 标准输出 |
显示器 | /dev/stderr | 2 | 标准错误输出 |
2️⃣输出重定向
使用>
或者>>
对输出进行重定向。符号的左边表示文件描述符,如果没有的话表示1
,也就是标准输出,符号的右边可以是一个文件,也可以是一个输出设备。
命令 | 介绍 |
---|---|
command >filename | 把标准输出重定向到新文件中 |
command 1>filename | 同上 |
command >>filename | 把标准输出追加到文件中 |
command 1>>filename | 同上 |
command 2>filename | 把标准错误重定向到新文件中 |
command 2>>filename | 把标准错误追加到新文件中 |
命令 > 文件 2>&1 |
以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中 |
命令 >> 文件 2>&1 |
以追加的方式,把正确输出和错误输出都保存到同一个文件当中 |
命令 &>文件 | 以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中 |
命令 &>>文件 | 以追加的方式,把正确输出和错误输出都保存到同一个文件当中 |
命令>>文件1 2>>文件2 | 把正确的输出追加到文件1 中,把错误的输出追加到文件2 中 |
重定向绑定
1. >/dev/null
将标准输出1重定向到/dev/null
中。 /dev/null
代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。
2. 2>&1
这条命令用到了重定向绑定,采用&可以将两个输出绑定在一起。错误输出和标准输出输出到同一个地方。
>/dev/null 2>&1
的作用就是让标准输出重定向到/dev/null
中(丢弃标准输出),然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了/dev/null
中,错误输出同样也被丢弃了。
3. >/dev/null 2>&1
VS 2>&1 >/dev/null
命令 | 标准输出 | 错误输出 |
---|---|---|
>/dev/null 2>&1 | 丢弃 | 丢弃 |
2>&1 >/dev/null | 丢弃 | 屏幕 |
# nohup java -jar xxxx.jar >/dev/null 2>&1 &
# nohup /root/runoob.sh > runoob.log 2>&1 &
3️⃣输入重定向
使用<
或<<
对输入做重定向,如果符号左边没有写值,那么默认就是0
命令 | 介绍 |
---|---|
command <filename | 以filename文件作为标准输入 , cat >out <input |
command 0<filename | 同上 |
command <<delimiter | 从标准输入中读入,直到遇到delimiter分隔符 |
wc [选项] [文件名]
选项:
-c 统计字节数
-w 统计单词数
-l 统计行数
wc -lcw << EOF
> 欢迎来到
> 菜鸟教程
> www.runoob.com
> EOF
4️⃣综合高级用法
# 从标准输入读入数据并重定向到/etc/yum.repos.d/kubernetes.repo文件中
# 直接存入文件(会覆盖文件)
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
#和上面一样(会覆盖文件)
#将标准输入内容重定向到filename.txt 文件且会覆盖
cat > filename.txt <<EOF
> Hello, World
> Linux!
> EOF
cat <<EOF > filename.txt
> Hello, World
> Linux!
> EOF
# 将标准输入内容重定向到hosts文件且不会覆盖
cat >> /etc/hosts <<EOF
192.168.0.199 k8svip
192.168.0.200 master01
192.168.0.201 master02
192.168.0.202 master03
192.168.0.210 worker01
EOF
cat <<EOF >> /etc/hosts
192.168.0.199 k8svip
192.168.0.200 master01
192.168.0.201 master02
192.168.0.202 master03
192.168.0.210 worker01
EOF
# 从标准输入读入数据并作为tee命令的标准输入,由于tee命令是读取标准输入显示屏幕并保存文件
# 所有下面的内容会输出到屏幕并保存文件
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF
5. 多命令顺序执行
多命令执行符 | 格 式 | 作 用 |
---|---|---|
; | 命令1 ;命令2 | 多个命令顺序执行,命令之间没有任何逻辑联系 |
&& | 命令1 && 命令2 | 当命令1 正确执行(\(?=0),则命令2 才会执行 <br/>当命令1 执行不正确(\)? ≠0),则命令2 不会执行 |
|| | 命令1 || 命令2 | 当命令1 执行不正确(\(? ≠0),则命令2 才会执行 <br/>当命令1 正确执行(\)?=0),则命令2 不会执行 |
# 如果ls 命令执行正确则输出为yes,否则输出为no
ls && echo yes || echo no
# 如果ls 命令执行不正确则输出为yes,否则输出为no
lsdd && echo yes || echo no
# 如果ls 命令执行正确则输出为no,否则输出为no
ls || echo yes && echo no
# 如果ls 命令执行不正确则输出为yes 和 no
lsdd || echo yes && echo no
6. 管道符 |
把上一个命令的输出,做下一个命令的输出
ll -a /etc/ | more
rpm -qa | grep httpd
netstat -an | grep "ESTABLISHED"
#查询下本地所有网络连接,提取包含ESTABLISHED(已建立连接)的行
#就可以知道我们的服务器上有多少已经成功连接的网络连接
netstat -an | grep "ESTABLISHED" | wc -l
#如果想知道具体的网络连接数量,就可以再使用wc 命令统计行数
行提取命令grep
grep [选项] "搜索内容" 文件名
选项:
-A 数字
: 列出符合条件的行,并列出后续的n 行
-B 数字
: 列出符合条件的行,并列出前面的n 行
-c
: 统计找到的符合条件的字符串的次数
-i
: 忽略大小写
-n
: 输出行号
-v
: 反向查找
--color=auto
搜索出的关键字用颜色显示
grep "/bin/bash" /etc/passwd
grep -A 3 "root" /etc/passwd
# 查找可以登录的用户,并显示行号
grep -n "/bin/bash" /etc/passwd
# 查找不含有“/bin/bash”的行,其实就是列出所有的伪用户
grep -v "/bin/bash" /etc/passwd
grep -ncv "/bin/bash" /etc/passwd
7. 通配符
通配符 | 作 用 |
---|---|
? | 匹配一个任意字符 |
* | 匹配0 个或任意多个任意字符,也就是可以匹配任何内容 |
[] | 匹配中括号中任意一个字符。例如:[abc]代表一定匹配一个字符,或者是a,或者是b,或者是c。 |
[-] | 匹配中括号中任意一个字符,-代表一个范围。例如:[a-z]代表匹配一个小写字母。 |
[^] | [^] 逻辑非,表示匹配不是中括号内的一个字符。例如:[^0-9] 代表匹配一个不是数字的字符。 |
#“*”代表所有的文件
[root@localhost tmp]# ls *
012 0abc abc abcd
#“?”匹配任意一个字符,所以会匹配0abc
#但是不能匹配abc,因为“?”不能匹配空
[root@localhost tmp]# ls ?abc
0abc
#匹配任何以数字开头的文件
[root@localhost tmp]# ls [0-9]*
012 0abc
#匹配不已数字开头的文件
[root@localhost tmp]# ls [^0-9]*
abc abcd
8. Bash 中其他特殊符号
符 号 | 作 用 |
---|---|
' ' 单引号 | 单引号。在单引号中所有的特殊符号,如“$”和“`”(反引号)都没有特殊含义。 |
" " | 双引号。在双引号中特殊符号都没有特殊含义,但是` |
------------- | ------------------------------------------------------------ |
、`(反引号) 和 \ 是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。 | |
` | 两个反引号。反引号括起来的内容是系统命令,在Bash 中会先执行它。和\(()作用一样,不过推荐使用\)(),因为反引号非常容易看错 |
$( ) | 和反引号作用一样,用来引用系统命令。 |
( ) | 用于一串命令执行时,( )中的命令会在子Shell 中运行 |
用于一串命令执行时,{ }中的命令会在当前Shell 中执行。也可以用于变量变形与替换。 | |
[ ] | 用于变量的测试 |
# | 在Shell 脚本中,#开头的行代表注释。 |
$ | 用于调用变量的值,如需要调用变量 name 的值时,需要用$name 的方式得到变量的值。 |
\ | 转义符,跟在 \ 之后的特殊符号将失去特殊含义,变为普通字符。如\$ 将输出$ 符号,而不当做是变量引用 。相当于单引号 |
"单引号和双引号"
[root@localhost ~]# name=sc
#定义变量name 的值是sc
[root@localhost ~]# echo '$name'
$name
#如果输出时使用单引号,则$name 原封不动的输出
[root@localhost ~]# echo "$name"
sc
#如果输出时使用双引号,则会输出变量name 的值sc
"反引号"
<root@localhost ~># echo date
date
<root@localhost ~># echo `date`
2021年 12月 02日 星期四 14:31:35 CST
<root@localhost ~># echo $(date)
2021年 12月 02日 星期四 14:31:46 CST
<root@localhost ~># echo "`date`"
2021年 12月 02日 星期四 14:32:14 CST
<root@localhost ~># echo '`date`'
`date`
小括号、中括号和大括号
在介绍小括号和大括号的区别之前,我们先要解释一个概念,那就是父Shell 和子 Shell。在我
们的Bash 中,是可以调用新的Bash 的,比如:
[root@localhost ~]# bash
这时,我们通过pstree 命令查看一下进程数:
[root@localhost ~]#
[root@localhost ~]# pstree
init─┬─abrt-dump-oops
...省略部分输出
├─sshd─┬─sshd───bash───bash───pstree
知道了父Shell 和子Shell,我们接着解释小括号和大括号的区别。如果是用于一串命令的执行,那么小括号和大括号的主要区别在于:
- ( )执行一串命令时,需要重新开一个子shell 进行执行
- { }执行一串命令时,是在当前shell 执行;
- ( )和{ }都是把一串的命令放在括号里面,并且命令之间用;号隔开;
- ( )最后一个命令可以不用分号;
- { }最后一个命令要用分号;
- { }的第一个命令和左括号之间必须要有一个空格;
- ( )里的各命令不必和括号有空格;
- ( )和{ }中括号里面的某个命令的重定向只影响该命令,但括号外的重定向则影响到括号里的所有命令。
"小括号、中括号和大括号"
#在父Shell 中定义变量name 的值是sc
[root@localhost ~]# name=sc
#如果用()括起来一串命令,这些命令都可以执行
#给name 变量重新赋值,但是这个值只在子Shell 中生效
#另开一个子shell,并把运行结果输出当前shell
[root@localhost ~]# (name=liming;echo $name)
liming
#父Shell 中name 的值还是sc,而不是liming
[root@localhost ~]# echo $name
sc
#但是用大括号来进行一串命令的执行时,name 变量的修改是直接在父Shell 当中的
#注意大括号的格式 空格和最后一个命令后的 ;
[root@localhost ~]# { name=liming;echo $name; }
liming
#所以name 变量的值已经被修改了
[root@localhost ~]# echo $name
liming
五、Bash 的变量和运算符
1. 定义变量
- 变量名称可以由字母、数字和下划线组成,但是不能以数字开头。如果变量名是“2name”则是错误的。
- 在 Bash 中,变量的默认类型都是字符串型,如果要进行数值运算,则必修指定变量类型为数值型。
- 变量用等号连接值,等号左右两侧不能有空格。
- 变量的值如果有空格,需要使用单引号或双引号包括。如:test="hello world! "。其中双引号括起来的内容“$”、“\”和反引号都拥有特殊含义,而单引号括起来的内容都是普通字符。
- 在变量的值中,可以使用“\”转义符。
- 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含"\(变量名"或用\){变量名}包含变量名。例如
变量值的叠加可以使用两种格式:“$变量名”或${变量名}
[root@localhost ~]# test=123
[root@localhost ~]# test="$test"456
[root@localhost ~]# echo $test
123456
#叠加变量test,变量值变成了123456
[root@localhost ~]# test=${test}789
[root@localhost ~]# echo $test
123456789
#再叠加变量test,变量值编程了123456789
-
如果是把命令的结果作为变量值赋予变量,则需要使用反引号或$()包含命令
-
环境变量名建议大写,便于区分
2. 变量的分类
-
用户自定义变量:这种变量是最常见的变量,由用户自由定义变量名和变量的值
-
环境变量(大写)
- 自定义环境变量:所有
shell
中都可以使用 - 系统环境变量:这种变量中主要保存的是和系统操作环境相关的数据
- 自定义环境变量:所有
-
位置参数变量:这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的
-
预定义变量:是Bash 中已经定义好的变量,变量名不能自定义,变量作用也是固定的
3. 用户自定义变量
"变量定义 "
name=shen chao
"变量调用 "
echo $name
"set 命令可以查看所有变量"
set [选项]
选项:
-u: 如果设定此选项,调用未声明变量时会报错(默认无任何提示) 变量不存在和变量值为空是不同的概念
-x: 如果设定此选项,会在执行每一行 shell 脚本时,把执行的内容输出来
BASH=/usr/bin/bash
BASH_VERSION='4.4.20(1)-release'
COLUMNS=110
EUID=0
GROUPS=()
HISTCONTROL=ignoredups
HISTFILE=/root/.bash_history
HISTFILESIZE=1000
HISTSIZE=1000
HOME=/root
HOSTNAME=localhost.localdomain
HOSTTYPE=x86_64
LANG=zh_CN.UTF-8
LINES=22
LOGNAME=root
SSH_TTY=/dev/pts/0
which_declare='declare -f'
which_opt=-f
which_shell=bash
<root@localhost ~># 查看系统环境变量
<root@localhost ~># echo $LANG
zh_CN.UTF-8
"变量删除"
unset 变量名
4. 用户自定义环境变量
使用export
声明的变量即是环境变量
"环境变量设置 "
export age="18
"环境变量查询和删除"
unset gender
env | grep gender
<root@localhost ~># env | grep LANG
LANG=zh_CN.UTF-8
<root@localhost ~># echo $LANG
zh_CN.UTF-8
"env 命令和set 命令的区别是,set 命令可以查看所有变量,而env 命令只能查看环境变量
"系统默认环境变量
<root@centos8 ~># env
LS_COLORS ....... 定义颜色显示
HOSTNAME=localhost.localdomain 主机名
SHELL=/bin/bash 当前的shell
TERM=linux 终端环境
HISTSIZE=1000 历史命令条数
SSH_CLIENT=192.168.4.159 4824 22 当前操作环境是用ssh 连接的,这里记录客户端ip
SSH_TTY=/dev/pts/1 ssh 连接的终端时pts/1
USER=root 当前登录的用户
HOSTTYPE=x86_64 主机架构
LANG=zh_CN.UTF-8 语言
age=18 我们刚刚定义的环境变量
PATH=/usr/lib/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/
root/bin 系统查找命令的路径
MAIL=/var/spool/mail/root 用户邮箱
PWD=/root 当前所在目录
HOME=/root 当前登录用户的家目录
SHLVL=2 当前在第二层子shell 中。
LOGNAME=root 登录用户
_=/bin/env 上次执行命令的最后一个参数或命令本身
<root@centos8 ~># echo $HOSTNAME
localhost.localdomain
<root@centos8 ~># hostname
centos8
<root@centos8 ~># echo $SHELL
/bin/bash
<root@centos8 ~># echo $BASH
/usr/bin/bash
<root@centos8 ~># echo $TERM
xterm
<root@centos8 ~># echo $HISTSIZE
1000
<root@centos8 ~># echo $SSH_CLIENT
192.168.19.1 14893 22
<root@centos8 ~># echo $SSH_TTY
/dev/pts/0
<root@centos8 ~># echo $USER
root
<root@centos8 ~># echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
<root@centos8 ~># echo $HOSTTYPE
x86_64
<root@centos8 ~># echo $LANG
zh_CN.UTF-8
env 命令可以查询到所有的环境变量,可是还有一些变量虽然不是环境变量,却是和Bash 操作接
口相关的变量,这些变量也对我们的 Bash 操作终端起到了重要的作用。这些变量就只能用 set 命令
来查看了,我只列出重要的内容:
[root@localhost ~]# set
BASH=/bin/bash Bash 的位置
BASH_VERSINFO=([0]="4" [1]="1" [2]="2" [3]="1" [4]="release"
[5]="i386-redhat-linux-gnu") Bash 版本
BASH_VERSION='4.1.2(1)-release' bash 的版本
COLORS=/etc/DIR_COLORS 颜色记录文件
HISTFILE=/root/.bash_history 历史命令保存文件
HISTFILESIZE=1000 在文件当中记录的历史命令最大条数
HISTSIZE=1000 在缓存中记录的历史命令最大条数
LANG=zh_CN.UTF-8 语系环境
MACHTYPE=i386-redhat-linux-gnu 软件类型是i386 兼容类型
MAILCHECK=60 每60 秒去扫描新邮件
PPID=2166 父shell 的PID。我们当前Shell 是一个子shell
PS1='[\u@\h \W]\$ ' 命令提示符
PS2='> ' 如果命令一行没有输入完成,第二行命令的提示符
UID=0 当前用户的UID
5. 几个环境变量
PATH 变量:系统查找命令的路径
"PATH 环境变量的值:
echo $PATH
"PATH 变量的值是用“:”分割的路径,这些路径就是系统查找命令的路径。也就是说当我们输入了一个程序名,如果没有写入路径,系统就会到 PATH 变量定义的路径中去寻找,是否有可以执行的程序。如果找到则执行,否则会报“命令没有发现”的错误"
# 把自己的脚本拷贝到 PATH 变量定义的路径
cp /root/sh/hello.sh /bin/
# 可以修改PATH 变量的值,而不是把程序脚本复制到/bin/目录中
# 在变量PATH 的后面,加入/root/sh 目录
PATH="$PATH":/root/sh
echo $PATH
# 这样定义的 PATH 变量只是临时生效,一旦重启或注销就会消失,如果想要永久生效,需要写入环境变量配置文件
PS1 变量:命令提示符设置
- \d:显示日期,格式为“星期 月 日”
- \H:显示完整的主机名。如默认主机名“localhost.localdomain”
- \h:显示简写主机名。如默认主机名“localhost”
- \t:显示24 小时制时间,格式为“HH:MM:SS”
- \T:显示12 小时制时间,格式为“HH:MM:SS”
- \A:显示24 小时制时间,格式为“HH:MM”
- @:显示12 小时制时间,格式为“HH:MM am/pm”
- \u:显示当前用户名
- \v:显示Bash 的版本信息
- \w:显示当前所在目录的完整名称
- \W:显示当前所在目录的最后一个目录
- #:执行的第几个命令
- $:提示符。如果是root 用户会显示提示符为“#”,如果是普通用户会显示提示符为
自定义设置(推荐):https://ezprompt.net/
永久性的改变终端命令行格式,需要修改.bashrc
文件。
写入/etc/profile或/etc/bashrc
对全部用户生效;
写入~/.bash_profile或~/.bashrc
只对当前用户生效。
在PS1 变量中,如果是可以解释的符号,如“\u”、“\h”等,则显示这个符号的作用。如果是不能解释的符号,如“@”或“空格”,则原符号输出。
#默认的提示符是显示“[用户名@简写主机名 最后所在目录]提示符”
[root@localhost ~]# echo $PS1
[\u@\h \W]\$
<root@centos8 ~># vim /etc/bashrc
PS1="<\[\e[35m\]\u\[\e[m\]\[\e[31m\]@\[\e[m\]\[\e[32m\]\h\[\e[m\]\[\e[33m\] \W\[\e[m\]>\[\e[36m\]\\$\[\e[m\] "
<root@centos8 ~># echo $PS1
<\[\e[35m\]\u\[\e[m\]\[\e[31m\]@\[\e[m\]\[\e[32m\]\h\[\e[m\]\[\e[33m\] \W\[\e[m\]>\[\e[36m\]\$\[\e[m\]
LANG 语系变量
# Linux 系统的当前默认主语系环境
echo $LANG
zh_CN.UTF-8
# 因为我们 Linux 安装时,选择的是中文安装,所以默认的主语系变量是“zh_CN.UTF-8”
# 查看支持的语言
locale -a | more
locale -a | wc -l
# 当前系统语言
<root@centos8 ~># locale
LANG=zh_CN.UTF-8
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=
LANG zh_CN.UTF-8 中文
LANG en_US.UTF-8 英文
6. 位置参数变量
主要是给函数参数
位置参数变量 | 作 用 |
---|---|
$n | n 为数字,$0 代表命令本身,$1-\(9 代表第一到第九个参数,十以上的参数需要用大括号包含,如\) |
|
$* | 这个变量代表命令行中所有的参数,$* 把所有的参数看成一个整体 |
|
$@ | 这个变量也代表命令行中所有的参数,不过$@ 把每个参数区分对待 |
|
$# |
这个变量代表命令行中所有参数的个数 |
<root@centos8 ~># cat parameter.sh
#!/bin/bash
echo "A total of $# parameters"
#使用$#代表所有参数的个数
echo "The parameters is: $*"
#使用$*代表所有的参数
echo "The parameters is: $@"
#使用$@也代表所有参数
<root@centos8 ~># bash parameter.sh a b c
A total of 3 parameters
The parameters is: a b c
The parameters is: a b c
[root@localhost sh]# vi count.sh
#!/bin/bash
num1=$1
#给num1 变量赋值是第一个参数
num2=$2
#给num2 变量赋值是第二个参数
sum=$(( $num1 + $num2))
#变量sum 的和是num1 加num2
#Shell 当中的运算还是不太一样的
echo $sum
#打印变量sum 的值
<root@centos8 ~># echo $((9 + 2))
11
<root@centos8 ~># echo $((9+2))
11
"$*会把接收的所有参数当成一个整体对待,而$@则会区分对待接收到的所有参数"
[root@localhost sh]# vi parameter2.sh
#!/bin/bash
#定义for 循环,in 后面有几个值,for 会循环多少次,注意“$*”要用双引号括起来
#每次循环会把in 后面的值赋予变量i
#Shell 把$*中的所有参数看成是一个整体,所以这个for 循环只会循环一次
for i in "$*"
do
#打印变量$i 的值 ,也就是打印所有参数
echo "The parameters is: $i"
done
#定义变量x 的值为1
x=1
#同样in 后面的有几个值,for 循环几次,每次都把值赋予变量y
#可是Shell 中把“$@”中的每个参数都看成是独立的,所以“$@”中有几个参数,就会循环几次
for y in "$@"
do
#输出变量y 的值
echo "The parameter$x is: $y"
#然变量x 每次循环都加1,为了输出时看的更清楚
x=$(( $x +1 ))
done
<root@centos8 ~># bash parameter.sh a b c
The parameters is: a b c
The parameter1 is: a
The parameter2 is: b
The parameter3 is: c
7. 预定义变量
预定义变量 | 作 用 |
---|---|
$! |
后台运行的最后一个进程的进程号(PID) |
`$ | 预定义变量 |
---------- | ------------------------------------------------------------ |
| 脚本执行的进程id |
| $?
| 上一条命令执行后的状态,结果为0表示执行正常,结果为1表示执行异常; |
"$? 使用
"所以经常用来检查一个进程是否存在,存在则echo $?返回0;不存在返回1"
<root@centos8 ~># killall -0 sshd
<root@centos8 ~># echo $?
0
<root@centos8 ~># killall -0 nginx
nginx: 未找到进程
<root@centos8 ~># echo $?
1
# kill -0 $(cat /var/run/cron.pid)
# echo $?
0
<root@centos8 ~># kill -0 $(cat /var/run/sshd.pid)
<root@centos8 ~># kill -0 $(cat /var/run/nginx.pid)
cat: /var/run/nginx.pid: 没有那个文件或目录
"$$ 和 $! 使用"
<root@centos8 ~># vi variable.sh
#!/bin/bash
#输出当前进程的PID。
#这个PID 就是variable.sh 这个脚本执行时,生成的进程的PID
echo "The current process is $$"
#使用find 命令在root 目录下查找hello.sh 文件
#符号&的意思是把命令放入后台执行
find /root -name hello.sh &
#输出这个后台执行命令的进程的PID,也就是输出find 命令的PID 号
echo "The last one Daemon process is $!"
<root@centos8 ~># ps
PID TTY TIME CMD
1343 pts/0 00:00:01 bash
351626 pts/0 00:00:00 ps
<root@centos8 ~># echo $$
1314
<root@centos8 ~># w
17:39:55 up 16:21, 2 users, load average: 0.00, 0.01, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root tty1 - 01:19 16:19m 0.08s 0.08s -bash
root pts/0 192.168.19.1 01:20 1.00s 1.23s 0.00s w
pkill killall类似,比较常见的用法是root用户强制踢出已登录的用户
[root@xuexi ~]# w //查看当前所有在线用户
11:08:44 up 1:43, 4 users, load average: 0.14, 0.13, 0.09
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
xf :0 :0 09:26 ?xdm? 4:07 0.68s /usr/libexec/gn
root pts/0 :1 11:08 4.00s 0.11s 0.03s w
root :1 :1 11:01 ?xdm? 4:07 0.55s /usr/libexec/gn
xf pts/1 192.168.128.137 11:08 21.00s 0.08s 0.08s -bash //目标
[root@xuexi ~]# pkill -kill -t pts/1 //发送kill信号给TTY为pts/1的目标
[root@xuexi ~]# w
11:09:07 up 1:43, 3 users, load average: 0.10, 0.13, 0.09
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
xf :0 :0 09:26 ?xdm? 4:10 0.69s /usr/libexec/gn
root pts/0 :1 11:08 3.00s 0.12s 0.03s w
root :1 :1 11:01 ?xdm? 4:10 0.56s /usr/libexec/gn
8. 接收键盘输入的变量
read [选项] [变量名]
选项:
-p "提示信息"
: 在等待read 输入时,输出提示信息
-t 秒数
: read 命令会一直等待用户输入,使用此选项可以指定等待时间
-n 字符数
: read 命令只接受指定的字符数,就会执行
-s
: 隐藏输入的数据,适用于机密信息的输入
变量名:
变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY
如果只提供了一个变量名,则整个输入行赋予该变量
如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字
<root@centos8 ~># cat read.sh
#!/bin/bash
read -t 30 -p "请输入姓名:" name
echo "Name is $name"
read -s -t 30 -p "请输入年龄:" age
echo -e "\n" # 加了-s 要加换行
echo "Age is $age"
read -n 1 -t 30 -p "请选择性别[M/F]: " gender
echo -e "\n"
echo "Sex is $gender"
<root@centos8 ~># bash read.sh
请输入姓名:xiaoming
Name is xiaoming
请输入年龄:
Age is 18
请选择性别[M/F]: M
Sex is M
六、Shell 的运算符
1. 数值运算的方法
使用declare 声明变量类型
所有变量的默认类型是字符串型
,使用declare
命令就可以实现声明变量的类型。命令如下:
declare [+/-][选项] 变量名
选项:
-: 给变量设定类型属性
+: 取消变量的类型属性
-a: 将变量声明为数组型
-i: 将变量声明为整数型(integer)
-r: 将变量声明为只读变量。注意,一旦设置为只读变量,既不能修改变量的值,也不能删除变量,
甚至不能通过+r 取消只读属性
-x: 将变量声明为环境变量
-p: 显示指定变量的被声明的类型
①数值运算
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
#给变量aa 和bb 赋值
[root@localhost ~]# declare -i cc=$aa+$bb
#声明变量cc 的类型是整数型,它的值是aa 和bb 的和
[root@localhost ~]# echo $cc
33
②数组变量类型
所谓数组,就是相同数据类型的元素按一定顺序排列的集合,就是把有限个类型相同的变量用一个名字命名,然后用编号区分他们的变量的集合,这个名字称为数组名,编号称为下标。组成数组的各个变量成为数组的分量,也称为数组的元素,有时也称为下标变量。
变量和数组都是用来保存数据的,只是变量只能赋予一个数据值,一旦重复复制,后一个值就会覆盖前一个值。而数组是可以赋予一组相同类型的数据值。
# 下标从0 开始
<root@centos8 ~># name[0]="shen chao"
<root@centos8 ~># name[1]="li ming"
<root@centos8 ~># name[2]="tong gang"
# 输出数组的内容,如果只写数组名,那么只会输出第一个下标的变量
<root@centos8 ~># echo ${name}
shen chao
# 输出数组所有的内容
<root@centos8 ~># echo ${name[*]}
shen chao li ming tong gang
注意数组的下标是从0
开始的,在调用数组值时,需要使用${数组[下标]}
的方式来读取。
③环境变量
其实也可以使用declare 命令把变量声明为环境变量,和export 命令的作用是一样的:
export 其实就是declare -x实现的
#把变量test 声明为环境变量
declare -x test=123
#打印环境变量
declare -p
④只读属性
注意一旦给变量设定了只读属性,那么这个变量既不能修改变量的值,也不能删除变量,甚至不能使用+r
选项取消只读属性
[root@localhost ~]# declare -r test
#给test 赋予只读属性
[root@localhost ~]# test=456
-bash: test: readonly variable
#test 变量的值就不能修改了
[root@localhost ~]# declare +r test
-bash: declare: test: readonly variable
#也不能取消只读属性
[root@localhost ~]# unset test
-bash: unset: test: cannot unset: readonly variable
#也不能删除变量
⑤使用expr 或let 数值运算工具
要想进行数值运算的第二种方法是使用 expr 命令,这种命令就没有 declare 命令复杂了。
#给变量aa 和变量bb 赋值
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
#dd 的值是aa 和bb 的和。注意“+”号左右两侧必须有空格
[root@localhost ~]# dd=$(expr $aa + $bb)
[root@localhost ~]# echo $dd
33
[root@localhost ~]# echo $(expr $aa + $bb)
33
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
#给变量aa 和变量bb 赋值
[root@localhost ~]# let ee=$aa+$bb
[root@localhost ~]# echo $ee
33
#变量ee 的值是aa 和bb 的和
[root@localhost ~]# n=20
#定义变量n
[root@localhost ~]# let n+=1
#变量n 的值等于变量本身再加1
[root@localhost ~]# echo $n
21
expr
命令和let
命令大家可以按照习惯使用,不过let
命令对格式要求要比expr
命令宽松,所以推荐使用let
命令进行数值运算。
⑥使用“\(((运算式))”或“\)[运算式]”方式运算
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
[root@localhost ~]# ff=$(( $aa+$bb ))
[root@localhost ~]# echo $ff
33
#变量ff 的值是aa 和bb 的和
[root@localhost ~]# gg=$[ $aa+$bb ]
[root@localhost ~]# echo $gg
33
2. Shell 常用运算符
优先级 | 运算符 | 说明 |
---|---|---|
13 | -, + | 单目负、单目正 |
12 | !, ~ | 逻辑非、按位取反或补码 |
11 | * , / , % | 乘、除(取商)、取模(取余) |
10 | +, - | 加、减 |
9 | << , >> | 按位左移、按位右移 |
8 | < =, > =, < , > | 小于或等于、大于或等于、小于、大于 |
7 | == , != | 等于、不等于 |
6 | & | 按位与 |
5 | ^ | 按位异或 |
4 | | | 按位或 |
3 | && | 逻辑与 |
2 | | | | 逻辑或 |
1 | =, +=, -=, *=, /=, %=, &=, | 赋值、运算且赋值 |
<root@centos8 ~># echo $[11 + 22]
33
<root@centos8 ~># echo $[11+22]
33
<root@centos8 ~># echo $[11 + 22]
33
<root@centos8 ~># echo $[ 11*22 ]
242
<root@centos8 ~># echo $[ 22/11 ]
2
<root@centos8 ~># echo $[ 23%11 ]
1
#逻辑与运算只有想与的两边都是1,与的结果才是1,否则与的结果是0
<root@centos8 ~># echo $[ 1&&0 ]
0
<root@centos8 ~># echo $[ 1&&1 ]
1
<root@centos8 ~># echo $[ 0&&0 ]
0
3. 变量的测试与内容置换
变量置换方式 | 变量y 没有设置 | 变量y 为空值 | 变量y 设置值 |
---|---|---|---|
x=${y-新值} | x=新值 | x 为空 | x=$y |
|||
x=\({y:-新值} | x=新值 | x=新值 | x=\)y | |||
x=$ | x 为空 | x=新值 | x=新值 |
x=${y:+新值} |
x 为空 | x 为空 | x=新值 |
x=\({y=新值} | x=新值 <br/>y=新值 <br/> | x 为空 <br/>y 值不变 <br/> | x=\)y y 值不变 |
|||
x=\({y:=新值} | x=新值 <br/>y=新值 <br/> | x=新值 <br/>y=新值 <br/> | x=\)y y 值不变 |
|||
x=\({y?新值} | 新值输出到标准错误输出(就是屏幕) | x 为空 | x=\)y | |||
x=\({y:?新值} | 新值输出到标准错误输出 | 新值输出到标准错误输出 | x=\)y |
如果大括号内没有“:”,则变量y 是为空,还是没有设置,处理方法是不同的;如果大括号内有“:”,则变量y 不论是为空,还是没有没有设置,处理方法是一样的。
如果大括号内是“-”或“+”,则在改变变量x 值的时候,变量y 是不改变的;如果大括号内是“=”,则在改变变量x 值的同时,变量y 的值也会改变。
如果大括号内是“?”,则当变量y 不存在或为空时,会把“新值”当成报错输出到屏幕上。
x=${y-新值}
:如果Y有值(Y为空值也是有值),就 x=y
,如果Y没值,就是这个变量不存在,则用x=new
。这个新值
相当于就是个默认值。相当于if else
,如果y
值存在,就x=y
,负责x=new
#删除变量y
[root@localhost ~]# unset y
#进行测试
[root@localhost ~]# x=${y-new}
#因为变量y 不存在,所以x=new
[root@localhost ~]# echo $x
new
#但是变量y 还是不存在的
[root@localhost ~]# echo $y
# y 值不存在
<root@centos8 ~># unset y
<root@centos8 ~># echo $y
<root@centos8 ~># x=${y-new}
<root@centos8 ~># echo $x
new
# y值存在且不为空值
<root@centos8 ~># y=ynew
<root@centos8 ~># x=${y-new}
<root@centos8 ~># echo $x
ynew
# y值存在且为空值
<root@centos8 ~># y=""
<root@centos8 ~># echo $y
<root@centos8 ~># x=${y-newnull}
<root@centos8 ~># echo $x
为空
七、环境变量配置文件
1. source 命令
[root@localhost ~]# source 配置文件
或
[root@localhost ~]# . 配置文件
2. 环境变量配置文件
1. 登录时生效的环境变量配置文件
在Linux 系统登录时主要生效的环境变量配置文件有以下五个:
/etc/profile
/etc/profile.d/*.sh
~/.bash_profile
~/.bashrc
/etc/bashrc
环境变量配置文件调用过程
- 在用户登录过程先调用
/etc/profile
文件 ,在这个环境变量配置文件中会定义这些默认环境变量:
USER 变量:根据登录的用户,给这个变量赋值(就是让USER 变量的值是当前用户)。
LOGNAME 变量:根据USER 变量的值,给这个变量赋值。
MAIL 变量:根据登录的用户,定义用户的邮箱为/var/spool/mail/用户名。
PATH 变量:根据登录用户的 UID 是否为 0,判断 PATH 变量是否包含/sbin、/usr/sbin
和/usr/local/sbin 这三个系统命令目录。
HOSTNAME 变量:更加主机名,给这个变量赋值。
HISTSIZE 变量:定义历史命令的保存条数。
umask:定义umask 默认权限。注意/etc/profile 文件中的umask 权限是在“有用户登
录过程(也就是输入了用户名和密码)”时才会生效。
调用/etc/profile.d/*.sh 文件,也就是调用/etc/profile.d/目录下所有以.sh 结尾的
文件。
- 由
/etc/profile
文件调用~/.bash_profile
文件 ,~/.bash_profile
这个文件主要实现了两个功能:
- 调用了
~/.bashrc
文件。 - 在
PATH
变量后面加入了:$HOME/bin
这个目录。那也就是说,如果我们在自己的家
目录中建立 bin 目录,然后把自己的脚本放入“~/bin”目录,就可以直接执行脚本,
而不用通过目录执行了
- 由
~/.bash_profile
文件调用~/.bashrc
文件 ,在~/.bashrc
文件中主要实现了:
- 定义默认别名,所以超哥把自己定义的别名也放入了这个文件。
- 调用
/etc/bashrc
- 由
~/.bashrc
调用了/etc/bashrc
文件,在/etc/bashrc
文件中主要定义了这些内容
PS1
变量:也就是用户的提示符umask
:定义umask
默认权限。这个文件中定义的umask
是针对“没有登录过程(也就
是不需要输入用户名和密码时,比如从一个终端切换到另一个终端,或进入子Shell)”
时生效的。如果是“有用户登录过程”,则是/etc/profile
文件中的umask
生效。PATH
变量:会给PATH 变量追加值,当然也是在“没有登录过程”时才生效。- 调用
/etc/profile.d/*.sh
文件,这也是在没有用户登录过程
是才调用。在有用 户登录过程
时,/etc/profile.d/*.sh
文件已经被/etc/profile
文件调用过了。
自定义的环境变量应该放入哪儿?
如果你的修改是打算对所有用户生效的,那么可以放入/etc/profile
环境变量配置文件;
如果你的修改只是给自己使用的,那么可以放入~/.bash_profile
或~/.bashrc
这两个配置文件中的任一个
可是如果我们误删除了这些环境变量,比如删除了/etc/bashrc
文件,或删除了~/.bashrc
文件,
那么这些文件中配置就会失效(~/.bashrc
文件会调用/etc/bashrc
文件)。那么我们的提示符就会
变成:
-bash-4.1#
2. 注销时生效的环境变量配置文件
在用户退出登录时,只会调用一个环境变量配置文件,就是~/.bash_logout
。这个文件默认没有写入任何内容,可是如果我们希望再退出登录时执行一些操作,比如清除历史命令,备份某些数据
,就可以把命令写入这个文件。
3. 其他配置文件
还有一些环节变量配置文件,最常见的就是~/bash_history
文件,也就是历史命令保存文件。
3. Shell 登录信息
-
/etc/issue
我们在登录tty1-tty6 这六个本地终端时,会有几行的欢迎界面。这些欢迎信息是保存在哪里的?可以修改吗?当然可以修改,这些欢迎信息是保存在
/etc/issue
文件中,我们查看下这个文件
<root@centos8 ~># cat /etc/issue
\S
Kernel \r on an \m
可以支持的转义符我们可以通过man agetty 命令查询,在表中我们列出常见的转义符作用:
转义符 | 作 用 |
---|---|
\d | 显示当前系统日期 |
\s | 显示操作系统名称 |
\l | 显示登录的终端号,这个比较常用。 |
\m | 显示硬件体系结构,如i386、i686 等 |
\n | 显示主机名 |
\o | 显示域名 |
\r | 显示内核版本 |
\t | 显示当前系统时间 |
\u | 显示当前登录用户的序列号 |
-
/etc/issue.net
/etc/issue
是在本地终端登录是显示欢迎信息的,如果是远程登录(如ssh 远程登录,或telnet
远程登录)需要显示欢迎信息,则需要配置/etc/issue.net 这个文件了。使用这个文件时由两点需要注意:
首先,在
/etc/issue
文件中支持的转义符,在/etc/issue.net
文件中不能使用。
其次,ssh 远程登录是否显示/etc/issue.net
文件中的欢迎信息,是由ssh 的配置文件决定
的。如果我们需要ssh 远程登录可以查看/etc/issue.net 的欢迎信息,那么首先需要修改ssh 的配置
文件/etc/ssh/sshd_config
,加入如下内容:
[root@localhost ~]# cat /etc/ssh/sshd_config
...省略部分输出...
# no default banner path
#Banner none
Banner /etc/issue.net
...省略部分输出...
这样在ssh 远程登录时,也可以显示欢迎信息,只是不再可以识别“\d”和“\l”等信息了
-
/etc/motd
/etc/motd
文件中也是显示欢迎信息的,这个文件和/etc/issue
及/etc/issue.net
文件的区别是:
/etc/issue
及/etc/issue.net
是在用户登录之前显示欢迎信息,而/etc/motd
是在用户输入用户名和
密码,正确登录之后显示欢迎信息。在/etc/motd
文件中的欢迎信息,不论是本地登录,还是远程登
录都可以显示。
八、shell中 set 指令的用法
详细用法参考:http://www.ruanyifeng.com/blog/2017/11/bash-set.html
1. 语法
set [-可选参数] [-o 选项]
-a:标示已修改的变量,以供输出至环境变量。
-b:使被中止的后台程序立刻回报执行状态。
-C:转向所产生的文件无法覆盖已存在的文件。
-d:Shell预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消。
-e:若指令传回值不等于0,则立即退出shell。
-f:取消使用通配符。 -h:自动记录函数的所在位置。
-H Shell:可利用"!"加<指令编号>的方式来执行history中记录的指令。
-k:指令所给的参数都会被视为此指令的环境变量。
-l:记录for循环的变量名称。
-m:使用监视模式。
-n:只读取指令,而不实际执行。
-p:启动优先顺序模式。
-P:启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。 -t:执行完随后的指令,即退出shell。
-u:当执行时使用到未定义过的变量,则显示错误信息。
-v:显示shell所读取的输入值。
-x:执行指令后,会先显示该指令及所下的参数。
2. 功能说明
set
指令可根据不同的需求来设置当前所使用 shell 的执行方式,同时也可以用来设置或显示 shell 变量的值。当指定某个单一的选项时将设置 shell 的常用特性,如果在选项后使用 -o
参数将打开特殊特性,若是 +o
将关闭相应的特殊特性。而不带任何参数的 set 指令将显示当前 shell 中的全部变量,且总是返回 true,除非遇到非法的选项。
3. 重点参数
最常用的两个参数就是 -e
与 -x
,一般写在 shell 代码逻辑之前,这两个组合在一起用,可以在 debug 的时候替你节省许多时间 。
-
set -x
会在执行每一行 shell 脚本时,把执行的内容输出来。它可以让你看到当前执行的情况,里面涉及的变量也会被替换成实际的值。set -x与set +x指令 用于脚本调试。set是把它下面的命令打印到屏幕 set -x 是开启 set +x 是关闭
-
set -e
会在执行出错时结束程序,就像其他语言中的“抛出异常”一样。(准确说,不是所有出错的时候都会结束程序,见下面的注)注:set -e 结束程序的条件比较复杂,在man bash里面,足足用了一段话描述各种情景。大多数执行都会在出错时退出,除非 shell 命令位于以下情况: 一个 pipeline 的非结尾部分,比如error | ok 一个组合语句的非结尾部分,比如ok && error || other 一连串语句的非结尾部分,比如error; ok 位于判断语句内,包括test、if、while等等。
4. Bash 的错误处理
" Bash 的错误处理
" 如果脚本里面有运行失败的命令(返回值非0),Bash 默认会继续执行后面的命令。
" 如果某个命令失败,往往需要脚本停止执行,防止错误累积。这时,一般采用下面的写法。
# 表示只要command有非零返回值,脚本就会停止执行
command || exit 1
# 如果停止执行之前需要完成多个操作,就要采用下面三种写法。
# 写法一
command || { echo "command failed"; exit 1; }
# 写法二
if ! command; then echo "command failed"; exit 1; fi
# 写法三
command
if [ "$?" -ne 0 ]; then echo "command failed"; exit 1; fi
# 除了停止执行,还有一种情况。如果两个命令有继承关系,只有第一个命令成功了,才能继续执行第二个命令
command1 && command2
""
上面这些写法多少有些麻烦,容易疏忽。set -e
从根本上解决了这个问题,它使得脚本只要发生错误,就终止执行。
#!/usr/bin/env bash
set -e
foo
echo bar
执行结果如下。
$ bash script.sh
script.sh:行4: foo: 未找到命令
可以看到,第4行执行失败以后,脚本就终止执行了。
set -e
根据返回值来判断,一个命令是否运行失败。但是,某些命令的非零返回值可能不表示失败,或者开发者希望在命令失败的情况下,脚本继续执行下去。这时可以暂时关闭set -e
,该命令执行结束后,再重新打开set -e
。
set +e
command1
command2
set -e
上面代码中,set +e
表示关闭-e
选项,set -e
表示重新打开-e
选项。
还有一种方法是使用command || true
,使得该命令即使执行失败,脚本也不会终止执行。
#!/bin/bash
set -e
foo || true
echo bar
上面代码中,true
使得这一行语句总是会执行成功,后面的echo bar
会执行。
-e
还有另一种写法-o errexit
。
set -o errexit
set -e
有一个例外情况,就是不适用于管道命令。
所谓管道命令,就是多个子命令通过管道运算符(|
)组合成为一个大的命令。Bash 会把最后一个子命令的返回值,作为整个命令的返回值。也就是说,只要最后一个子命令不失败,管道命令总是会执行成功,因此它后面命令依然会执行,set -e
就失效了。
#!/bin/bash
set -e
foo | echo a
echo bar
执行结果如下
$ bash script.sh
a
script.sh:行4: foo: 未找到命令
bar
上面代码中,foo
是一个不存在的命令,但是foo | echo a
这个管道命令会执行成功,导致后面的echo bar
会继续执行。
set -o pipefail
用来解决这种情况,只要一个子命令失败,整个管道命令就失败,脚本就会终止执行。
#!/bin/bash
set -eo pipefail
foo | echo a
echo bar
运行后,结果如下。
$ bash script.sh
a
script.sh:行4: foo: 未找到命令
可以看到,echo bar
没有执行。
总结
set
命令的上面这四个参数,一般都放在一起使用。
# 写法一
set -euxo pipefail
# 写法二
set -eux
set -o pipefail
这两种写法建议放在所有 Bash 脚本的头部。
另一种办法是在执行 Bash 脚本的时候,从命令行传入这些参数。
$ bash -euxo pipefail script.sh
九、vim 小技巧
`定位行
vim +/pattern filename:打开文件,并将光标置于第一个与pattern匹配的字符串处
vim +n filename :打开文件,并将光标置于第n行首
在vim 末行模式下
多行注释:
:10,20s/^/#/g
取消连续行注释:
:10,20s/#/^/g
定位第几行
末行模式下: :n
普通模式下: n + shift +g 或 n + gg
(1)快速定位到最后一行: shift + G
(2)快速定位到第一行: 1 + shift +G
(3)快速定位到第x行: 40 + shift + G
`快速格式化代码
gg :到达文件最开始
= :要求缩进
G :直到文件尾
`删除`
ggdG 删除全文
:13,25d 删除13-25行
1,nd(删除1~n行)末行模式下的删除。
5yy 表示拷贝光标以下 5 行.
5dd 表示删除光标以下 5 行.
Alt + . :使用上一条命令的最后一个参数 (常用)
!! :执行上一条命令
!$ :上一条命令的最后一个参数
$? : 上一条命令的执行结果
$#:传入脚本的参数个数;
$0: 脚本自身的名称;
$1: 传入脚本的第一个参数;
$2: 传入脚本的第二个参数;
$@: 传入脚本的所有参数;
$*:传入脚本的所有参数;
$$: 脚本执行的进程id;
$?: 上一条命令执行后的状态,结果为0表示执行正常,结果为1表示执行异常;
`常用快捷键
Ctrl – r :搜索历史命令,随着输入会显示历史命令中的一条匹配命令,Enter键执行匹配命令;ESC键在命令行显示而不执行匹配命令。
Ctrl – g :从历史搜索模式(Ctrl – r)退出。
Ctrl – c :终止命令
Ctrl – z :挂起命令
ctrl+左右键:在单词之间跳转
ctrl+a:跳到本行的行首
ctrl+e:跳到页尾
Ctrl+u:删除当前光标前面的文字 (还有剪切功能)
ctrl+k:删除当前光标后面的文字(还有剪切功能)
Ctrl+l:进行清屏操作
Ctrl+y:粘贴Ctrl+u或ctrl+k剪切的内容 (配置ctrl+k 好用 剪切命令的参数)
Ctrl+w:删除光标前面的单词的字符
" 搜索之后不高亮显示搜索词
set noh
" 语法高亮
syntax on
" 支持使用鼠标
set mouse=a
" 打开
set number 或 set nu
set ai 设置自动缩进
TAB 就是制表符, 单独拿出来做一节是因为这个东西确实很有用.
<< 输入此命令则光标所在行向左移动一个 tab.
>> 输入此命令则光标所在行向右移动一个 tab.
5>> 输入此命令则光标后 5 行向右移动一个 tab.
:12,24> 此命令将12行到14行的数据都向右移动一个 tab.
:12,24>> 此命令将12行到14行的数据都向右移动两个 tab
多文件编辑
在一个 vim 程序中打开很多文件进行编辑是挺方便的.
:sp(:vsp) 文件名 vim 将分割出一个横(纵)向窗口, 并在该窗口中打开新文件.
从 vim6.0 开始, 文件名可以是一个目录的名称, 这样, vim 会
把该目录打开并显示文件列表, 在文件名上按回车则在本窗口打
开该文件, 若输入 O 则在新窗口中打开该文件, 输入 ? 可以看
到帮助信息.
:e 文件名 vim 将在原窗口中打开新的文件, 若旧文件编辑过, 会要求保存.
具体步骤: :sp(:vsp) 文件名 + :set mouse=a + x 或 :wq
5yy 表示拷贝光标以下 5 行.
5dd 表示删除光标以下 5 行.
y2fa 表示拷贝从当前光标到光标后面的第二个a字符之间的内容.
:12,24y 表示拷贝第12行到第24行之间的内容.
:12,y 表示拷贝第12行到光标所在行之间的内容.
:,24y 表示拷贝光标所在行到第24行之间的内容. 删除类似.
yy 表示拷贝光标所在行.
dd 表示删除光标所在行.
D 表示删除从当前光标到光标所在行尾的内容.
'. 此命令相当好使, 它移动光标到上一次的修改行.
`. 此命令相当强大, 它移动光标到上一次的修改点.
d0 表示删除从当前光标到光标所在行首的内容.
d$ 表示删除从当前光标到光标所在行尾的内容.
dG 删除光标所在行下方的所有内容,包括光标所在的行
dgg 删除光标所在行上方的所有内容,包括光标所在的行
命令 | 说明 |
---|---|
i |
在当前光标处进行编辑 |
I |
在行首插入 |
A |
在行末插入 |
a |
在光标后插入编辑 |
o |
在当前行后插入一个新行 |
O |
在当前行前插入一个新行 |
cw |
替换从光标所在位置后到一个单词结尾的字符 |
命令 | 说明 |
---|---|
x |
删除游标所在的字符 |
X |
删除游标所在前一个字符 |
Delete |
同x |
dd |
删除整行 |
dw |
删除一个单词(不适用中文) |
`d | 命令 |
--------- | -------------------------- |
x |
删除游标所在的字符 |
X |
删除游标所在前一个字符 |
Delete |
同x |
dd |
删除整行 |
dw |
删除一个单词(不适用中文) |
或D |
删除至行尾 |
d^ |
删除至行首 |
dG |
删除到文档结尾处 |
d1G |
删至文档首部 |
十、cat 和tee 几个小用法
cat
(英文全拼:concatenate)命令用于连接文件并打印到标准输出设备上。
cat命令用以将文件、标准输入内容打印至标准输出。常用于显示文件内容、创建文件、向文件中追加内容。
# cat >out <<end
> 123
> test
> end
# cat > filename.txt <<EOF
> Hello, World
> Linux!
> EOF
# 清空 /etc/test.txt 文档内容
cat /dev/null > /etc/test.txt
# 清空 /etc/test.txt 文档内容
> /etc/test.txt
# 把文件1内容写入文件3(复制)
cat file1.txt > file3.txt
cat file1.txt >> file3.txt
cat file1 file2 > file 将几个文件合并为一个文件
# 把input文件内容写入out文件(会覆盖原文件)
cat >out <input
# 把input文件内容追加到out文件
cat >>out <input
--- 从标准输入中读入,直到遇到EOF分隔符
$ wc -l << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF
--- 脚本中的应用
#!/bin/bash
cat << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF
tee
读取标准输入,并输出到指定文件,可指定多个备份。在输出的同时,也将其存入一个文件。
tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。
# 把一个命令的输出传递给另一个命令作为输入
kubadm init --config=kubeadm-config.yaml |tee kubadm.log
# tee filename.txt <<EOF
> Hello, World
> Linux!
EOF
> Hello, World
> Linux!
-a 追加到到给定文件,不覆盖原来的内容
# tee -a filename.txt <<EOF
> 1111111
> EOF
#cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF
# 从标准输入读入数据并重定向到/etc/yum.repos.d/kubernetes.repo文件中
# 直接存入文件
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
#和上面一样(会覆盖文件)
#将标准输入内容重定向到filename.txt 文件且会覆盖
cat > filename.txt <<EOF
> Hello, World
> Linux!
> EOF
cat <<EOF > filename.txt
> Hello, World
> Linux!
> EOF
# 将标准输入内容重定向到hosts文件且不会覆盖
cat >> /etc/hosts <<EOF
192.168.0.199 k8svip
192.168.0.200 master01
192.168.0.201 master02
192.168.0.202 master03
192.168.0.210 worker01
EOF
cat <<EOF >> /etc/hosts
192.168.0.199 k8svip
192.168.0.200 master01
192.168.0.201 master02
192.168.0.202 master03
192.168.0.210 worker01
EOF
# 从标准输入读入数据并作为tee命令的标准输入,由于tee命令是读取标准输入显示屏幕并保存文件
# 所有下面的内容会输出到屏幕并保存文件
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF
三、Bash 的基本功能
十一、Shell 输入/输出重定向
一般程序会处理外部的输入,然后将运算结果输出到指定的位置
1. 文件描述符
类型 | 文件描述符 | 默认情况 | 对应文件句柄位置 |
---|---|---|---|
标准输入(standard input) | 0 | 从键盘获得输入 | /proc/self/fd/0 |
标准输出(standard output) | 1 | 输出到屏幕(即控制台) | /proc/self/fd/1 |
错误输出(error output) | 2 | 输出到屏幕(即控制台) | /proc/self/fd/2 |
/dev/null
代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”
2. 输出重定向
命令 | 介绍 |
---|---|
command >filename | 把标准输出重定向到新文件中 |
command 1>filename | 同上 |
command >>filename | 把标准输出追加到文件中 |
command 1>>filename | 同上 |
command 2>filename | 把标准错误重定向到新文件中 |
command 2>>filename | 把标准错误追加到新文件中 |
使用>
或者>>
对输出进行重定向。符号的左边表示文件描述符,如果没有的话表示1
,也就是标准输出,符号的右边可以是一个文件,也可以是一个输出设备。
当使用>
时,会覆盖目标的原有内容。当文件存在时会先删除原文件,再重新创建文件,然后把内容写入该文件;否则直接创建文件。
当使用>>
时,会在目标原有内容后追加内容。当文件存在时直接在文件末尾进行内容追加,不会删除原文件;否则直接创建文件。
# tree
.
└── a.txt
0 directories, 1 file
# ls a.txt b.txt
ls: cannot access bb: No such file or directory --- 错误输出
a.txt ---标准输出
--- 标准输出重定向到了out文件,错误输出到屏幕终端 ---
# ls a.txt b.txt 1>out
ls: cannot access bb: No such file or directory
# cat out
a.txt
# ls a.txt b.txt >>out
ls: cannot access bb: No such file or directory
# cat out
a.txt
a.txt
--- 错误输出重定向到out文件中,标准输出屏幕终端 ---
# ls a.txt b.txt 2>err
a.txt
# cat err
ls: cannot access bb: No such file or directory
--- 错误输出重定向到out文件中,标准输出重定向到err 文件 ---
# ls a.txt b.txt >out 2>err
# cat out
a.txt
# cat err
ls: cannot access bb: No such file or directory
3. 输入重定向
使用<
对输入做重定向,如果符号左边没有写值,那么默认就是0
命令 | 介绍 |
---|---|
command <filename | 以filename文件作为标准输入 |
command 0<filename | 同上 |
command <<delimiter | 从标准输入中读入,直到遇到delimiter分隔符 |
cat(英文全拼:concatenate)命令用于连接文件并打印到标准输出设备上。
cat /dev/null > /etc/test.txt 清空 /etc/test.txt 文档内容
--- 如果cat后面没有跟文件名的话,那它的作用就是将标准输入(比如键盘)回显到标准输出(比如屏幕)上:
# cat
123
123
test
test
--- 标准输入写入到文件中。我们需要使用ctrl+c来结束输入
# cat >out
123
test
^C
# cat out
123
test
--- input文件内容作为标准输入,写入到out文件
# cat input
aaa
111
# cat >out <input
# cat out
aaa
111
---
# cat >out <<end
> 123
> test
> end
# cat out
123
test
--- 从标准输入中读入,直到遇到EOF分隔符
$ wc -l << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF
3 # 输出结果为 3 行
$
--- 脚本中的应用
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com
cat << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF
4. 高级用法
重定向绑定
1. >/dev/null
将标准输出1重定向到/dev/null
中。 /dev/null
代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。
2. 2>&1
这条命令用到了重定向绑定,采用&可以将两个输出绑定在一起。错误输出和标准输出输出到同一个地方。
>/dev/null 2>&1
的作用就是让标准输出重定向到/dev/null
中(丢弃标准输出),然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了/dev/null
中,错误输出同样也被丢弃了。
3. >/dev/null 2>&1
VS 2>&1 >/dev/null
命令 | 标准输出 | 错误输出 |
---|---|---|
>/dev/null 2>&1 | 丢弃 | 丢弃 |
2>&1 >/dev/null | 丢弃 | 屏幕 |
-
>/dev/null 2>&1
即1>/dev/null 2>&1
>/dev/null
,把标准输入重定向到/dev/null
,即丢弃2>&1
,将错误输出绑定到标准输出上。由于此时的标准输出是丢弃,所以错误输出也会丢弃。 -
2>&1 >/dev/null
2>&1
,将错误输出绑定到标准输出上。由于此时的标准输出是默认值,也就是输出到屏幕,所以错误输出会输出到屏幕。
>/dev/null
,将标准输出1重定向到/dev/null
中。
4. >/dev/null 2>&1
VS >/dev/null 2>/dev/null
为什么要用重定向绑定,而不是像>/dev/null 2>/dev/null
这样子重复一遍呢
# ls a.txt b.txt >out 2>out
# cat out
a.txt
无法访问b.txt: 没有那个文件或目录
采用这种写法,标准输出和错误输出会抢占往out文件的管道,所以可能会导致输出内容的时候出现缺失、覆盖等情况。而且,由于out文件被打开了两次,两个文件描述符会抢占性的往文件中输出内容,所以整体IO效率不如>/dev/null 2>&1
来得高。
5. nohup结合
我们经常使用nohup command &
命令形式来启动一些后台程序,比如一些java服务:
# nohup java -jar xxxx.jar &
为了不让一些执行信息输出到前台(控制台),我们还会加上刚才提到的>/dev/null 2>&1
命令来丢弃所有的输出:
# nohup java -jar xxxx.jar >/dev/null 2>&1 &
# nohup /root/runoob.sh > runoob.log 2>&1 &
十二、剔除用户
# 先用w(或者who)查看有谁登陆在服务器上
<root@centos8 ~># ps
PID TTY TIME CMD
1343 pts/0 00:00:01 bash
351626 pts/0 00:00:00 ps
<root@centos8 ~># w
17:39:55 up 16:21, 2 users, load average: 0.00, 0.01, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root tty1 - 01:19 16:19m 0.08s 0.08s -bash
root pts/0 192.168.19.1 01:20 1.00s 1.23s 0.00s w
<root@centos8 ~># echo $$
1343
<root@centos8 ~># who
root tty1 2021-12-02 01:19
root pts/0 2021-12-02 01:20 (192.168.19.1)
方法一:
pkill -kill -t pts/1 踢掉
方法二:
先通过ps -ef | grep pts/6 命令查找对应的进程号,然后在把进程4266杀掉
kill -9 4266
十三、linux后台运行和关闭、查看后台任务
fg、bg、jobs、&、nohup、ctrl+z、ctrl+c 命令
&
加在一个命令的最后,可以把这个命令放到后台执行,如
watch -n 10 sh test.sh & #每10s在后台执行一次test.sh脚本
ctrl + z
可以将一个正在前台执行的命令放到后台,并且处于暂停状态。
jobs
查看当前有多少在后台运行的命令
jobs -l
选项可显示所有任务的PID
,jobs
的状态可以是running, stopped, Terminated
。但是如果任务被终止了(kill),shell
从当前的shell
环境已知的列表中删除任务的进程标识。
fg
将后台中的命令调至前台继续运行。如果后台中有多个命令,可以用fg %jobnumber
(是命令编号,不是进程号)将选中的命令调出。
bg
将一个在后台暂停的命令,变成在后台继续执行。如果后台中有多个命令,可以用bg %jobnumber将选中的命令调出。
kill
后台进程的终止
- 法子1:通过
jobs
命令查看job
号(假设为num),然后执行kill %num
- 法子2:通过
ps
命令查看job
的进程号(PID,假设为pid),然后执行kill pid
nohup
如果让程序始终在后台执行,即使关闭当前的终端也执行(之前的&
做不到),这时候需要nohup
。该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。关闭终端后,在另一个终端jobs
已经无法看到后台跑得程序了,此时利用ps
(进程查看命令)
#a:显示所有程序 u:以用户为主的格式来显示 x:显示所有程序,不以终端机来区分
ps -aux | grep "test.sh"