Loading

Shell基础

Shell基础

一、Shell 分类

常用shbash

/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 

环境变量配置文件调用过程

image-20211202193718767

  1. 在用户登录过程先调用/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 结尾的
文件。
  1. /etc/profile 文件调用~/.bash_profile 文件 ,~/.bash_profile 这个文件主要实现了两个功能:
  • 调用了~/.bashrc 文件。
  • PATH 变量后面加入了:$HOME/bin这个目录。那也就是说,如果我们在自己的家
    目录中建立 bin 目录,然后把自己的脚本放入“~/bin”目录,就可以直接执行脚本,
    而不用通过目录执行了
  1. ~/.bash_profile 文件调用~/.bashrc 文件 ,在~/.bashrc 文件中主要实现了:
  • 定义默认别名,所以超哥把自己定义的别名也放入了这个文件。
  • 调用/etc/bashrc
  1. ~/.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>&11>/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选项可显示所有任务的PIDjobs的状态可以是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"  
posted @ 2021-12-03 00:31  詹丶老头  阅读(144)  评论(0编辑  收藏  举报