上传文件至 /

This commit is contained in:
wxin 2025-03-28 20:09:53 +08:00
commit 94429534f1
5 changed files with 1867 additions and 0 deletions

213
shell-Expect.md Normal file
View File

@ -0,0 +1,213 @@
<h2><center>Shell Expect</center></h2>
------
## 一Expect
### 1. Expect 介绍
通过 Shell 可以实现简单的控制流功能循环、判断等。但是对于需要交互的场合则必须通过人工来干预有时候可能会需要实现和交互程序如telnet服务器等进行交互的功能。而Expect就使用来实现这种功能的工具。
Expect 是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。
Expect 是一个用来实现自动交互功能的软件套件 (Expect [is a] software suite for automating interactive tools)。使用它系统管理员 的可以创建脚本用来实现对命令或程序提供输入而这些命令和程序是期望从终端terminal得到输入一般来说这些输入都需要手工输入进行的。 Expect则可以根据程序的提示模拟标准输入提供给程序需要的输入来实现交互程序执行。甚至可以实现实现简单的BBS聊天机器人。
Expect 需要 Tcl 编程语言的支持,要在系统上运行 Expect 必须首先安装Tcl。
### 2. Expect 工作原理
- Expect 的工作方式象一个通用化脚本工具。用来实现计算机之间需要建立连接时进行特定的登录会话的自动化。
- 脚本由一系列 expect-send 对组成expect 等待输出中输出特定的字符,通常是一个提示符,然后发送特定的响应。解决人机交互的问题。
### 3. Expect 安装
```bash
[root@wxin ~]# yum -y install expect tcl tclx tcl-devel
```
### 4. Expect 命令
Expect 的核心是 spawn、expect、send、set。
- spawn调用要执行的命令
- expect监听交互输出
- send进行交互输入
- set设置变量值
- interact交互完后将控制权交给控制台。
- expect eof与 spawn 对应,表示捕捉终端输出信息终止,类似 if...endif
- settimeout -1设置 expect 永不超时
- settimeout 300如果 300 后没有捕捉到 expect 的监听的内容,那么就退出
### 5. Expect 语法
Expect 使用的是 tcl 语法,语法结构如下
#### 1. 命令之后是参数,相互用空格间隔
```shell
命令 参数 [参数]
```
#### 2. 使用变量
```bash
[root@wxin ~]# set var 1000 # 定义变量<br>set var [lindex $argv 0] #将 var = argv[0]<br>$foo
```
#### 3. 嵌套命令
将一个命令的输出,作为另一个命令的输入参数
```shell
命令 [命令 参数]
```
#### 4. 双引号
将词组标记为一个参数,双引号内`$`符号有效
```shell
命令 "hello world $foo"
```
#### 5. 大括号
将词组标记为一个参数,但大括号内无法扩展变量
```shell
命令 {hello world}
```
#### 6. 反斜线,转义
### 6.示例解决`ssh`登录验证免交互
#### 1.命令选项说明
- spawn expect 内部命令启动一个shell程序。
- expect 期望哪些内容
- yes/no 就send发送 yes \r 表示回车
- password 就 send 发送 centos
- exp_continue跳过循环就继续下一条语句。
- interact 允许用户交互
#### 2. Expect 实现免交互公钥推送
**安装和生成公钥**
```shell
[root@client ~]# vim key.sh
#!/bin/bash
# 创建一个IP地址文件。
>ip.txt
# 检测expect是否安装检测公钥是否创建。
rpm -q expect &> /dev/null
if [ $? -ne 0 ]; then
yum install -y expect tcl tclx tcl-devel
fi
if [ ! -f ~/.ssh/id_rsa ]; then
ssh-keygen -P "" -f ~/.ssh/id_rsa
fi
#使用for循环ping测试主机是否在线。之前插入安装和准备秘钥。
[root@client ~]# bash key.sh
Generating public/private rsa key pair.
Created directory '/root/.ssh'.
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:SjHCxgoBPmduW2ia/FC/gYorRqsZG5JUui4F5IB7EK4 root@client
The key's randomart image is:
+---[RSA 2048]----+
|*. |
|*o o |
|=* += o |
|+oOo.. o |
|E=.* .. S |
|ooO =. . |
|*B.o o. |
|*B+ o |
|@+ . . |
+----[SHA256]-----+
```
注意!!!:缩进绝对不能用空格,必须回车
**通过`shell`循环判断在线主机**
```bash
[root@client ~]# vim address.sh
#!/bin/bash
# 创建一个IP地址文件
>ip2.txt
# 使用for循环ping测试主机是否在线。
for i in {2..254}; do
{
ip=192.168.159.$i
ping -c1 -W1 $ip &> /dev/null
if [ $? -eq 0 ]; then
echo "$ip" >> ip2.txt
fi
}&
done
[root@client ~]# bash address.sh
[root@client ~]# cat ip2.txt
192.168.159.2
192.168.159.131
192.168.159.130
```
**通过`expect`进行交互**
使用`expect`解释器
```bash
[root@client ~]# expect <<-eof
> spawn ssh-copy-id 192.168.159.130
> expect {
> "yes/no" { send "yes\n"; exp_continue }
> "password" { send "123456\n" }
> }
> expect eof
> eof
```
使用`shell`脚本
```bash
#!/bin/bash
# 创建一个IP地址文件
>ip.txt
# 检查公钥是否存在
if [ ! -f /root/.ssh/id_rsa.pub ]; then
echo "SSH公钥不存在正在生成..."
ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa
fi
# 使用for循环ping测试主机是否在线。
for i in {2..254}; do
{
ip=192.168.159.$i
ping -c1 -W1 $ip &> /dev/null
if [ $? -eq 0 ]; then
echo "$ip" >> ip.txt
/usr/bin/expect <<-EOF
set timeout 10
spawn ssh-copy-id $ip
expect {
"yes/no" { send "yes\n"; exp_continue }
"password:" { send "123456\n" }
}
expect eof
EOF
fi
}&
done
wait
echo "finish..."
```

286
shell-函数.md Normal file
View File

@ -0,0 +1,286 @@
<h2><center>Shell 函数</center></h2>
------
## 一:函数
### 1. 介绍
Shell 函数的本质是一段可以重复使用的脚本代码,这段代码被提前编写好了,放在了指定的位置,使用时直接调取即可。
### 2. 定义函数
可以带function fun() 定义也可以直接fun() 定义,不带任何参数。
```shell
function name() {
# 函数体
commands
}
```
- function 是 Shell 中的关键字,专门用来定义函数;
- name 是函数名;
- commands 是函数要执行的代码,也就是一组语句;
- return value 表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。
- 由 { } 包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。
- 函数的优势
1. 方便n次使用减少代码量使之方便整洁。
2. 当需要修改里面的重复代码时,只需要修改一次函数即可实现需求;
3. 将函数写进文件,需要时直接通过文件调用
### 3. 调用函数
**执行不带参数的函数**
直接输入函数名即可,不需要带括号
```shell
functionName
```
- 执行函数时,函数名前的关键字`function`和函数名后面的`()`均不需要带
- 函数的定义必须要在执行的程序前定义或加载
**执行带参数的函数**
```shell
greet() {
echo "Hello, $1! You have $# arguments."
}
greet "Alice" 42 # 输出Hello, Alice! You have 2 arguments.
```
- 向函数传递参数时,参数通过位置变量 `$1`, `$2`, `$3`... 接收。
- `$@` 表示所有参数,`$#` 表示参数个数。
**调用函数示例**
```bash
[root@wxin ~]# vim testfunction.sh
#!/bin/bash
# first function
function HelloWorld() {
echo "Hello world"
}
# second function
Welcome() {
echo "Welcome to qfedu"
}
# third function
function HelloShell {
echo "Hello Shell"
}
# file functions
HelloWorld # 调用函数
Welcome
HelloShell
[root@wxin ~]# bash testfunction.sh
Hello world
Welcome to qfedu
Hello Shell
```
**从文件中调用函数示例**
```bash
[root@wxin ~]# vim filefunction.sh
function Sum () {
for((i=1;i<=100;i++))
do
((sum=sum+i))
done
echo '{1..100} sum is :' $sum
}
[root@wxin ~]# vim filefunctionfromfile.sh
#!/bin/bash
path="/root/Test/filefunction.sh"
if [ -f ${path} ]
then
source $path # 加载函数
Sum # 调用函数
else
echo "file not exist or error"
fi
[root@wxin ~]# bash filefunctionfromfile.sh
{1..100} sum is : 5050
```
**函数参数传递示例**
```bash
[root@wxin ~]# vim functionwithargs.sh
#!/bin/bash
function Add () { # 定义函数
((sum=$1+$2))
echo "$1 + $2 sum is" ${sum}
}
Add $1 $2 # 调用函数并传递参数
[root@wxin ~]# bash functionwithargs.sh 100 150
100 + 150 sum is 250
[root@wxin ~]# bash functionwithargs.sh 509 150
509 + 150 sum is 659
```
### 4. 返回值
- Shell 函数通过 `return` 返回**整数状态码**0 表示成功,非 0 表示失败)。
- 若要返回字符串或复杂数据,可使用 `echo` 输出到标准输出,再通过命令替换捕获。
```bash
add() {
local sum=$(( $1 + $2 ))
echo $sum # 输出结果到标准输出
}
result=$(add 3 5) # 捕获返回值
echo "Sum is $result" # 输出Sum is 8
```
### 5. 变量作用域
- 默认情况下,函数内变量是**全局的**(会影响到脚本其他部分)。
- 使用 `local` 关键字定义**局部变量**(仅在函数内有效)。
```bash
test_scope() {
local var_local="I'm local"
var_global="I'm global"
}
test_scope
echo $var_global # 输出I'm global
echo $var_local # 输出为空(局部变量不可见)
```
### 6. 函数案例
```bash
#!/bin/bash
list()
{
echo "+++++++++++++++++++++++++++++++++"
echo "+++++++ 百宝箱 ++++++++"
echo "+++++++++++++++++++++++++++++++++"
echo "|||||||||||||||||||||||||||||||||"
echo "================================="
echo "= 1.yum仓库初始化 ="
echo "= 2.上课笔记工具安装 ="
echo "= 3.kvm虚拟机安装 ="
echo "= 4.vmware虚拟机安装 ="
echo "= 5.vs code安装 ="
echo "= 6.google浏览器安装 ="
echo "= 7.vnc-server的安装 ="
echo "= 8.一键安装所有 ="
echo "= 9.退出 ="
echo "================================="
}
yum-install(){
echo "====正在执行yum初始化操作请耐心等待===="
rm -rf /etc/yum.repos.d/*
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo > /dev/null
yum -y install wget > /dev/null
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
echo "====正在清空缓存,请耐心等待!===="
yum clean all
echo "====正在重新加载,请耐心等待!===="
yum makecache
echo "====successed===="
}
cherrytree(){
echo "====正在进行安装......====="
yum -y install cherrytree > /dev/null
if [ $? -eq 0 ];then
echo "====successed===="
else
echo "==== failed ===="
echo "====检查网络和yum仓库===="
exit
fi
}
kvm-install(){
echo "====正在安装kvm虚拟机===="
yum -y install libvirt* virt-manager >> /dev/null && yum -y groupinstall 'Virtualization Host' >> /dev/null
echo "==== successed ===="
}
vmware-install(){
echo "====请将vmware安装包放到当前目录下===="
chmod +x
echo "......."
echo "..........."
echo ".................100%"
}
data=`date | awk '{print $4}'`
read -p "当前时间为${data},你是否要进行电脑初始化,继续请按回车(已记录你的初始时间)"
ping -c1 www.baidu.com 1> /dev/null
if [ $? -eq 0 ];then
echo "网络状况良好,请继续~"
else
echo "网络状况不佳,检查网络~"
exit
fi
echo "xingdian" > user.txt
echo "dianye" > password.txt
read -p "欢迎使用行癫工具箱,进行安装部署操作:"
read -p "请输入用户名:" name
username=`cat user.txt | awk '{print $1}'`
passwd=`cat password.txt | awk '{print $1}'`
if [ "${name}" == "${username}" ];then
read -p "请输入密码:" password
if [ "${password}" == "${passwd}" ];then
echo "登陆成功,进入工具箱"
while :
do
list
read -p "请选择你要使用的工具代码:" num
case $num in
1)
yum-install
sleep 3
;;
2)
cherrytree
sleep 3
;;
3)
;;
4)
;;
9)
break
;;
esac
done
data2=`date | awk '{print $4}'`
echo "结束时间为${data2};感谢您的使用!"
else
echo "用户名密码错误,请重新执行脚本!"
exit
fi
else
echo "用户名输入错误,请重新输入!"
exit
fis
```

422
shell-变量.md Normal file
View File

@ -0,0 +1,422 @@
<h2><center>Shell 变量</center></h2>
------
## 一:概述
### 1. 介绍
变量可以通过变量名访问,在指令式语言中,变量通常是可变的;在某些条件下也是不可变的。
### 2. 变量规则
- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头
- 中间不能有空格,可以使用下划线
- 不能使用标点符号
- 不能使用bash里的关键字
## 二:变量分类
### 1. 自定义变量
- 定义变量:`变量名=变量值`
- 引用变量:`$变量名``${变量名}`
- 查看变量:`echo $变量名`
- 取消变量:`unset 变量名`
- 作用范围仅在当前shell中有效
### 2. 环境变量
- 定义环境变量:
1. `export back_dir2=/home/backup`
2. `export back_dir1` 将自定义变量转换成环境变量
- 引用环境变量:`$ 变量名``${变量名}`
- 查看环境变量:`echo $变量名`
- 取消环境变量:`unset 变量名`
- 变量作用范围在当前shell和子shell有效
注意:
- 环境变量拥有可继承性:`export`之后就拥有继承性。
- 永久生效:写到环境变量脚本,`/etc/profile` `~/.baserc` `~/.bash_profile` `/etc/bashrc`
### 3. 位置变量
```shell
$1 $2 $3 $4 $5 $6 $7 $8 $9 $(10)
```
**案例:**
```bash
[root@wxin ~]# vim test.sh
#!/bin/bash
echo "$1"
[root@wxin ~]# bash test.sh 123
123
```
### 4. 预定义变量
```shell
$0 # 脚本名
$* # 所有的参数
$@ # 所有的参数
$# # 参数的个数
$$ # 当前进程的PID
$! # 上一个后台进程的PID
$? # 上一个命令的返回值 0表示成功
```
**案例:**
```bash
[root@wxin ~]# vim test.sh
echo "第2个位置参数是$2"
echo "第1个位置参数是$1"
echo "第4个位置参数是$4"
echo "所有参数是: $*"
echo "所有参数是: $@"
echo "参数的个数是: $#"
echo "当前进程的PID是: $$"
echo '$1='$1
echo '$2='$2
echo '$3='$3
echo '$*='$*
echo '$@='$@
echo '$#='$#
echo '$$='$$
```
**$*和$@区别**
- 当$*和$@没有被引用的时候,它们确实没有什么区别,都会把位置参数当成一个个体。最好不要使用,因为如果位置参数中带有空格或者通配符的情况下,可能结果会和想要的不一样
- "$\*" 会把所有位置参数当成一个整体(或者说当成一个字符串),如果没有位置参数,则"$\*"为空如果有两个位置参数并且IFS为空格时"$*"相当于"$1 $2"
- "$@" 会把所有位置参数当成一个单独的字段,如果没有位置参数($#为0),则"$@"展开为空(不是空字符串,而是空列表),如果存在一个位置参数,则"$@"相当于"$1",如果有两个参数,则"$@"相当于"$1" "$2"等等
```bash
[root@wxin ~]# vim a.sh
#!/bin/bash
for i in $@
do
echo $i
done
for i in $*
do
echo $i
done
for i in "$@"
do
echo $i
done
for i in "$*"
do
echo $i
done
```
## 三:变量赋值
### 1. 显示赋值
变量名=变量值
**示例:**
```bash
[root@wxin ~]# ip1=192.168.1.251
[root@wxin ~]# school="BeiJing 1000phone"
[root@wxin ~]# today1=`date +%F`
[root@wxin ~]# today2=$(date +%F)
```
### 2. 键盘输入
**语法格式:**
```shell
read 变量名
read -p "提示信息:" 变量名
read -t 5 -p "提示信息:" 变量名 # -t 后面跟秒数,定义输入字符的等待时间
read -n 2 变量名 # -n 后跟一个数字,定义输入文本的长度,很实用
read -s -p "" 变量名 # -s 是隐藏用户输入
read [-ers][-a 数组][-d 分隔符][-i 缓冲区文字][-n 读取字符数][-N 读取字符数][-p 提示符][-t 超时][-u 文件描述符][名称...]
```
**示例:**
```bash
[root@wxin ~]# vim first.sh
back_dir1=/var/backup
read -p "请输入你的备份目录: " back_dir2
echo $back_dir1
echo $back_dir2
[root@wxin ~]# sh first.sh
[root@wxin ~]# vim ping2.sh
#!/bin/bash
read -p "Input IP: " ip
ping -c2 $ip &>/dev/null
if [ $? = 0 ];then
echo "host $ip is ok"
else
echo "host $ip is fail"
fi
[root@wxin ~]# chmod a+x ping2.sh
[root@wxin ~]# ./ping.sh
```
### 4. 引用变量
`""`:弱引用 可以实现变量和命令的替换
`''`:强引用 不完成变量替换
反引:命令替换 等价于 $() 反引号中的shell命令会被先执行
**示例:**
```bash
[root@wxin ~]# school=1000phone
# 强引用
[root@wxin ~]# echo "${school} is good"
1000phone is good
# 弱引用
[root@wxin ~]# echo '${school} is good'
${school} is good
# 反引
[root@wxin ~]# touch `date +%F`_file1.txt
[root@wxin ~]# touch $(date +%F)_file2.txt
[root@wxin ~]# disk_free3="df -Ph |grep '/$' |awk '{print $4}'" # 错误
[root@wxin ~]# disk_free4=$(df -Ph |grep '/$' |awk '{print $4}')
[root@wxin ~]# disk_free5=`df -Ph |grep '/$' |awk '{print $4}'`
```
## 四:变量运算
### 1. 整数运算
方法一:`expr`
```bash
[root@wxin ~]# expr 1 + 2
[root@wxin ~]# expr $num1 + $num2 + - \* / %
```
方法二:`$(())`
```bash
[root@wxin ~]# echo $(($num1+$num2)) + - * / %
[root@wxin ~]# echo $((num1+num2))
[root@wxin ~]# echo $((5-3*2))
[root@wxin ~]# echo $(((5-3)*2))
[root@wxin ~]# echo $((2**3))
[root@wxin ~]# sum=$((1+2)); echo $sum
```
方法三:`$[]`
```bash
[root@wxin ~]# echo $[5+2] + - * / %
[root@wxin ~]# echo $[5**2]
```
方法四:`let`
```bash
[root@wxin ~]# let sum=2+3; echo $sum
[root@wxin ~]# let i++; echo $i
```
### 2. 小数运算
使用bc做小数运算scale指定小数点位数
**加法运算(scale参数无效)**
```bash
[root@wxin ~]# echo "5.999 + 5.001"|bc
6.000
[root@wxin ~]# echo "5.111+ 5.1114"|bc
10.2224
```
**减法运算(scale参数无效)**
```bash
[root@wxin ~]# echo "2.22 - 1.11"|bc
1.11
```
**乘法运算**
```bash
[root@wxin ~]# echo "5.12 * 5.6000"|bc
28.6720
```
注意:
乘积小数点位数默认以乘数中小数点位数最多的为准不指定scale参数
**除法运算**
```bash
[root@wxin ~]# echo "scale=2;9.898 / 1.11"|bc
8.91
[root@wxin ~]# echo "9.898 / 1.11"|bc
8
```
## 五:变量截取
### 1. 匹配截取
```bash
[root@wxin ~]# url=www.sina.com.cn
[root@wxin ~]# echo ${#url} # 获取变量值的长度
15
[root@wxin ~]# echo ${url} # 标准查看
www.sina.com.cn
[root@wxin ~]# echo ${url#*.} # 从前往后,最短匹配
sina.com.cn
[root@wxin ~]# echo ${url##*.} # 从前往后,最长匹配 贪婪匹配
cn
[root@wxin ~]# url=www.sina.com.cn
[root@wxin ~]# echo ${url}
www.sina.com.cn
[root@wxin ~]# echo ${url%.*} # 从后往前,最短匹配
www.sina.com
[root@wxin ~]# echo ${url%%.*} # 从后往前,最长匹配 贪婪匹配
www
[root@wxin ~]# url=www.sina.com.cn
[root@wxin ~]# echo ${url#a.}
www.sina.com.cn
[root@wxin ~]# echo ${url#*sina.}
com.cn
[root@wxin ~]# echo $HOSTNAME
wxin.1000phone.com
[root@wxin ~]# echo ${HOSTNAME%%.*}
wxin
```
### 2. 索引切片
```bash
[root@wxin ~]# echo ${url:0:5}
[root@wxin ~]# echo ${url:5:5}
[root@wxin ~]# echo ${url:5}
```
### 3. 字符替换
```bash
[root@wxin ~]# url=www.sina.com.cn
[root@wxin ~]# echo ${url/sina/baidu}
www.baidu.com.cn
[root@wxin ~]# url=www.sina.com.cn
[root@wxin ~]# echo ${url/n/N}
www.siNa.com.cn
[root@wxin ~]# echo ${url//n/N} #贪婪匹配
www.siNa.com.cN
```
### 4. 自增运算
对变量的值的影响
```bash
[root@wxin ~]# i=1
[root@wxin ~]# let i++
[root@wxin ~]# echo $i
2
[root@wxin ~]# j=1
[root@wxin ~]# let ++j
[root@wxin ~]# echo $j
2
```
对表达式的值的影响
```bash
[root@wxin ~]# unset i
[root@wxin ~]# unset j
[root@wxin ~]# i=1
[root@wxin ~]# j=1
[root@wxin ~]# let x=i++ 先赋值,再运算
[root@wxin ~]# let y=++j 先运算,再赋值
[root@wxin ~]# echo $i
2
[root@wxin ~]# echo $j
2
[root@wxin ~]# echo $x
1
[root@wxin ~]# echo $y
2
```
### 5. `${变量名-新变量值}`
```bash
[root@wxin ~]# unset var1
[root@wxin ~]# echo ${var1}
[root@wxin ~]# echo ${var1-aaaaa}
aaaaa
[root@wxin ~]# var2=111
[root@wxin ~]# echo ${var2-bbbbb}
111
[root@wxin ~]# var3=
[root@wxin ~]# echo ${var3-ccccc}
```
变量没有被赋值:会使用"新的变量值"替代
变量有被赋值(包括空值): 不会被替代
```bash
[root@wxin ~]# unset var1
[root@wxin ~]# unset var2
[root@wxin ~]# unset var3
[root@wxin ~]# var2=
[root@wxin ~]# var3=111
[root@wxin ~]# echo ${var1:-aaaa}
aaaa
[root@wxin ~]# echo ${var2:-aaaa}
aaaa
[root@wxin ~]# echo ${var3:-aaaa}
111
```
### 6. `${变量名:-新变量值}`
变量没有被赋值(包括空值):都会使用“新的变量值“ 替代
变量有被赋值: 不会被替代
```bash
[root@wxin ~]# echo ${var3+aaaa}
[root@wxin ~]# echo ${var3:+aaaa}
[root@wxin ~]# echo ${var3=aaaa}
[root@wxin ~]# echo ${var3:=aaaa}
[root@wxin ~]# echo ${var3?aaaa}
[root@wxin ~]# echo ${var3:?aaaa}
```

242
shell-并发控制.md Normal file
View File

@ -0,0 +1,242 @@
<h2><center>Shell 并发控制</center></h2>
------
## 一:文件描述符
FD文件描述符 / 文件句柄,进程使用文件描述符来管理进程打开的文件。
### 1. 查看当前进程打开的文件
```bash
[root@wxin ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 3月 26 17:21 0 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 1 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 2 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 19:49 255 -> /dev/pts/0
```
### 2. 自定义当前进程用描述符号操作文件
手动定义文件描述符,只要没有被占用可以使用
自定义用当前进程打开一个文件
```bash
[root@wxin ~]# exec 6<> /file
[root@wxin ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 3月 26 17:21 0 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 1 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 2 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 19:49 255 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 6 -> /file
```
自定义用当前进程关闭一个文件
```bash
[root@wxin ~]# exec 6<&-
[root@wxin ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 3月 26 17:21 0 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 1 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 2 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 19:49 255 -> /dev/pts/0
```
### 3. 给文件写入内容
文件描述符fd方式
```bash
[root@wxin ~]# echo "wxin" >> /proc/$$/fd/6
[root@wxin ~]# cat /file
wxin
```
文件名方式
```bash
[root@wxin ~]# echo "wxin" >> /file
```
### 4. 文件描述符恢复文件
删除源文件
```bash
[root@wxin ~]# rm -rf /file
```
新文件产生新的文件 inode
```shell
[root@wxin ~]# cp /proc/$$/fd/6 /file
```
查看状态为deleted
```bash
[root@wxin ~]# ll /proc/$$/fd
总用量 0
lrwx------. 1 root root 64 3月 26 17:21 0 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 1 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 2 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 19:49 255 -> /dev/pts/0
lrwx------. 1 root root 64 3月 26 17:21 6 -> /file (deleted)
```
释放文件描述符,重新读取就显示正常了
### 二:管道
### 1. 匿名管道
```bash
[root@wxin ~]# rpm -qa |grep rpm
```
### 2. 命令管道
创建命名管道文件(不是普通文件,管道内的文件只能读取一次,不会永久保存)
```bash
[root@wxin ~]# mkfifo /tmp/fifo1 f
```
查看命名管道文件
```bash
[root@wxin ~]# cat /tmp/fifo1
[root@wxin ~]# file /tmp/fifo1
/tmp/fifo1: fifo (named pipe)
```
## 三:并发控制
### 1. 基础并发:后台任务与`wait`
通过 `&` 将命令放到后台执行,再通过 `wait` 等待所有任务完成。
```shell
#!/bin/bash
# 启动多个后台任务
wget -q http://example.com/file1.jpg &
wget -q http://example.com/file2.jpg &
wget -q http://example.com/file3.jpg &
# 等待所有后台任务完成
wait
echo "All downloads completed!"
```
### 2. 控制并发数量
同时运行过多任务可能导致资源耗尽。以下方法可限制并发数量:
**方法 1循环计数 + 等待**
```shell
#!/bin/bash
max_workers=3 # 最大并发数
task_list=(task1 task2 task3 task4 task5) # 任务列表
for task in "${task_list[@]}"; do
(
# 执行任务(此处替换为实际逻辑)
echo "Processing $task..."
sleep 1
) &
# 控制并发数量
if [[ $(jobs -r | wc -l) -ge $max_workers ]]; then
wait -n # 等待任意一个后台任务完成(需要 bash 4.3+
fi
done
wait # 等待剩余任务完成
echo "All tasks done!"
```
**方法 2使用 `xargs``-P` 参数**
```shell
# 使用 xargs 控制并行进程数
echo task1 task2 task3 task4 task5 | xargs -n1 -P3 -I{} bash -c 'echo "Processing {}"; sleep 1'
```
**方法 3命名管道FIFO**
```shell
#!/bin/bash
max_workers=3
task_list=(task1 task2 task3 task4 task5)
# 创建命名管道
fifo=$(mktemp -u)
mkfifo "$fifo"
exec 3<>"$fifo"
rm -f "$fifo"
# 初始化管道令牌
for ((i=0; i<max_workers; i++)); do
echo >&3
done
# 处理任务
for task in "${task_list[@]}"; do
read -u3 # 获取令牌
{
echo "Processing $task..."
sleep 1
echo >&3 # 归还令牌
} &
done
wait
exec 3>&-
echo "All tasks done!"
```
### 3. 示例
```shell
#!/bin/bash
max_workers=3
urls=(
"http://example.com/file1.jpg"
"http://example.com/file2.jpg"
"http://example.com/file3.jpg"
"http://example.com/file4.jpg"
)
download() {
local url=$1
local filename=$(basename "$url")
if wget -q "$url" -O "$filename"; then
echo "Downloaded: $filename"
else
echo "Failed: $filename" >&2
return 1
fi
}
# 控制并发下载
counter=0
for url in "${urls[@]}"; do
((counter++))
( download "$url" ) &
if [[ $((counter % max_workers)) -eq 0 ]]; then
wait # 每启动 max_workers 个任务后等待
fi
done
wait
echo "All downloads processed!"
```

704
shell.md Normal file
View File

@ -0,0 +1,704 @@
<h2><center>Shell</center></h2>
------
## 一Shell 概述
### 1. shell 定义
Shell 中文含义是“壳”它是相对于内核来说的因为它是建立在内核的基础上面向用户的一种表现形式比如我们看到一个球见到的是它的壳而非核。Linux中的Shell是指一个面向用户的命令接口表现形式就是一个可以由用户录入的界面这个界面也可以反馈运行信息。
Shell 是命令解释器,用于与操作系统交互,用来完成各种任务,如文件操作、系统管理、网络管理等。
Shell 是一种程序设计语言他有变量有自己的语法结构等shell程序设计语言可以编写功能很强、代码简短的程序
### 2. Shell 在 Linux 中的存在形式
由于 Linux 不同于 WindowsLinux 是内核与界面分离的,它可以脱离图形界面而单独运行,同样也可以在内核的基础上运行图形化的桌面。 这样在Linux 系统中,就出现了两种 Shell 表现形式,一种是在无图形界面下的终端运行环境下的 Shell另一种是桌面上运行的类似 Windows 的 MS-DOS 运行窗口。
### 3. Shell 如何执行用户的指令
Shell 有两种执行指令的方式
- 第一种方法是用户事先编写一个 sh 脚本文件,内含 Shell 脚本,而后使用 Shell 程序执行该脚本,这种方式,我们习惯称为 Shell 编程。
- 第二种形式则是用户直接在Shell界面上执行 Shell 命令,由于 Shell 界面的关系,大家都习惯一行行的书写,很少写出成套的程序来一起执行,所以也称命令行。
**总结:**
Shell 只是为用户与机器之间搭建成的一个桥梁让我们能够通过Shell来对计算机进行操作和交互从而达到让计算机为我们服务的目的。
### 4. Shell 分类
Linux中默认Shell是bash流行的Shell有ash、bash、ksh、csh、zsh等不同的Shell都有自己的特点以及用途。
**Bash**
大多数Linux系统默认使用的Shellbash Shell是Bourne Shell 的一个免费版本它是最早的Unix Shell,bash还有一个特点可以通过help命令来查看帮助。包含的功能几乎可以涵盖Shell所具有的功能所以一般的Shell脚本都会指定它为执行路径。
**Csh**
C Shell 使用的是“类C”语法csh 是具有C语言风格的一种 Shell其内部命令有52个较为庞大。**目前使用的并不多,已经被`/bin/tcsh`所取代**。
**Ksh**
Korn Shell 的语法与 Bourne Shell 相同,同时具备了 C Shell 的易用特点。许多安装脚本都使用 kshksh 有42条内部命令**与 bash 相比有一定的限制性**。
**TCsh**
tcsh 是 csh 的增强版与C Shell完全兼容。
**Sh**
是一个快捷方式,已经被`/bin/bash`所取代。
**Nologin**
指用户不能登录
**Zsh**
前 Linux 里最庞大的一种 zsh。它有84个内部命令使用起来也比较复杂。一般情况下不会使用该Shell。
### 5. Shell 使用场景
- 自动化批量系统初始化程序 update软件安装时区设置安全策略...
- 自动化批量软件部署程序 LAMPLNMPTomcatLVSNginx
- 应用管理程序 (KVM集群管理扩容MySQLDELLR720批量RAID
- 日志分析处理程序PV, UV, 200, !200, top 100, grep/awk
- 自动化备份恢复程序MySQL完全备份/增量 + Crond
- 自动化管理程序(批量远程修改密码,软件升级,配置更新)
- 自动化信息采集及监控程序(收集系统/应 用状态信息CPU,Mem,Disk,Net,TCP Status,Apache,MySQL
- 配合Zabbix信息采集收集系统/应用状态信息CPU,Mem,Disk,Net,TCP Status,Apache,MySQL
- 自动化扩容(增加云主机-->业务上线)
- Zabbix监控CPU 80%+|-50% Python API AWS/EC2增加/删除云主机) + Shell Script业务上线
- 俄罗斯方块,打印三角形,打印圣诞树,打印五角星,运行小火车,坦克大战,排序算法实现
- “Shell可以做任何事”一切取决于业务需求
### 6. Bash 初始化
**Bash 环境变量文件的加载**
```shell
/etc/profile 使用范围:所有账户
/etc/bashrc 使用范围:所有账户
~/.bashrc 使用范围:~所代表的账户
~/.bash_profile 使用范围:~所代表的账户
~/.bash_logout
login shell
登录的时候需要输入用户名
nologin shell
登录的时候不需要输入用户
```
1. **`/etc/profile`**:全局配置,每个用户登录时都会读取该文件。
2. **`/etc/bashrc`**全局配置Ubuntu 没有此文件,与之对应的是 `/ect/bash.bashrc`bash 执行时,不管是何种方式,都会读取此文件。
3. **`~/.profile`**:若`bash`是以`login`方式执行,读取`~/.bash_profile`,若它不存在,则读取`~/.bash_login`,若前两者不存在,读取`~/.profile`。图形模式登录时,此文件将被读取,即使存在`~/.bash_profile``~/.bash_login`
4. **`~/.bash_login`**:若`bash`是以`login`方式执行时,读取`~/.bash_profile`,若它不存在,则读取`~/.bash_login`,若前两者不存在,读取`~/.profile`
5. **`~/.bash_profile`**Unbutu 默认没有此文件,可新建。只有`bash`是以`login`形式执行时,才会读取此文件。通常该配置文件还会配置成去读取 `~/.bashrc`
6. **`~/.bashrc`**:当 bash 是以`non-login`形式执行时,读取此文件。若是以`login`形式执行,则不会读取此文件。
7. **`~/.bash_logout`**:当 bash 是以`non-login`形式执行时,读取此文件。若是以`login`形式执行,则不会读取此文件。
**Bash 环境变量加载**
- 图形模式登录时,顺序读取:/etc/profile 和 ~/.profile
- 图形模式登录后,打开终端时,顺序读取:/etc/bash.bashrc 和 ~/.bashrc
- 文本模式登录时,顺序读取:/etc/bash.bashrc/etc/profile 和 ~/.bash_profile
- 从其它用户 su 到该用户,则分两种情况:
- 如果带 -l 参数(或-参数,--login 参数su -l username则 bash 是 login 的,它将顺序读取以下配置文件:/etc/bash.bashrc/etc/profile 和~/.bash_profile。
- 如果没有带 -l 参数,则 bash 是 non-login 的,它将顺序读取:/etc/bash.bashrc 和 ~/.bashrc
- 注销时,或退出 su 登录的用户,如果是 longin 方式,那么 bash 会读取:~/.bash_logout
- 执行自定义的 Shell 文件时,若使用 bash -l a.sh 的方式,则 bash 会读取行:/etc/profile 和 ~/.bash_profile若使用其它方式bash a.sh./a.shsh a.sh这个不属于bash Shell则不会读取上面的任何文件。
- 上面的例子凡是读取到 ~/.bash_profile 的,若该文件不存在,则读取 ~/.bash_login若前两者不存在读取 ~/.profile。
## 二Bash 特性
### 1. 自动补全
很多命令都会提供一个`bash-complete`的脚本,在执行该命令时,敲`tab`可以自动补全参数,会极大提高生产效率。
`linux`命令自动补全需要安装`bash-completion`
```bash
# yum install -y bash-completion
```
默认情况下,`Bash``Linux`用户提供了下列标准补全功能。
- 变量补全
- 用户名补全
- 主机名补全
- 路径补全
- 文件名补全
### 2. 历史命令
- `Bash`有自动记录命令的功能,自动记录到`.bash_history`隐藏文件中。还可以在下次需要是直接调用历史记录中的命令。
- `Centos`可以通过`/etc/profile`中的文件来定义一些参数。
- 在`bash`中,使用`history`命令来查看和操作之前的命令,以此来提高工作效率。
- `history``bash`的内部命令,所以可以使用`help history`命令调出`history`命令的帮助文档。
- 获取某一条历史命令:`!number` 直接执行对应序号的命令。
- 快捷键调用历史命令:`Ctrl+r`输入关键字调出之前的命令。
```bash
# 查看之前使用的所有命令
[root@wxin ~]# history
# 显示最近的n个命令
[root@wxin ~]# history n
# 删除相应的第n个命令
[root@wxin ~]# history -d n
# 指定执行命令历史中的第n条语句
[root@wxin ~]# !n
# 指定执行命令历史中倒数第n条语句
[root@wxin ~]# !-n
# 执行命令历史中最后一条语句
[root@wxin ~]# !!
# 执行命令历史中最近一条以[String]开头的语句
[root@wxin ~]# ![String]
# 引用上一个命令中的最后一个参数
[root@wxin ~]# !$
# COMMAND + Esc键 + . 输入COMMAND之后,按下Esc键,松开后再按 . 则可以自动输入最近一条语句使用的参数
*# COMMAND + Alt + . 输入COMMAND之后,同时按下Alt和. 键,也可以自动输入最近一条语句使用的参数
# 将命令历史写入命令历史的文件中
[root@wxin ~]# history -w
# 回显 echo 之后的语句,而使用 echo $FILENAME 命令可以查看该 file 所在的路径
[root@wxin ~]# echo $HISTFILE
[root@wxin ~]# echo $HISTSIZE
# 查看命令历史的内容
[root@wxin ~]# cat .bash_history
# 删除所有的命令历史记录
[root@wxin ~]# history -c
```
### 3. 别名
`alias`命令,别名的好处是可以把本来很长的指令简化缩写,来提高工作效率。
```bash
[root@wxin ~]# alias #查看系统当前所有的别名
[root@wxin ~]# alias h5=head -5 #定义新的别名。这时候输入h5就等于输入head-5
[root@wxin ~]# unalias h5 #取消别名定义
```
如果想要文件永久生效,只需将上述别名命令写到对应用户或者系统`bashrc`文件中。
如果想用真实命令可以在命令前面添加反斜杠 ,使别名失效 。
```bash
[root@wxin ~]# \cp -rf /etc/hosts
```
### 4. 快捷键
**命令行编辑**
| 快捷键 | 作用 |
| :---------: | :--------------------------: |
| `Ctrl + a` | 将光标移动到行首 |
| `Ctrl + e` | 将光标移动到行尾 |
| `Ctrl + b` | 光标向左移动一个字符 |
| `Ctrl + f` | 光标向右移动一个字符 |
| `Alt + b` | 光标向左移动一个单词 |
| `Alt + f` | 光标向右移动一个单词 |
| `Ctrl + xx` | 在行首和当前光标位置之间切换 |
| `Ctrl + t` | 交换光标前两个字符的位置 |
| `Ctrl + w` | 删除光标前的一个单词 |
| `Ctrl + u` | 删除从光标到行首的所有内容 |
| `Ctrl + k` | 删除从光标到行尾的所有内容 |
| `Ctrl + y` | 粘贴最近一次删除的内容 |
| `Ctrl + -` | 撤销上一次操作 |
**历史命令操作**
| 快捷键 | 作用 |
| :--------------------: | :--------------------------: |
| `Ctrl + p` | 显示上一条历史命令 |
| `Ctrl + n` | 显示下一条历史命令 |
| `Ctrl + r` | 反向搜索历史命令 |
| `Ctrl + g` | 退出反向搜索历史模式 |
| `Alt + .``Esc + .` | 插入上一条命令的最后一个参数 |
| `!!` | 执行上一条命令 |
| `!n` | 执行历史记录中第 `n` 条命令 |
**进程控制**
| 快捷键 | 作用 |
| ---------- | -------------------------------------- |
| `Ctrl + c` | 终止当前正在运行的命令 |
| `Ctrl + d` | 发送 EOF文件结束符退出当前 Shell |
| `Ctrl + z` | 挂起当前进程(放入后台) |
| `Ctrl + l` | 清屏(等同 `clear` 命令) |
### 5. 前后台作业控制
`bash`单一终端界面下,经常需要管理或同时完成多个作业,如一边执行编译,一边实现数据备份,以及执行`SQL`查询等其他的任务。所有的上述的这些工作可以在一个`bash`内实现,在同一个终端窗口完成。
通过`jobs`方式来管理作业,当前终端的作业在其他终端不可见。
#### 1. 前后台作业的定义
- 前后台作业实际上对应的也就是前后台进程,因此也就有对应的`pid`。在这里统称为作业。
- 无论是前台作业还是后台作业,两者都来自当前的`Shell`,是当前`Shell`的子程序。
- 前台作业:可以由用户参与交互及控制的作业我们称之为前台作业。
- 后台作业:在内存可以自运行的作业,用户无法参与交互以及使用`[ctrl]+c`来终止,只能通过`bg``fg`来调用该作业。
#### 2. 作业命令
| 命令 | 作用 |
| :---------: | :----------------------------------------: |
| `&` | 在命令末尾添加 `&` 即可后台运行 |
| `jobs` | 显示当前会话的任务列表 |
| `fg %n` | 将任务 `n` 切到前台 |
| `bg %n` | 将任务 `n` 切到后台继续运行 |
| `Ctrl + z` | 暂停当前前台任务,任务状态变为 `suspended` |
| `kill + %n` | 移除指定的作业n |
- 任务编号前的 `+` 表示最近操作的任务,`-` 表示次近任务。
**示例:**
```bash
[root@wxin ~]# sleep 10000 &
[1] 2787
[root@wxin ~]# jobs
[1]+ 运行中 sleep 10000 &
[root@wxin ~]# sleep 30000 &
[2] 2800
[root@wxin ~]# jobs
[1]- 运行中 sleep 10000 &
[2]+ 运行中 sleep 30000 &
[root@wxin ~]# sleep 60000 &
[3] 2813
[root@wxin ~]# jobs
[1] 运行中 sleep 10000 &
[2]- 运行中 sleep 30000 &
[3]+ 运行中 sleep 60000 &
[root@wxin ~]# fg %1
sleep 10000
^Z
[1]+ 已停止 sleep 10000
[root@wxin ~]# bg %1
[1]+ sleep 10000 &
[root@wxin ~]# jobs
[1] 运行中 sleep 10000 &
[2]- 运行中 sleep 30000 &
[3]+ 运行中 sleep 60000 &
[root@wxin ~]# kill %1
[1] 已终止 sleep 10000
[root@wxin ~]# kill -15 %2
[2]- 已终止 sleep 30000
[root@wxin ~]# kill -9 %3
[3]+ 已杀死 sleep 60000
```
#### 3. 作业脱机管理
- 作业(进程)切换到后台可以避免由于误操作如`[ctrl] + c`等导致的job被异常中断的情形而脱机管理主要是针对终端异常断开的情形。
- 通常使用`nohup`命令来使得脱机或注销之后,`Job`依旧可以继续运行。也就是说`nohup`忽略所有挂断SIGHUP信号。
- 如果该方式命令之后未指定`&`符号,则`job`位于前台,指定`&`符号,则`job`位于后台。
#### 4. Screen 命令使用
**简介**
Screen 是一款由GNU计划开发的用于命令行终端切换的自由软件。用户可以通过该软件同时连接多个本地或远程的命令行会话并在其间自由切换。GNU Screen可以看作是窗口管理器的命令行界面版本。它提供了统一的管理多个会话的界面和相应的功能。
- **会话恢复:**只要`Screen`本身没有终止,在其内部运行的会话都可以恢复。这一点对于远程登录的用户特别有用——即使网络连接中断,用户也不会失去对已经打开的命令行会话的控制。只要再次登录到主机上执行`screen -r`就可以恢复会话的运行。同样在暂时离开的时候,也可以执行分离命令`detach`,在保证里面的程序正常运行的情况下让`Screen`挂起(切换到后台)。这一点和图形界面下的`VNC`很相似。
- **多窗口:**在`Screen`环境下,所有的会话都独立的运行,并拥有各自的编号、输入、输出和窗口缓存。用户可以通过快捷键在不同的窗口下切换,并可以自由的重定向各个窗口的输入和输出。`Screen`实现了基本的文本操作,如复制粘贴等;还提供了类似滚动条的功能,可以查看窗口状况的历史记录。窗口还可以被分区和命名,还可以监视后台窗口的活动。 会话共享`Screen`可以让一个或多个用户从不同终端多次登录一个会话,并共享会话的所有特性(比如可以看到完全相同的输出)。它同时提供了窗口访问权限的机制,可以对窗口进行密码保护。
**安装 `screen`**
流行的Linux发行版例如Red Hat Enterprise Linux通常会自带screen实用程序如果没有的话可以从GNU screen的官方网站下载。
```bash
[root@wxin ~]# yum install -y screen
```
**语法**
```shell
screen [-AmRvx -ls -wipe][-d <作业名称>][-h <行数>][-r <作业名称>][-s ][-S <作业名称>]
-A  将所有的视窗都调整为目前终端机的大小。
-d <作业名称>  将指定的screen作业离线。
-h <行数>  指定视窗的缓冲区行数。
-m  即使目前已在作业中的screen作业仍强制建立新的screen作业。
-r <作业名称>  恢复离线的screen作业。
-R  先试图恢复离线的作业。若找不到离线的作业即建立新的screen作业。
-s  指定建立新视窗时所要执行的Shell。
-S <作业名称>  指定screen作业的名称。
-v  显示版本信息。
-x  恢复之前离线的screen作业。
-ls或--list  显示目前所有的screen作业。
-wipe  检查目前所有的screen作业并删除已经无法使用的screen作业。
```
示例:
```bash
[root@wxin ~]# screen -S yourname # 新建一个叫yourname的session
[root@wxin ~]# screen -ls # 列出当前所有的session
[root@wxin ~]# screen -r yourname # 回到yourname这个session
[root@wxin ~]# screen -d yourname # 远程detach某个session
[root@wxin ~]# screen -d -r yourname # 结束当前session并回到yourname这个session
```
**在`Session`下,使用`Ctrl + a` C-a**
```shell
C-a ? # 显示所有键绑定信息
C-a c # 创建一个新的运行Shell的窗口并切换到该窗口
C-a n # Next切换到下一个 window
C-a p # Previous切换到前一个 window
C-a 0..9 # 切换到第 0..9 个 window
Ctrl+a [Space] # 由视窗0循序切换到视窗9
C-a C-a # 在两个最近使用的 window 间切换
C-a x # 锁住当前的 window需用用户密码解锁
C-a d # detach暂时离开当前session将目前的 screen session (可能含有多个 windows) 丢到后台执行,并会回到还没进 screen 时的状态,此时在 screen session 里,每个 window 内运行的 process (无论是前台/后台)都在继续执行,即使 logout 也不影响。
C-a z # 把当前session放到后台执行用 Shell 的 fg 命令则可回去。
C-a w # 显示所有窗口列表
C-a t # time显示当前时间和系统的 load
C-a k # kill window强行关闭当前的 window
C-a [ # 进入 copy mode在 copy mode 下可以回滚、搜索、复制就像用使用 vi 一样
C-b BackwardPageUp
C-f ForwardPageDown
H(大写) High将光标移至左上角
L Low将光标移至左下角
0 移到行首
$ 行末
w forward one word以字为单位往前移
b backward one word以字为单位往后移
Space 第一次按为标记区起点,第二次按为终点
Esc 结束 copy mode
C-a ] # paste把刚刚在 copy mode 选定的内容贴上
```
**常用操作**
- 创建会话(-m 强制)
```bash
[root@wxin ~]# screen -dmS session_name # session_name session名称
```
- 关闭会话
```bash
[root@wxin ~]# screen -X -S [session # you want to kill] quit
```
- 查看所有会话
```bash
[root@wxin ~]# screen -ls
```
- 进入会话
```bash
[root@wxin ~]# screen -r session_name
```
- 清楚`dead`会话
如果由于某种原因其中一个会话死掉了(例如人为杀掉该会话),这时`screen -list`会显示该会话为`dead`状态。使用`screen -wipe`命令清除该会话。
- 关闭或杀死窗口
1. 正常情况下当你退出一个窗口中最后一个程序通常是bash这个窗口就关闭了。另一个关闭窗口的方法是使用C-a k这个快捷键杀死当前的窗口同时也将杀死这个窗口中正在运行的进程。
2. 如果一个Screen会话中最后一个窗口被关闭了那么整个Screen会话也就退出了screen进程会被终止。
3. 除了依次退出/杀死当前`Screen`会话中所有窗口这种方法之外还可以使用快捷键C-a :,然后输入`quit`命令退出`Screen`会话。需要注意的是,这样退出会杀死所有窗口并退出其中运行的所有程序。其实`C-a`:这个快捷键允许用户直接输入的命令有很多,包括分屏可以输入[split](http://man.linuxde.net/split)等,这也是实现`Screen`功能的一个途径,不过个人认为还是快捷键比较方便些。
### 6. 输入输出重定向
一般情况下计算机从键盘读取用户输入的数据然后再把数据拿到程序C语言程序、Shell 脚本程序等)中使用;这就是标准的输入方向,也就是从键盘到程序。
程序中产生数据直接呈现到显示器上,这就是标准的输出方向,也就是从程序到显示器。输入输出方向就是数据的流动方向:
- 输入方向就是数据从哪里流向程序。数据默认从键盘流向程序,如果改变了它的方向,数据就从其它地方流入,这就是输入重定向。
- 输出方向就是数据从程序流向哪里。数据默认从程序流向显示器,如果改变了它的方向,数据就流向其它地方,这就是输出重定向。
#### 1. 文件描述符
又称文件句柄
计算机的硬件设备有很多,常见的输入设备有键盘、鼠标等,输出设备有显示器、投影仪、打印机等。不过,在 Linux中标准输入设备指的是键盘标准输出设备指的是显示器。
- Linux 中一切皆文件,包括标准输入设备(键盘)和标准输出设备(显示器)在内的所有计算机硬件都是文件。
- 为了表示和区分已经打开的文件Linux 会给每个文件分配一个 ID这个 ID 就是一个整数被称为文件描述符File Descriptor
| 文件描述符(文件句柄) | 文件名 | 类型 | 硬件 |
| ---------------------- | ------ | ---------------- | ------ |
| 0 | stdin | 标准输入文件 | 键盘 |
| 1 | stdout | 标准正确输出文件 | 显示器 |
| 2 | stderr | 标准错误输出文件 | 显示器 |
- Linux 程序在执行任何形式的 I/O 操作时都是在读取或者写入一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数它的背后可能是一个硬盘上的普通文件、FIFO、管道、终端、键盘、显示器甚至是一个网络连接。
- stdin、stdout、stderr 默认都是打开的在重定向的过程中0、1、2 这三个文件描述符可以直接使用。
#### 2. 输出重定向
输出重定向是指命令的结果不再输出到显示器上而是输出到其它地方一般是文件中。这样做的最大好处就是把命令的结果保存起来当我们需要的时候可以随时查询。Bash 支持的输出重定向符号如下表所示。
- 输出重定向中,> 代表的是覆盖,>> 代表的是追加。
- 输出重定向的完整写法其实是 fd>file 或者 fd>>file ,其中 fd 表示文件描述符,如果不写,默认为 1也就是标准输出文件。
- 当文件描述符为 1 时,一般都省略不写,如上表所示;当然,如果你愿意,也可以将 command >file 写作 command 1>file但这样做是多此一举。
- 当文件描述符为大于 1 的值时,比如 2就必须写上。
- 需要重点说明的是fd 和 >之间不能有空格,否则 Shell 会解析失败;> 和 file 之间的空格可有可无。为了保持一致,习惯在 > 两边都不加空格。
下面的语句是一个反面教材:
```bash
[root@wxin ~]# echo "c.biancheng.net" 1 >log.txt
```
注意 1 和 > 之间的空格。echo 命令的输出结果是 c.biancheng.net初衷是将输出结果重定向到 log.txt打开 log.txt 文件后,发现文件的内容为 c.biancheng.net 1这就是多余的空格导致的解析错误。也就是说Shell 将该条语句理解成了下面的形式:
```bash
[root@wxin ~]# echo "c.biancheng.net" 1 1>log.txt
```
**示例**
将 echo 命令的输出结果以追加的方式写入到 demo.txt 文件中。
```bash
[root@wxin ~]# echo $(date) >> demo.txt #将输入结果以追加的方式重定向到文件
[root@wxin ~]# cat demo.txt
Fri Feb 14 19:18:40 CST 2019
```
`ls -l`命令的输出结果重定向到文件中。
```bash
[root@wxin ~]# ls -l #先预览一下输出结果
total 4
-rw-------. 1 root root 1526 Mar 30 2019 anaconda-ks.cfg
[root@wxin ~]# ls -l >demo.txt #重定向
[c.biancheng.net]$ cat demo.txt #查看文件内容
total 4
-rw-------. 1 root root 1526 Mar 30 2019 anaconda-ks.cfg
```
**错误输出重定向示例**
命令正确执行是没有错误信息的,我们必须刻意地让命令执行出错,如下所示:
```bash
[root@wxin ~]# ls java #先预览一下错误信息
ls: cannot access java: No such file or directory
[root@wxin ~]# ls java 2>err.log #重定向
[root@wxin ~]# cat err.log #查看文件
ls: cannot access java: No such file or directory
```
**正确输出和错误信息同时保存**
```bash
[root@wxin ~]# ls -l >out.log 2>&1
[root@wxin ~]# ls java >>out.log 2>&1
[root@wxin ~]# cat out.log
total 8
-rw-------. 1 root root 1526 Mar 30 2019 anaconda-ks.cfg
-rw-r--r-- 1 root root 50 Feb 14 20:15 err.log
-rw-r--r-- 1 root root 0 Feb 14 20:15 out.log
ls: cannot access java: No such file or directory
```
- `out.log`的最后一行是错误信息,其它行都是正确的输出结果。
- 上面的实例将正确结果和错误信息都写入同一个文件中,建议把正确结果和错误信息分开保存到不同的文件中
```bash
[root@wxin ~]# ls -l >>out.log 2>>err.log
```
正确的输出结果会写入到`out.log`,而错误的信息则会写入到`err.log`
**`/dev/null`文件**
如果不想把命令的输出结果保存到文件,也不想把命令的输出结果显示到屏幕上,干扰命令的执行,可以把命令的所有结果重定向到`/dev/null`文件中
```bash
[root@wxin ~]# ls -l &>/dev/null
```
可以把`/dev/null`当成 Linux 系统的垃圾箱,任何放入垃圾箱的数据都会被丢弃,不能恢复。
#### 3. 输入重定向
输入重定向就是改变输入的方向,不再使用键盘作为命令输入的来源,而是使用文件作为命令的输入。
**示例:**
```bash
输入重定向例子:
if [ 5 -gt 3 ];then
cat >> ifcfg-eth2 <<-eof
TYPE=Ethernet
BOOTPROTO=none
NAME=ens160
DEVICE=ens160
ONBOOT=yes
IPADDR=192.168.26.200
PREFIX=24
GATEWAY=192.168.26.2
DNS1=192.168.26.2
eof
fi
```
**输入重定向举例**
统计文档中有多少行文字。
Linux wc 命令可以用来对文本进行统计,包括单词个数、行数、字节数,
```shell
wc [选项] [文件名]
选项:
-c # 选项统计字节数
-w # 选项统计单词数
-l # 选项统计行数
```
统计 readme.txt 文件中有多少行文本
```bash
[root@wxin ~]# vim readme.txt
www.wxin.oline
www.wxin.cn
www.wxin.eg
[root@wxin ~]# wc -l < readme.txt
3
```
逐行读取文件内容
```bash
#!/bin/bash
while read str; do
echo $str
done < readme.txt
while ls; do
echo hell
done
```
统计用户在终端输入的文本的行数。
用输入重定向符号 <<,这个符号的作用是使用特定的分界符作为命令输入的结束标志,而不使用 Ctrl+D 键。
```bash
[root@wxin ~]# wc -l <<END
> 123
> 789
> abc
> xyz
> END
4
```
wc 命令会一直等待用输入,直到遇见分界符 END 才结束读取。
<< 之后的分界符可以自由定义只要再碰到相同的分界符两个分界符之间的内容将作为命令的输入不包括分界符本身
**read 命令用法**
```shell
#!/bin/bash
stty -echo
read -p "输入密码:" password
echo
echo "我的密码是:$password"
stty echo
```
### 7. 管道
**管道`|`**
- 管道,从一头进去,从另一头出来。
- 在Shell中管道将一个程序的标准输出作为另一个程序的标准输入就像用一根管子将一个程序的输出连接到另一个程序的输入一样。
- 管道的符号是 |,下面的程序将 man 的标准输出作为 less 的标准输入,以实现翻页的功能:
```bash
[root@wxin ~]# man ls | less
```
**管道`tee`**
- 有时候我们想要同时将程序的输出显示在屏幕上(或进入管道)和保存到文件中,这个时候可以使用`tee`
- tee 程序的输出和它的输入一样,但是会将输入内容额外的保存到文件中:
```bash
[root@wxin ~]# cat /etc/passwd | tee hello.txt
```
tee 程序将 cat 程序的输出显示在屏幕上,并且在 hello.txt 文件中保留了副本。需要注意的是,如果 tee 命令中指定的文件已经存在,那么它将会被覆盖,使用 -a 选项在文件末尾追加内容(而不是覆盖)
```bash
[root@wxin ~]# cat hello.txt | tee -a hello.txt.bk
```
### 8. 命令排序
| 符号 | 说明 |
| ---- | ---------------------------------------------- |
| `;` | 无论前面是否执行成功,分号后的命令都会继续执行 |
| `&&` | 前面执行成功,后面的才继续执行 |
| `||` | 前面命令不成功,后面的命令也会继续 |
### 9. 通配符
**基础通配符**
| 通配符 | 说明 |
| :----: | :--------------------------------: |
| `*` | 匹配任意数量(包括零个)的任意字符 |
| `?` | 匹配单个任意字符 |
| `[]` | 匹配方括号内的任意**一个字符** |
| `[!]` | 匹配**不在**方括号内的字符 |
| `{}` | 匹配大括号内的任意一个字符串 |
**范围匹配**
| 模式 | 说明 |
| ---------- | ------------ |
| `[a-z]` | 匹配小写字母 |
| `[0-9]` | 匹配数字 |
| `[A-Za-z]` | 匹配所有字母 |