Bootstrap

Shell 脚本相关语法

文章目录


一、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中,没有直接的布尔值类型,不像编程语言那样有 truefalse 作为专门的布尔数据类型。逻辑判断依赖于命令的退出状态码,即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]:匹配数字 09 中的任意一个字符。

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

任何语句(命令块),如 forwhileif等在结束后都可以通过管道符 | 将输出传递给下一个命令。使得可以将多个命令组合在一起。

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 108 15:31 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 108 15:31 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 108 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、中括号

  • 符号表示: [ ]
  • 作用:表示匹配一范围或者其中一个
;