文章目录
一、Shell 特点
1.1 shell 特点
1、 属于弱类型语言,所以定义变量时不需要指定变量类型
2、 Shell中只有字符串一种变量类型,所有数据实际上都是以字符串形式处理,所有变量和数据都可以看作是字符串;
3、 属于解释型语言,编写好的Shell脚本需要指定的shell解释器才能执行;
1.2 Shell 脚本组成
一个Shell 脚本一般由三部分组成:
1、首行的 shebang
机制,指定需要使用的shell解释器类型
2、需要执行的相关指令
3、注释信息
例如:
#!/bin/bash
# 注释信息
if [ $(id -u) -ne 1000 ]; then
echo "need tomrun this script"
exit 1
fi
1.3 shell 解释器
现在Linux操作系统中,一般用的shell解释器都是bash,除了bash还有其他的一些解释器都是可用的。
查看当前使用的解释器类型:
ubuntu@node1:~$ echo $SHELL
/bin/bash
查看当前系统支持的解释器:
ubuntu@node1:~$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash
1.4 执行shell脚本
方法一:解释器名称 + 脚本路径
,需要当前用户具有对这个脚本文件的读权限;
例如:
bash backup.sh
方法二:类似于执行命令一样执行脚本,需要当前用户具有对这个脚本文件的读和执行权限;
例如:
# 绝对路径
/home/tom/backup.sh
# 相对路径 ./表示在当前目录下
./bash.sh
二、使用变量
1.定义变量
格式:变量名=变量值
特点:
1、 Shell对空格非常严格,在变量赋值时,要求 = 两边不能有任何空格
2、 变量由数字、字母和下划线组成,且不允许使用数字开头,命名方式一般采用蛇形命名法
3、 在变量赋值时,无论是否加引号,赋值时变量的值都不会受到影响,Shell 都会把命令的输出完整地赋值给变量
例如:
SERVER_PATH=/Worker/module_pieces
2. 销毁变量
格式:unset 变量名
例如:
unset SERVER_PATH
3. 调用变量
格式:$变量名 或 ${变量名}
例如:
# 使用大括号的目的是用来将变量部分和后续的字符进行分隔
${SERVER_PATH}
3.1 调用变量时无任何引号
- 变量值会根据当前的 IFS(默认为空格、换行符、制表符)将变量的值进行分割。换行符会被转换为空格,多个连续的空格也会被压缩为一个。
- 如果变量的值中包含通配符(例如 *、? 等),Shell 会将它们扩展为匹配当前目录下的文件名。
例如:
ubuntu@node1:~/test$ ls -1
file1
file2
file3
file4
# 定义变量
ubuntu@node1:~/test$ NAME=$(ls -1)
# 输出变量
ubuntu@node1:~/test$ echo $NAME
file1 file2 file3 file4
3.2 调用变量是使用双引号
- 变量值会做为一个整体进行输出,格式会得到保存,包括换行符、空格等符号也不会被处理;
- 变量值里面的通配符不会被进行解析为文件名
- IFS 不会对变量内容进行任何拆分或替换
例如:
ubuntu@node1:~/test$ echo $NAME
file1 file2 file3 file4
# 加引号时输出变量
ubuntu@node1:~/test$ echo "$NAME"
file1
file2
file3
file4
4. 指定变量默认值
4.1 临时使用默认值
如果变量未定义或为空,则指定的默认值只在本次引用时有效,不会修改变量的实际值。即如果下一次执行脚本时,变量依然为空或未定义
格式: 变量名:-默认值
${var:-"default_value"}
例如:
MySQL_LOG_DIR="${MySQL_LOG_DIR:-/var/log/mysql}"
4.2 改变原有变量的值
如果变量未定义或为空,则直接将指定的默认值赋值给这个变量,下一次执行脚本的时候,变量值就是这个指定的默认值;
格式:变量名:=默认值
${var:="default_value"}
例如:
MySQL_LOG_DIR="${MySQL_LOG_DIR:=/var/log/mysql}"
三: 使用数组
1. 普通数组
格式:arry_name=(value1 value2 value3 value4)
特点:
1、索引是整数,不能自定义为字符串,并且定义数组时赋值的话,不能手动指定索引,索引是从0开始的连续整数;
2、定义数组时指定数组元素,可以使用空格或换行符来做为元素之间的分隔符;
例如:
PATTERNS=(
"lct_net_manager.tb_err_future_loc_20%"
"lct_net_manager.tb_heartbeat_data_20%"
"lct_net_manager.tb_loc_data_20%"
"lct_net_manager.tb_ori_loc_data_20%"
"position_ehcommon.tb_blood_pressure_history_%"
"position_ehcommon.tb_locationinfo_%"
"position_ehcommon.tb_heart_rate_history_%"
"position_ehcommon.tb_pit_pressure_history_%"
"position_ehcommon.tb_heart_pressure_history_%"
"position_ehcommon.tb_gps_locationinfo_%"
"only_door.tb_person_location_history"
)
2. 关联数组:
格式:declare -A arry_name=([key1]=v1 [key2]=v3 [key3]=v3)
特点:
1、 数组赋值时,可以给数组的元素加上自定义的索引,而不是默认的整数索引。
2、数组赋值的时候需要在方括号 []
内指定索引(键),否则会报错,不像普通数组一样会自动使用整数作为索引;
例如:
key_size=$(redis-cli -h ${HOST} -p ${PORT} -a ${PASSWD} -n ${db_num} MEMORY USAGE "$key_name")
# redis的键名做为key 键值作为value
key_arry["$key_name"]=$key_size
3. 操作数组
- 获取指定索引的值:
${数组名[索引]}
- 获取数组所有元素:
${arry_name[@]}
- 获取数组所有索引:
${!arry_name[@]}
- 获取数组的长度:
${#arry_name[@]}
- 设置数组的默认值:
${arry[@]:-22 80 8000 8001 3306 9100 9001 1883 1884 9802}
四:使用字符串
Shell只有字符串这一种类型,所有的变量或变量值都可以看作是字符串,对字符串的操作一般有以下几种;
1. 获取变量值长度
格式:len=${#variable}
例如:
ubuntu@node1:~$ num=123456789
ubuntu@node1:~$ len=${#num}
ubuntu@node1:~$ echo $len
9
2. 删除变量值的前缀:
-
会从字符串的开头进行匹配
-
从字符串开头删除最短匹配:
variable=${variable#pattern}
-
从字符串开头删除最长匹配:
variable=${variable##pattern}
例如:
ubuntu@node1:~$ NAME="lct_net_manager.tb_err_future_loc_20%"
ubuntu@node1:~$ tb_name=${NAME#*.}
ubuntu@node1:~$ echo $tb_name
tb_err_future_loc_20%
3. 删除变量值的后缀
特点:
- 会从字符串的默认开始进行匹配
- 从字符串末尾删除最短匹配:
variable=${variable%pattern}
- 从字符串末尾删除最长匹配:
variable=${variable%%pattern}
例如:
ubuntu@node1:~$ NAME="lct_net_manager.tb_err_future_loc_20%"
# 删除从 . 开始的所有字符
ubuntu@node1:~$ db_name=${NAME%%.*}
ubuntu@node1:~$ echo $db_name
lct_net_manager
4. 检查是否包含特定字符串
格式:[[ string == *substring* ]]
例如:
if [[ "${DAILY_BACKUP_NAME}" == *.tar.gz ]];then
五:使用函数
1. 定义函数
- 格式一:
function fun_name()
{
command;
}
- 格式二:
function fun_name
{
command;
command;
}
- 格式三:
fun_name()
{
command;
command;
}
2. 函数传参
- 调用函数时,只需要将参数写在函数名后面即可,使用空格进行分隔;
- 函数内部通过Shell预定义的位置变量就可以接收到调用函数时传递的参数;
$n
# n 是大于0的正整数,表示传递给脚本或函数的前个参数。例如:$1
# n 等于0,表示的是脚本的名称或当前函数的名称。
3. 函数返回值
# 执行到retun部分时候,整个函数就会立即结束
return [0-255]
4. 调用函数
fun_name [parameter]
六:实现算数运算
Shell中虽然只有字符串一种数据类型,但是可以使用数字型字符串(仅包含数字字符0-9)来表示和操作数字,然后通过相应的工具和命令来实现进行算数运算;
1.1 表达式
- 表达式:编程中的表达式由常量、变量、操作符和函数等组成的代码片段,可以通过计算产生一个值;
- 运算表达式:只包含数字和算术运算符,不包含赋值符号
=
,用于执行数学运算。 - 赋值表达式:包含赋值操作符
=
的表达式,用于将一个值(或另一个表达式的结果)赋给变量。 - 操作符表达式:包含算术运算符、逻辑运算符、比较运算符等,但不包含赋值运算符
=
。主要作用是进行计算、判断条件以及执行逻辑操作,而不涉及赋值操作。
1.2 算数运算命令
命令 | 变量解析 | 表达式格式 | 返回值 | 范例 |
---|---|---|---|---|
let | 能自动解析变量,变量前可以不加$符号对变量进行引用 | 运算、赋值表达式 | 无计算返回值 | let sum=a+b |
expr | 不能自动解析变量,需要在变量前加$符号对变量进行引用 | 操作符表达式 | 返回值是计算值 | sum=$(expr $a + $b) |
bc | 不能自动解析变量,需要在变量前加$符号对变量进行引用 | 操作符表达式 | 返回值是计算值 | a=$(echo “1+2” |
(()) | 能自动解析变量,变量前可以不加$符号对变量进行引用 | 运算、赋值表达式 | 返回值是命令退出状态码 | (( sum = a+b )) |
$(()) | 能自动解析变量,变量前可以不加$符号对变量进行引用 | 运算、赋值表达式 | 返回值是计算值 | $((5 + 3)) |
七:实现逻辑运算
- Shell中,没有直接的布尔值类型,不像编程语言那样有
true
和false
作为专门的布尔数据类型。逻辑判断依赖于命令的退出状态码,即0 表示真,非 0 表示假, - Shell中,通过 $? 可以获取到上一个命令执行后的命令退出状态码;
1. 与 运算
与(&&
)
- 当两个条件都为真时,整个表达式为真。只要有一个条件为假,结果就是假。
- 如果第一个条件为假,则不会再评估第二个条件,因为整个表达式已经确定为假了。
- 如果第一个条件为真,才会继续评估第二个条件。
例如:
# 前面执行成功才会执行后面
cd $(dirname ${FULL_BACKUP_PATH}) && tar -czf ${FULL_BACKUP_NAME}.tar.gz ${FULL_BACKUP_NAME}
2. 或 运算
或(||
)
- 当至少一个条件为真时,整个表达式为真。只有当所有条件都为假时,结果才是假。
- 如果第一个条件为真,则不会再评估第二个条件,因为整个表达式已经确定为真了。
- 如果第一个条件为假,才会继续评估第二个条件。
3. 非 运算
非(!
)
- 将条件的真假值取反。如果条件为真,使用
!
后结果为假;如果条件为假,使用!
后结果为真。
单个
|
或&
Shell中的 | 是管道符,用于将前一个命令的输出作为输入传递给下一个命令,实现数据的逐步处理。
Shell 中的 & 是后台符, 用于将命令放入后台执行,使脚本或终端可以继续执行其他任务,而不会等待该命令完成。
八:测试运算
1. 测试运算命令
1.1 test
和 [
test
是一种早期的命令形式,等价于[
,它们在功能上几乎完全相同。可以理解为[
就是test命令的别名;[
是现在常用的一种用于进行条件测试的命令,它的语句结构比test更加简洁和容易阅读;- [ ] 的左右方括号
[
和]
必须用空格与表达式隔开。 [
执行测试操作,而]
仅作为语法符号来表示条件表达式的结束位置。
语法格式:
# test 语法格式
test 表达式
# [ 语法格式
[ 表达式 ]
例如:
test 5 -eq 5
[ 5 -eq 5 ]
1.2 [[ ]]
[[ ]] 是 在 [ ] 的基础上进行了扩展,提供更强大的能力,主要体现在两个方面;
-
支持Shell的通配符:如果使用
= 或 !=
来连接比较的字符串,则会将右侧的特定符号视为通配符; -
支持正则表达式:如果使用
=~
来连接比较的字符串,则会将右侧的特定符号视为正则表达式的元字符;
例如:
if [[ "${new_project_path}" == "${PROJECT_PATH}/html/"* ]]; then
2. 数值测试运算
-eq
:检查两个数值是否相等-ne
:检查两个数值是否不等-gt
:检查第一个数值是否大于第二个数值-lt
:检查第一个数值是否小于第二个数值-ge
:检查第一个数值是否大于等于第二个数值-le
:检查第一个数值是否小于等于第二个数值
例如:
if [[ $(date +%u) -eq 7 ]]; then
# 今天是周日
START_OF_WEEK=$(date +'%Y%m%d')
else
# 不是周日,使用上一个周日
START_OF_WEEK=$(date -d "last Sunday" +'%Y%m%d')
fi
3. 字符串测试运算
Shell中进行字符串测试时,= 和 ==
都是等价的,但是为了兼容性,一般使用的都是 =
=
:检查字符串是否相等。!=
:检查字符串是否不等。-z string
:检查字符串是否为空。-n string
:检查字符串是否非空。
例如:
if [ -n "${LATEST_FULL_BACKUP}" ]; then
LATEST_FULL_BAK_NAME=$(basename "${LATEST_FULL_BACKUP}")
DIFF_BackUP_NAME="DIFF_$(date +'%Y%m%d%H%M')_ON_${LATEST_FULL_BAK_NAME}"
DIFF_BackUP_PATH="${BACKUP_PATH}/${DIFF_BackUP_NAME}"
fi
4. 文件测试运算
-e filename
:检查文件是否存在-f filename
:检查是否为普通文件-d filename
:检查是否为目录-s filename
:检查文件是否非空
例如:
if [ ! -d "${BACKUP_PATH}" ];then
mkdir -p "${BACKUP_PATH}" && echo "$(date +'%F %T') ${BACKUP_PATH} 创建成功" | tee -a ${LogFile};
fi
九:语句结构
1. 分支语句
1.1 if 分支语句
- 单分支
if 条件; then
Command;
fi
- 双分支
if 条件; then
Command;
else
Command;
fi
- 多分支
if 条件; then
Command;
elif 条件; then
Command;
else
Command;
fi
例如:
if [ $? -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') 打包成功:person_data-${Date}.tar.gz" | tee -a ${LogFile}
else
echo "$(date '+%Y-%m-%d %H:%M:%S') 打包失败,请检查密码、网络是否正常" | tee -a ${LogFile}
exit 1
fi
1.2 case 分支语句
case 变量 in
模式1)
# 当变量匹配模式1时执行的代码
;;
模式2)
# 当变量匹配模式2时执行的代码
;;
*)
# 以上模式都不匹配时执行的代码
;;
esac
2. 循环语句
2.1 for 循环语句
# 普通风格
for 变量名 in 参数列表
do
command;
done
# c语言风格
for ((i=0; i<=5; i++))
do
command
done
例如:打印乘法表
#!/bin/bash
for (( i=1; i<=9; i++ )) ;do
for (( j=1; j<=i; j++ ));do
echo -ne "$i * $j = $(expr $i \* $j) \t"
if [ $i -eq $j ];then
echo
fi
done
done
2.2 while 循环语句
while 条件; do
command;
done
十:Shell 中的引号
1. 双引号
使用双引号括起来的字符串,变量值中的命令和变量会替换为输出值,其他字符串原样输出;
例如:
# 变量会替换为具体的变量值
ubuntu@ubuntu:~$ name=TOM
ubuntu@ubuntu:~$ test="name is ${name}"
ubuntu@ubuntu:~$ echo $test
name is TOM
例如:
# 通配符不会进行拓展
ubuntu@ubuntu:~/test$ ls *
file1 file2 file3 file4 file5 file6
ubuntu@ubuntu:~/test$ ls "*"
ls: cannot access '*': No such file or directory
2. 单引号
使用单引号括起来的字符串,变量值中的内容会完全保留字面值,所有的字符都不会被解释或扩展 ;
例如:
ubuntu@ubuntu:~/test$ test='name is ${name}'
ubuntu@ubuntu:~/test$ echo $test
name is ${name}
3. 反引号
反引号的作用等同于 $()
,会将括起来的内容当作命令执行,并将其输出作为一个字符串返回;
例如:
ubuntu@ubuntu:~/test$ filename=$(ls)
ubuntu@ubuntu:~/test$ echo $filename
file1 file2 file3 file4 file5 file6
十一:Shell 中的括号
1. 大括号
Shell 中的大括号有以下几个作用:
1.1 变量分隔
使用 $
引用变量时,如果变量名后面紧跟其他的字符串,这个时候Shell就没法正确识别正确的变量名,这个时候使用${}
就可以将变量名和后续的字符串进行分隔;
例如:
ubuntu@ubuntu:~/test$ name=TOM
ubuntu@ubuntu:~/test$ echo ${name}
TOM
# 无输出
ubuntu@ubuntu:~/test$ echo $namefdas
1.2 命令块
如果有多个语句或者命令需要作为一个整体进行执行,可以使用大括号将这些命令都括起来。
格式:{ cmd1; cmd2; }
特点:
1、大括号两边必须有空格
2、如果写在一行每个命令都需要以分号结尾
3、整个的退出状态码是最后一个命令的退出状态码。
1.3 字符串组合
例如:
echo {a,b,c} 会输出 a b c
1.4 生成有序序列
格式:{ 开始..结束 }
例如:
echo {1..5} 会输出 1 2 3 4 5。
例如:
# 删除指定的二进制日志文件
sudo rm -f mysql-bin.0018{37..79}
2. 小括号
2.1 命令组合
将多个命令或语句通过小括号括起来后,会将这些命令组合作为一个整体来进行执行,但是和大括号有以下不同。
格式:( cd /some/dir && ls )
1、通过在当前进程下开启一个子进程来执行这个命令组合,子 Shell 中的变量和更改不会影响当前 Shell,但是当前进程的相关内容是被子shell继承了的。
2、整个的退出状态码是最后一个命令的退出状态码。
2.2 启用子shell
使用小括号括起来的命令会在当前进程开启一个子shell来执行,子进程的相关环境变量信息会继承父进程的,但是子进程的环境变量信息父进程看不到;
格式:( command )
2.3 进程替换
2.3.1 输入替换
根据小括号的特性,会在当前进程中开启一个子进程来执行command2命令。command2命令原本该输出到屏幕的内容,会通过<
来进行重定向,此时<(command2)
返回的是一个伪文件路径,而不是直接的数据流,这个伪文件路径指向子进程的输出。
这样通过输入重定向 < 就可以读取该文件指向的数据了。
输入替换格式:command1 < <(command2)
例如:
declare -a ip_arry
# 会首先执行括号里面的内容,然后
mapfile -t ip_arry < <(ip -4 a | grep -v 127.0.0.1 | awk '/inet / {print $2}' | cut -d '/' -f 1)
2.3.2 输出替换
会将当前命令command1的输出进行重定向,作为括号里面命令command2的输入。
输出替换格式:command1 > >(command2)
例如:
# >(command2) 会启动一个子进程来运行 command2,并生成一个伪文件路径
# > 会将当前命令的输出重定向到这个为文件中
ls -1 | tee >(grep ".sh" > scripts.txt) >(grep -v ".sh" > others.txt)
3. 中括号
3.1 条件测试
作为 test 命令的简写。用于测试文件、字符串和数字等条件。返回的是命令的退出状态码;
3.2 通配符
用于匹配特定的字符集或字符范围
3.3 单字符匹配
例如:
[abc]:匹配任意一个字符,如 a、b 或 c
3.4 范围匹配
例如:
[a-z]:匹配小写字母 a 到 z 中的任意一个字符。
[0-9]:匹配数字 0 到 9 中的任意一个字符。
3.5 否定匹配
例如:
[^abc]:匹配除了 a、b、c 以外的任何字符。^ 放在 [] 中的开头表示取反。
十二:管道符使用
Linux 相关操作系统中,使用 | 符号来表示一个匿名管道符,从而实现将前一个语句或命令的标准输出(stdout)作为输入(stdin)传递给下一个语句或命令。
格式:语句1 | 语句2
1. 单个命令的使用场景
ps aux | grep "apache"
2. 复杂命令组合(语句)的场景
# 将 for语句本来应该输出到屏幕的内容作为sort命令的输出,然后将sort命令本该输出到屏幕的内容作为while语句的输入
for key_name in "${!key_arry[@]}"; do
echo "${key_arry[$key_name]} $key_name"
done | sort -nr | while read size key; do
echo "$size $key"
done
任何语句(命令块),如
for
、while
、if
等在结束后都可以通过管道符|
将输出传递给下一个命令。使得可以将多个命令组合在一起。
3. 不能直接使用管道符处理方法
存在某些命令不能直接使用管道符来处理从上一个命令传递的数据,例如:rm、mv、cp、kaill
等。
可以通过 xargs
来实现管道操作。xargs
可以将管道传递的输入转换为命令的参数,使得这些命令能够处理数据流。
格式:语句1 | xargs 命令或语句
xargs
会自动将多行输入转换为单行参数传递给命令
十三:使用 heredoc
heredoc 是一种特殊的重定向方式,允许一次性从标准输入读取多行内容给一个命令。有两种语法格式
1. 定界符无引号
1、定界符可以随意定义,一般使用EOF表示结束,当在文本块中在单独一行输入这个定界符,此时就会结束输入
2、内容中的变量、通配符、转义字符会被shell解释,变量会替换为具体的变量值,通配符会被解释为匹配当前目录中的文件和目录,反斜线()会被解释为转义字符,如 \n 表示换行;
# <<DELIMITER 是固定搭配,<< 后必须跟一个界符,不是类似于> 和 >>这种。
command <<DELIMITER
line_1
line_2
...
DELIMITER
例如:
cat > /etc/rsync.pas <<EOF
rsync:123456
EOF
2. 定界符有引号
定界符加上单引号或双引号都可以
内容中的变量、通配符、转义字符都不会被Shell解释,而是将内容视为纯文本,不会进行任何拓展。
command <<'DELIMITER'
line_1
line_2
...
DELIMITER
例如:
cat > /home/tom/work/job/startRsync.sh <<"EOF"
#!/bin/sh
sersyncStatus=$(pgrep sersync2 | wc -l)
sersyncBin="/usr/local/sersync/bin/sersync2"
xmlPath="/usr/local/sersync/conf"
xmlNum=$(find $xmlPath -name "*.xml" | wc -l)
backupPath="/home/tom/work/html/EHCommon/resources"
#i=0
#for fileName in $xmlPath/*.xml; do
# i=$(($i+1))
#done
if [ $sersyncStatus -ne $xmlNum ]; then
killall sersync2
# 双主同步将以下注释打开,服务器启动后自动同步远程服务器数据
# /usr/bin/rsync -artuz -R [email protected]::rsyncdata $backupPath --password-file=/etc/rsync1.pas
for fileName in $xmlPath/*.xml; do
$sersyncBin -d -o $fileName
done
fi
EOF
十四:使用重定向
Linux 中的三个文件描述符 0 1 2 固定和标准输入(stdin)、标准输出(stdout)和标准错误(stderr)进行绑定。当某个进程启动时,操作系统会自动将它的标准输入(stdin)、标准输出(stdout)和标准错误(stderr)绑定到对应的终端设备上;
所以进程获取的输入信息一般是用户通过键盘输入的信息,进程的输出信息默认就显示到终端屏幕上。通过 Linux提供的重定向功能,可以实现从文件读取标准输入或将输出的内容输出到某个文件中。
- 标准输入:文件描述符:
0
文件路径:/dev/stdin
符号表示:< 或 <
0 - 标准输出:文件描述符:
1
文件路径:/dev/stdout
符号表示:> 或 >1
- 标准输入:文件描述符:
2
文件路径:/dev/stderr 符号表示:2>
ubuntu@ubuntu:~$ ls -l /dev/std*
lrwxrwxrwx 1 root root 15 10月 8 15:31 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 10月 8 15:31 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 10月 8 15:31 /dev/stdout -> /proc/self/fd/1
1. 输入重定向使用
<< 是 heredoc中的一种固定语法,后面必须跟定界符,和输入重定向无关;
例如:
wc -l < CheckEncryption.log
2. 输出重定向使用
例如: 追加内容,会覆盖以前内容
echo "12345" > 1.txt
如果文件不存在,会先创建对应的文件,如果文件存在且存在内容,会将以前的内容全部清空
例如: 追加内容,不覆盖以前内容
echo "12345" >> 1.txt
如果文件不存在,会先创建对应的文件,如果文件存在且存在内容,不会清空文件内容,而是在下一行追加
3. 错误重定向使用
例如:同时将标准输出和标准错误都输出到不同文件中
output 1> file1 2> file2
警告信息是在错误重定向中输出
例如:将标准输出和标准错误都输出到同一文件中
output 1> file1 2> file1
# &符号的作用是表示1是描述符不是文件名1
output 1> file1 2>&1
# 简写形式 &> 或 >& 等价
outpu &> file1
# 错误形式。因为此时标准输出也不知道自己应该重定向到哪个文件,故会将内容输出到屏幕
output 2>&1 > file.txt
十五:环境变量管理
1. PATH 变量
PATH变量定义了可执行文件的路径,当以相对路径执行某个命令时,会通过PATH变量定义的路径来进行查找;
通过PATH定义的路径进行查找时,不会递归查找,只会查找所指定路径下是否存在该可执行文件;
Linux中的PATH变量是一个全局变量,对所有用户和进程可见,所以系统启动时需要首先加载PATH变量,这样在才能在当前或后续的子进程中使用PATH变量。
1.1 查看PATH变量值:
ubuntu@ubuntu:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/xtrabackup-8.0.35-31/bin
1.2 临时修改PATH变量:
export PATH=$PATH:/directory
仅限于当前终端有效,只会影响当前会话及其子进程中的 PATH 变量,即环境变量的更改在用户切换或会话结束后不会保留
1.3 永久修改PATH变量:
方式一:通过在当前Shell的配置文件中进行定义,后续启动Shell进程的时候加载配置文件的同时,也会使得定义的PATH变量生效;
- 用户级配置文件:仅在当前用户下生效,对其他用户无影响。例如:PATH 修改添加到
~/.bashrc、~/.bash_profile
- 系统级配置文件:所有用户在启动 Shell 进程时都会加载这些配置文件。例如
/etc/profile 或 /etc/profile.d/
方式二:通过在/etc/environment 这个配置文件中定义PATH变量,系统初始化时会读取该文件中的内容,使其对所有用户生效。
1.4 不受PATH影响的命令
例如sudo、crontab等命令具有自己的PATH变量,默认情况下不会继承用户的 PATH。
crontab:
可以通过修改配置文件中的PATH路径来自定义crontab的PATH变量。
/etc/crontab
sudo
可以通过修改 Defaults secure_path 来修改sudo的PATH变量,或者注释掉该选项,就会默认使用当前用户的PATH。
/etc/sudoers
systemd
systemd也拥有自己的PATH变量,不会继承用户的 PATH;可以通过在service文件中添加Environment 指令来指定PATH路径;
[Service]
Environment="PATH=/custom/path:/usr/bin:/bin"
2. env 命令
env是用于查看、临时设置和管理环境变量。它可以显示当前会话中所有可用的环境变量,并且允许在临时环境中设置变量后再运行命令。
查看当前所有的环境变量:
env
临时设置一个或多个环境变量,并运行指定的命令:
env variable=value command
3. 其他变量
-
$$
获取当前进程的pid; -
$PPID
获取当前进程的父进程 pid;
十六:子Shell 和 子进程
1. 子Shell
在Shell 中通过将某些命令在小括号中执行,就会启动一个子shell来执行这个命令。子 Shell 在启动时会完全继承当前Shell的相关环境信息,例如局部变量、环境变量等。
例如:
ubuntu@ubuntu:~$ name=tom
ubuntu@ubuntu:~$ (echo $name)
tom
2. 子进程
例如使用bash这种解释器时,可以通过bash -c 来创建一个子进程,此时子进程只继承父进程的 环境变量,而不继承局部变量。
例如:
ubuntu@ubuntu:~$ name=tom
ubuntu@ubuntu:~$ export sex=m
# 不会继承定义的局部变量
ubuntu@ubuntu:~$ bash -c 'echo $name'
ubuntu@ubuntu:~$ bash -c 'echo $sex'
m
十七:Shell 通配符
Shell 提供的通配符只能用来匹配已经存在的文件名,不能用来匹配字符串;
1、星号
- 符号表示:
*
- 作用:表示任意个字符(不包括隐藏文件)
2、问号
- 符号表示:
?
- 作用:单个任意字符(中文也算一个字符)
3、中括号
- 符号表示:
[ ]
- 作用:表示匹配一范围或者其中一个