当前位置: 首页 > news >正文

一篇文章让你彻底掌握 Shell

大家好,这里是 Lucifer三思而后行,专注于提升数据库运维效率。

文章目录

  • 一篇文章让你彻底掌握 Shell
  • 简介
    • 什么是 shell
    • 什么是 shell 脚本
    • Shell 环境
      • 指定脚本解释器
    • 模式
      • 交互模式
      • 非交互模式
    • 基本语法
      • 解释器
      • 注释
      • echo
      • printf
        • printf 的转义符
    • 变量
      • 变量命名原则
      • 声明变量
      • 只读变量
      • 删除变量
      • 变量类型
    • 字符串
      • 单引号和双引号
      • 拼接字符串
      • 获取字符串长度
      • 截取子字符串
    • 查找子字符串
    • 数组
      • 创建数组
      • 访问数组元素
        • 访问数组的单个元素
        • 访问数组的所有元素
        • 访问数组的部分元素
      • 访问数组长度
      • 向数组中添加元素
      • 从数组中删除元素
    • 运算符
      • 算术运算符
      • 关系运算符
      • 布尔运算符
      • 逻辑运算符
      • 字符串运算符
      • 文件测试运算符
    • 控制语句
      • 条件语句
        • if
        • case
      • 循环语句
        • for 循环
        • while 循环
        • until 循环
        • select 循环
        • break 和 continue
    • 函数
      • 位置参数
      • 函数处理参数
    • Shell 扩展
      • 大括号扩展
      • 命令置换
      • 算数扩展
      • 单引号和双引号
    • 流和重定向
      • 输入、输出流
      • 重定向
      • /dev/null 文件
    • Debug
    • 往期精彩文章

一篇文章让你彻底掌握 Shell

Bash 是 Linux 标准默认的 shell 解释器,可以说 bash 是 shell 编程的基础。
本文主要介绍 bash 的语法,对于 Linux 指令不做任何介绍。

███████╗██╗  ██╗███████╗██╗     ██╗
██╔════╝██║  ██║██╔════╝██║     ██║
███████╗███████║█████╗  ██║     ██║
╚════██║██╔══██║██╔══╝  ██║     ██║
███████║██║  ██║███████╗███████╗███████╗
╚══════╝╚═╝  ╚═╝╚══════╝╚══════╝╚══════╝

简介

什么是 shell

  • Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。
  • Shell 既是一种命令语言,又是一种程序设计语言。
  • Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问 Linux 内核的服务。

Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。

什么是 shell 脚本

Shell 脚本(shell script),是一种为 shell 编写的脚本程序,一般文件后缀为 .sh

业界所说的 shell 通常都是指 shell 脚本,但 shell 和 shell script 是两个不同的概念。

Shell 环境

Shell 编程跟 java、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。

Shell 的解释器种类众多,常见的有:

  • sh - 即 Bourne Shell。sh 是 Unix 标准默认的 shell。
  • bash - 即 Bourne Again Shell。bash 是 Linux 标准默认的 shell。
  • fish - 智能和用户友好的命令行 shell。
  • xiki - 使 shell 控制台更友好,更强大。
  • zsh - 功能强大的 shell 与脚本语言。

指定脚本解释器

在 shell 脚本,#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器。#! 被称作 shebang(也称为 Hashbang )。

所以,你应该会在 shell 中,见到诸如以下的注释:

指定 sh 解释器:

#!/bin/sh

指定 bash 解释器:

#!/bin/bash

注意: 上面的指定解释器的方式是比较常见的,但有时候,你可能也会看到下面的方式:

#!/usr/bin/env bash

这样做的好处是,系统会自动在 PATH 环境变量中查找你指定的程序(本例中的 bash)。相比第一种写法,你应该尽量用这种写法,因为程序的路径是不确定的。这样写还有一个好处,操作系统的 PATH 变量有可能被配置为指向程序的另一个版本。比如,安装完新版本的 bash,我们可能将其路径添加到 PATH 中,来“隐藏”老版本。如果直接用 #!/bin/bash,那么系统会选择老版本的 bash 来执行脚本,如果用 #!/usr/bin/env bash,则会使用新版本。

模式

shell 有交互和非交互两种模式。

交互模式

简单来说,你可以将 shell 的交互模式理解为执行命令行。

看到形如下面的东西,说明 shell 处于交互模式下:

user@host:~$

接着,便可以输入一系列 Linux 命令,比如 ls,grep,cd,mkdir,rm 等等。

非交互模式

简单来说,你可以将 shell 的非交互模式理解为执行 shell 脚本。

在非交互模式下,shell 从文件或者管道中读取命令并执行。

当 shell 解释器执行完文件中的最后一个命令,shell 进程终止,并回到父进程。

可以使用下面的命令让 shell 以非交互模式运行:

sh /path/to/script.sh
bash /path/to/script.sh
source /path/to/script.sh
./path/to/script.sh

上面的例子中,script.sh 是一个包含 shell 解释器可以识别并执行的命令的普通文本文件,sh 和 bash 是 shell 解释器程序。你可以使用任何喜欢的编辑器创建 script.sh(vim,nano,Sublime Text, Atom 等等)。

其中,source /path/to/script.sh./path/to/script.sh 是等价的。

除此之外,你还可以通过 chmod 命令给文件添加可执行的权限,来直接执行脚本文件:

chmod +x /path/to/script.sh #使脚本具有执行权限
/path/to/test.sh

这种方式要求脚本文件的第一行必须指明运行该脚本的程序,比如:

💻 “示例源码”:

#!/usr/bin/env bash
echo "Hello, world!"

上面的例子中,我们使用了一个很有用的命令 echo 来输出字符串到屏幕上。

基本语法

解释器

前面虽然两次提到了 #! ,但是本着重要的事情说三遍的精神,这里再强调一遍:

在 shell 脚本,#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器。#! 被称作 shebang(也称为 Hashbang )。

#! 决定了脚本可以像一个独立的可执行文件一样执行,而不用在终端之前输入 sh, bash, python, php 等。

# 以下两种方式都可以指定 shell 解释器为 bash,第二种方式更好
#!/bin/bash
#!/usr/bin/env bash

注释

注释可以说明你的代码是什么作用,以及为什么这样写。

shell 语法中,注释是特殊的语句,会被 shell 解释器忽略。

  • 单行注释 - 以 # 开头,到行尾结束。
  • 多行注释 - 以 :<<EOF 开头,到 EOF 结束。

💻 “示例源码”:

#--------------------------------------------
# shell 注释示例
# author:zp
#--------------------------------------------# echo '这是单行注释'########## 这是分割线 ##########:<<EOF
echo '这是多行注释'
echo '这是多行注释'
echo '这是多行注释'
EOF

echo

echo 用于字符串的输出。

输出普通字符串:

echo "hello, world"
# Output: hello, world

输出含变量的字符串:

echo "hello, \"zp\""
# Output: hello, "zp"

输出含变量的字符串:

name=zp
echo "hello, \"${name}\""
# Output: hello, "zp"

输出含换行符的字符串:

# 输出含换行符的字符串
echo "YES\nNO"
#  Output: YES\nNOecho -e "YES\nNO" # -e 开启转义
#  Output:
#  YES
#  NO

输出含不换行符的字符串:

echo "YES"
echo "NO"
#  Output:
#  YES
#  NOecho -e "YES\c" # -e 开启转义 \c 不换行
echo "NO"
#  Output:
#  YESNO

输出重定向至文件:

echo "test" > test.txt

输出执行结果:

echo `pwd`
#  Output:(当前目录路径)

💻 “示例源码”:

#!/usr/bin/env bash# 输出普通字符串
echo "hello, world"
#  Output: hello, world# 输出含变量的字符串
echo "hello, \"zp\""
#  Output: hello, "zp"# 输出含变量的字符串
name=zp
echo "hello, \"${name}\""
#  Output: hello, "zp"# 输出含换行符的字符串
echo "YES\nNO"
#  Output: YES\nNO
echo -e "YES\nNO" # -e 开启转义
#  Output:
#  YES
#  NO# 输出含不换行符的字符串
echo "YES"
echo "NO"
#  Output:
#  YES
#  NOecho -e "YES\c" # -e 开启转义 \c 不换行
echo "NO"
#  Output:
#  YESNO# 输出内容定向至文件
echo "test" > test.txt# 输出执行结果
echo `pwd`
#  Output:(当前目录路径)

printf

printf 用于格式化输出字符串。

默认,printf 不会像 echo 一样自动添加换行符,如果需要换行可以手动添加 \n。

💻 “示例源码”:

# 单引号
printf '%d %s\n' 1 "abc"
#  Output:1 abc# 双引号
printf "%d %s\n" 1 "abc"
#  Output:1 abc# 无引号
printf %s abcdef
#  Output: abcdef(并不会换行)# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出
printf "%s\n" abc def
#  Output:
#  abc
#  defprintf "%s %s %s\n" a b c d e f g h i j
#  Output:
#  a b c
#  d e f
#  g h i
#  j# 如果没有参数,那么 %s 用 NULL 代替,%d 用 0 代替
printf "%s and %d \n"
#  Output:
#   and 0# 格式化输出
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
#  Output:
#  姓名     性别   体重kg
#  郭靖     男      66.12
#  杨过     男      48.65
#  郭芙     女      47.99
printf 的转义符
序列说明
\a警告字符,通常为 ASCII 的 BEL 字符
\b后退
\c抑制(不显示)输出结果中任何结尾的换行字符(只在%b 格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f换页(formfeed)
\n换行
\r回车(Carriage return)
\t水平制表符
\v垂直制表符
\\一个字面上的反斜杠字符
\ddd表示 1 到 3 位数八进制值的字符。仅在格式字符串中有效
\0ddd表示 1 到 3 位的八进制值字符

变量

跟许多程序设计语言一样,你可以在 bash 中创建变量。

Bash 中没有数据类型,bash 中的变量可以保存一个数字、一个字符、一个字符串等等。同时无需提前声明变量,给变量赋值会直接创建变量。

变量命名原则

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
  • 中间不能有空格,可以使用下划线(_)。
  • 不能使用标点符号。
  • 不能使用 bash 里的关键字(可用 help 命令查看保留关键字)。

声明变量

访问变量的语法形式为:${var}$var

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,所以推荐加花括号。

word="hello"
echo ${word}
# Output: hello

只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

rword="hello"
echo ${rword}
readonly rword
# rword="bye"  # 如果放开注释,执行时会报错

删除变量

使用 unset 命令可以删除变量。变量被删除后不能再次使用。unset 命令不能删除只读变量。

dword="hello"  # 声明变量
echo ${dword}  # 输出变量值
# Output: hellounset dword    # 删除变量
echo ${dword}
# Output: (空)

变量类型

  • 局部变量 - 局部变量是仅在某个脚本内部有效的变量。它们不能被其他的程序和脚本访问。
  • 环境变量 - 环境变量是对当前 shell 会话内所有的程序或脚本都可见的变量。创建它们跟创建局部变量类似,但使用的是 export 关键字,shell 脚本也可以定义环境变量。

常见的环境变量:

变量描述
$HOME当前用户的用户目录
$PATH用分号分隔的目录列表,shell 会到这些目录中查找命令
$PWD当前工作目录
$RANDOM0 到 32767 之间的整数
$UID数值类型,当前用户的用户 ID
$PS1主要系统输入提示符
$PS2次要系统输入提示符

这里 有一张更全面的 Bash 环境变量列表。

💻 “示例源码”:

#!/usr/bin/env bash################### 声明变量 ###################
name="world"
echo "hello ${name}"
# Output: hello world################### 输出变量 ###################
folder=$(pwd)
echo "current path: ${folder}"################### 只读变量 ###################
rword="hello"
echo ${rword}
# Output: hello
readonly rword
# rword="bye"  # 如果放开注释,执行时会报错################### 删除变量 ###################
dword="hello" # 声明变量
echo ${dword} # 输出变量值
# Output: hellounset dword # 删除变量
echo ${dword}
# Output: (空)################### 系统变量 ###################
echo "UID:$UID"
echo LOGNAME:$LOGNAME
echo User:$USER
echo HOME:$HOME
echo PATH:$PATH
echo HOSTNAME:$HOSTNAME
echo SHELL:$SHELL
echo LANG:$LANG################### 自定义变量 ###################
days=10
user="admin"
echo "$user logged in $days days age"
days=5
user="root"
echo "$user logged in $days days age"
# Output:
# admin logged in 10 days age
# root logged in 5 days age################### 从变量读取列表 ###################
colors="Red Yellow Blue"
colors=$colors" White Black"for color in $colors
doecho " $color"
done

字符串

单引号和双引号

shell 字符串可以用单引号 ‘’,也可以用双引号 “”,也可以不用引号。

  • 单引号的特点 - 单引号里不识别变量 - 单引号里不能出现单独的单引号(使用转义符也不行),但可成对出现,作为字符串拼接使用。
  • 双引号的特点 - 双引号里识别变量 - 双引号里可以出现转义字符

综上,推荐使用双引号。

拼接字符串

# 使用单引号拼接
name1='white'
str1='hello, '${name1}''
str2='hello, ${name1}'
echo ${str1}_${str2}
# Output:
# hello, white_hello, ${name1}# 使用双引号拼接
name2="black"
str3="hello, "${name2}""
str4="hello, ${name2}"
echo ${str3}_${str4}
# Output:
# hello, black_hello, black

获取字符串长度

text="12345"
echo ${#text}
# Output:
# 5

截取子字符串

text="12345"
echo ${text:2:2}
# Output:
# 34

从第 3 个字符开始,截取 2 个字符。

查找子字符串

#!/usr/bin/env bashtext="hello"
echo `expr index "${text}" ll`# Execute: ./str-demo5.sh
# Output:
# 3

查找 ll 子字符在 hello 字符串中的起始位置。

💻 “示例源码”:

#!/usr/bin/env bash################### 使用单引号拼接字符串 ###################
name1='white'
str1='hello, '${name1}''
str2='hello, ${name1}'
echo ${str1}_${str2}
# Output:
# hello, white_hello, ${name1}################### 使用双引号拼接字符串 ###################
name2="black"
str3="hello, "${name2}""
str4="hello, ${name2}"
echo ${str3}_${str4}
# Output:
# hello, black_hello, black################### 获取字符串长度 ###################
text="12345"
echo "${text} length is: ${#text}"
# Output:
# 12345 length is: 5# 获取子字符串
text="12345"
echo ${text:2:2}
# Output:
# 34################### 查找子字符串 ###################
text="hello"
echo `expr index "${text}" ll`
# Output:
# 3################### 判断字符串中是否包含子字符串 ###################
result=$(echo "${str}" | grep "feature/")
if [[ "$result" != "" ]]; thenecho "feature/ 是 ${str} 的子字符串"
elseecho "feature/ 不是 ${str} 的子字符串"
fi################### 截取关键字左边内容 ###################
full_branch="feature/1.0.0"
branch=`echo ${full_branch#feature/}`
echo "branch is ${branch}"################### 截取关键字右边内容 ###################
full_version="0.0.1-SNAPSHOT"
version=`echo ${full_version%-SNAPSHOT}`
echo "version is ${version}"################### 字符串分割成数组 ###################
str="0.0.0.1"
OLD_IFS="$IFS"
IFS="."
array=( ${str} )
IFS="$OLD_IFS"
size=${#array[*]}
lastIndex=`expr ${size} - 1`
echo "数组长度:${size}"
echo "最后一个数组元素:${array[${lastIndex}]}"
for item in ${array[@]}
doecho "$item"
done################### 判断字符串是否为空 ###################
#-n 判断长度是否非零
#-z 判断长度是否为零str=testing
str2=''
if [[ -n "$str" ]]
thenecho "The string $str is not empty"
elseecho "The string $str is empty"
fiif [[ -n "$str2" ]]
thenecho "The string $str2 is not empty"
elseecho "The string $str2 is empty"
fi#	Output:
#	The string testing is not empty
#	The string  is empty################### 字符串比较 ###################
str=hello
str2=world
if [[ $str = "hello" ]]; thenecho "str equals hello"
elseecho "str not equals hello"
fiif [[ $str2 = "hello" ]]; thenecho "str2 equals hello"
elseecho "str2 not equals hello"
fi

数组

bash 只支持一维数组。

数组下标从 0 开始,下标可以是整数或算术表达式,其值应大于或等于 0。

创建数组

# 创建数组的不同方式
nums=([2]=2 [0]=0 [1]=1)
colors=(red yellow "dark blue")

访问数组元素

访问数组的单个元素
echo ${nums[1]}
# Output: 1
访问数组的所有元素
echo ${colors[*]}
# Output: red yellow dark blueecho ${colors[@]}
# Output: red yellow dark blue

上面两行有很重要(也很微妙)的区别:

为了将数组中每个元素单独一行输出,我们用 printf 命令:

printf "+ %s\n" ${colors[*]}
# Output:
# + red
# + yellow
# + dark
# + blue

为什么 dark 和 blue 各占了一行?尝试用引号包起来:

printf "+ %s\n" "${colors[*]}"
# Output:
# + red yellow dark blue

现在所有的元素都在一行输出 —— 这不是我们想要的!让我们试试 ${colors[@]}

printf "+ %s\n" "${colors[@]}"
# Output:
# + red
# + yellow
# + dark blue

在引号内,${colors[@]}将数组中的每个元素扩展为一个单独的参数;数组元素中的空格得以保留。

访问数组的部分元素
echo ${nums[@]:0:2}
# Output:
# 0 1

在上面的例子中,${array[@]} 扩展为整个数组,:0:2 取出了数组中从 0 开始,长度为 2 的元素。

访问数组长度

echo ${#nums[*]}
# Output:
# 3

向数组中添加元素

向数组中添加元素也非常简单:

colors=(white "${colors[@]}" green black)
echo ${colors[@]}
# Output:
# white red yellow dark blue green black

上面的例子中,${colors[@]} 扩展为整个数组,并被置换到复合赋值语句中,接着,对数组 colors 的赋值覆盖了它原来的值。

从数组中删除元素

用 unset 命令来从数组中删除一个元素:

unset nums[0]
echo ${nums[@]}
# Output:
# 1 2

💻 “示例源码”:

#!/usr/bin/env bash################### 创建数组 ###################
nums=( [ 2 ] = 2 [ 0 ] = 0 [ 1 ] = 1 )
colors=( red yellow "dark blue" )################### 访问数组的单个元素 ###################
echo ${nums[1]}
# Output: 1################### 访问数组的所有元素 ###################
echo ${colors[*]}
# Output: red yellow dark blueecho ${colors[@]}
# Output: red yellow dark blueprintf "+ %s\n" ${colors[*]}
# Output:
# + red
# + yellow
# + dark
# + blueprintf "+ %s\n" "${colors[*]}"
# Output:
# + red yellow dark blueprintf "+ %s\n" "${colors[@]}"
# Output:
# + red
# + yellow
# + dark blue################### 访问数组的部分元素 ###################
echo ${nums[@]:0:2}
# Output:
# 0 1################### 获取数组长度 ###################
echo ${#nums[*]}
# Output:
# 3################### 向数组中添加元素 ###################
colors=( white "${colors[@]}" green black )
echo ${colors[@]}
# Output:
# white red yellow dark blue green black################### 从数组中删除元素 ###################
unset nums[ 0 ]
echo ${nums[@]}
# Output:
# 1 2

运算符

算术运算符

下表列出了常用的算术运算符,假定变量 x 为 10,变量 y 为 20:

运算符说明举例
+加法expr $x + $y 结果为 30。
-减法expr $x - $y 结果为 -10。
*乘法expr $x * $y 结果为 200。
/除法expr $y / $x 结果为 2。
%取余expr $y % $x 结果为 0。
=赋值x=$y 将把变量 y 的值赋给 x。
==相等。用于比较两个数字,相同则返回 true。[ $x == $y ] 返回 false。
!=不相等。用于比较两个数字,不相同则返回 true。[ $x != $y ] 返回 true。

注意:条件表达式要放在方括号之间,并且要有空格,例如: [ x = = x== x==y] 是错误的,必须写成 [ $x == $y ]。

💻 “示例源码”:

x=10
y=20echo "x=${x}, y=${y}"val=`expr ${x} + ${y}`
echo "${x} + ${y} = $val"val=`expr ${x} - ${y}`
echo "${x} - ${y} = $val"val=`expr ${x} \* ${y}`
echo "${x} * ${y} = $val"val=`expr ${y} / ${x}`
echo "${y} / ${x} = $val"val=`expr ${y} % ${x}`
echo "${y} % ${x} = $val"if [[ ${x} == ${y} ]]
thenecho "${x} = ${y}"
fi
if [[ ${x} != ${y} ]]
thenecho "${x} != ${y}"
fi#  Output:
#  x=10, y=20
#  10 + 20 = 30
#  10 - 20 = -10
#  10 * 20 = 200
#  20 / 10 = 2
#  20 % 10 = 0
#  10 != 20

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

下表列出了常用的关系运算符,假定变量 x 为 10,变量 y 为 20:

运算符说明举例
-eq检测两个数是否相等,相等返回 true。[ $a -eq $b ] 返回 false。
-ne检测两个数是否相等,不相等返回 true。[ $a -ne $b ] 返回 true。
-gt检测左边的数是否大于右边的,如果是,则返回 true。[ $a -gt $b ] 返回 false。
-lt检测左边的数是否小于右边的,如果是,则返回 true。[ $a -lt $b ] 返回 true。
-ge检测左边的数是否大于等于右边的,如果是,则返回 true。[ $a -ge $b ] 返回 false。
-le检测左边的数是否小于等于右边的,如果是,则返回 true。[ $a -le $b ] 返回 true。

💻 “示例源码”:

x=10
y=20echo "x=${x}, y=${y}"if [[ ${x} -eq ${y} ]]; thenecho "${x} -eq ${y} : x 等于 y"
elseecho "${x} -eq ${y}: x 不等于 y"
fiif [[ ${x} -ne ${y} ]]; thenecho "${x} -ne ${y}: x 不等于 y"
elseecho "${x} -ne ${y}: x 等于 y"
fiif [[ ${x} -gt ${y} ]]; thenecho "${x} -gt ${y}: x 大于 y"
elseecho "${x} -gt ${y}: x 不大于 y"
fiif [[ ${x} -lt ${y} ]]; thenecho "${x} -lt ${y}: x 小于 y"
elseecho "${x} -lt ${y}: x 不小于 y"
fiif [[ ${x} -ge ${y} ]]; thenecho "${x} -ge ${y}: x 大于或等于 y"
elseecho "${x} -ge ${y}: x 小于 y"
fiif [[ ${x} -le ${y} ]]; thenecho "${x} -le ${y}: x 小于或等于 y"
elseecho "${x} -le ${y}: x 大于 y"
fi#  Output:
#  x=10, y=20
#  10 -eq 20: x 不等于 y
#  10 -ne 20: x 不等于 y
#  10 -gt 20: x 不大于 y
#  10 -lt 20: x 小于 y
#  10 -ge 20: x 小于 y
#  10 -le 20: x 小于或等于 y

布尔运算符

下表列出了常用的布尔运算符,假定变量 x 为 10,变量 y 为 20:

运算符说明举例
!非运算,表达式为 true 则返回 false,否则返回 true。[ ! false ] 返回 true。
-o或运算,有一个表达式为 true 则返回 true。[ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a与运算,两个表达式都为 true 才返回 true。[ $a -lt 20 -a $b -gt 100 ] 返回 false。

💻 “示例源码”:

x=10
y=20echo "x=${x}, y=${y}"if [[ ${x} != ${y} ]]; thenecho "${x} != ${y} : x 不等于 y"
elseecho "${x} != ${y}: x 等于 y"
fiif [[ ${x} -lt 100 && ${y} -gt 15 ]]; thenecho "${x} 小于 100 且 ${y} 大于 15 : 返回 true"
elseecho "${x} 小于 100 且 ${y} 大于 15 : 返回 false"
fiif [[ ${x} -lt 100 || ${y} -gt 100 ]]; thenecho "${x} 小于 100 或 ${y} 大于 100 : 返回 true"
elseecho "${x} 小于 100 或 ${y} 大于 100 : 返回 false"
fiif [[ ${x} -lt 5 || ${y} -gt 100 ]]; thenecho "${x} 小于 5 或 ${y} 大于 100 : 返回 true"
elseecho "${x} 小于 5 或 ${y} 大于 100 : 返回 false"
fi#  Output:
#  x=10, y=20
#  10 != 20 : x 不等于 y
#  10 小于 100 且 20 大于 15 : 返回 true
#  10 小于 100 或 20 大于 100 : 返回 true
#  10 小于 5 或 20 大于 100 : 返回 false

逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 x 为 10,变量 y 为 20:

运算符说明举例
&&逻辑的 AND[[ ${x} -lt 100 && ${y} -gt 100 ]] 返回 false
``

💻 “示例源码”:

x=10
y=20echo "x=${x}, y=${y}"if [[ ${x} -lt 100 && ${y} -gt 100 ]]
thenecho "${x} -lt 100 && ${y} -gt 100 返回 true"
elseecho "${x} -lt 100 && ${y} -gt 100 返回 false"
fiif [[ ${x} -lt 100 || ${y} -gt 100 ]]
thenecho "${x} -lt 100 || ${y} -gt 100 返回 true"
elseecho "${x} -lt 100 || ${y} -gt 100 返回 false"
fi#  Output:
#  x=10, y=20
#  10 -lt 100 && 20 -gt 100 返回 false
#  10 -lt 100 || 20 -gt 100 返回 true

字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 “abc”,变量 b 为 “efg”:

运算符说明举例
=检测两个字符串是否相等,相等返回 true。[ $a = $b ] 返回 false。
!=检测两个字符串是否相等,不相等返回 true。[ $a != $b ] 返回 true。
-z检测字符串长度是否为 0,为 0 返回 true。[ -z $a ] 返回 false。
-n检测字符串长度是否为 0,不为 0 返回 true。[ -n $a ] 返回 true。
str检测字符串是否为空,不为空返回 true。(注:这可能是一个误解,因为str不是标准的 shell 测试运算符。正确的可能是 -n[[ ]] 测试。)[ $a ] 返回 true。

💻 “示例源码”:

x="abc"
y="xyz"echo "x=${x}, y=${y}"if [[ ${x} = ${y} ]]; thenecho "${x} = ${y} : x 等于 y"
elseecho "${x} = ${y}: x 不等于 y"
fiif [[ ${x} != ${y} ]]; thenecho "${x} != ${y} : x 不等于 y"
elseecho "${x} != ${y}: x 等于 y"
fiif [[ -z ${x} ]]; thenecho "-z ${x} : 字符串长度为 0"
elseecho "-z ${x} : 字符串长度不为 0"
fiif [[ -n "${x}" ]]; thenecho "-n ${x} : 字符串长度不为 0"
elseecho "-n ${x} : 字符串长度为 0"
fiif [[ ${x} ]]; thenecho "${x} : 字符串不为空"
elseecho "${x} : 字符串为空"
fi#  Output:
#  x=abc, y=xyz
#  abc = xyz: x 不等于 y
#  abc != xyz : x 不等于 y
#  -z abc : 字符串长度不为 0
#  -n abc : 字符串长度不为 0
#  abc : 字符串不为空

文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。

属性检测描述如下:

操作符说明举例
-b检测文件是否是块设备文件,如果是,则返回 true。[ -b $file ] 返回 false。
-c检测文件是否是字符设备文件,如果是,则返回 true。[ -c $file ] 返回 false。
-d检测文件是否是目录,如果是,则返回 true。[ -d $file ] 返回 false。
-f检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。[ -f $file ] 返回 true。
-g检测文件是否设置了 SGID 位,如果是,则返回 true。[ -g $file ] 返回 false。
-k检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。[ -k $file ] 返回 false。
-p检测文件是否是有名管道,如果是,则返回 true。[ -p $file ] 返回 false。
-u检测文件是否设置了 SUID 位,如果是,则返回 true。[ -u $file ] 返回 false。
-r检测文件是否可读,如果是,则返回 true。[ -r $file ] 返回 true。
-w检测文件是否可写,如果是,则返回 true。[ -w $file ] 返回 true。
-x检测文件是否可执行,如果是,则返回 true。[ -x $file ] 返回 true。
-s检测文件是否为空(文件大小是否大于 0),不为空返回 true。[ -s $file ] 返回 true。
-e检测文件(包括目录)是否存在,如果是,则返回 true。[ -e $file ] 返回 true。

💻 “示例源码”:

file="/etc/hosts"if [[ -r ${file} ]]; thenecho "${file} 文件可读"
elseecho "${file} 文件不可读"
fi
if [[ -w ${file} ]]; thenecho "${file} 文件可写"
elseecho "${file} 文件不可写"
fi
if [[ -x ${file} ]]; thenecho "${file} 文件可执行"
elseecho "${file} 文件不可执行"
fi
if [[ -f ${file} ]]; thenecho "${file} 文件为普通文件"
elseecho "${file} 文件为特殊文件"
fi
if [[ -d ${file} ]]; thenecho "${file} 文件是个目录"
elseecho "${file} 文件不是个目录"
fi
if [[ -s ${file} ]]; thenecho "${file} 文件不为空"
elseecho "${file} 文件为空"
fi
if [[ -e ${file} ]]; thenecho "${file} 文件存在"
elseecho "${file} 文件不存在"
fi#  Output:(根据文件的实际情况,输出结果可能不同)
#  /etc/hosts 文件可读
#  /etc/hosts 文件可写
#  /etc/hosts 文件不可执行
#  /etc/hosts 文件为普通文件
#  /etc/hosts 文件不是个目录
#  /etc/hosts 文件不为空
#  /etc/hosts 文件存在

控制语句

条件语句

跟其它程序设计语言一样,Bash 中的条件语句让我们可以决定一个操作是否被执行。结果取决于一个包在[[ ]]里的表达式。

由[[ ]](sh 中是[ ])包起来的表达式被称作 检测命令 或 基元。这些表达式帮助我们检测一个条件的结果。这里可以找到有关 bash 中单双中括号区别的答案。

共有两个不同的条件表达式:if 和 case。

if

(1)if 语句

if 在使用上跟其它语言相同。如果中括号里的表达式为真,那么 then 和 fi 之间的代码会被执行。fi 标志着条件代码块的结束。

# 写成一行
if [[ 1 -eq 1 ]]; then echo "1 -eq 1 result is: true"; fi
# Output: 1 -eq 1 result is: true# 写成多行
if [[ "abc" -eq "abc" ]]
thenecho ""abc" -eq "abc" result is: true"
fi
# Output: abc -eq abc result is: true

(2)if else 语句

同样,我们可以使用 if…else 语句,例如:

if [[ 2 -ne 1 ]]; thenecho "true"
elseecho "false"
fi
# Output: true

(3)if elif else 语句

有些时候,if…else 不能满足我们的要求。别忘了 if…elif…else,使用起来也很方便。

💻 “示例源码”:

x=10
y=20
if [[ ${x} > ${y} ]]; thenecho "${x} > ${y}"
elif [[ ${x} < ${y} ]]; thenecho "${x} < ${y}"
elseecho "${x} = ${y}"
fi
# Output: 10 < 20
case

如果你需要面对很多情况,分别要采取不同的措施,那么使用 case 会比嵌套的 if 更有用。使用 case 来解决复杂的条件判断,看起来像下面这样:

💻 “示例源码”:

exec
case ${oper} in"+")val=`expr ${x} + ${y}`echo "${x} + ${y} = ${val}";;"-")val=`expr ${x} - ${y}`echo "${x} - ${y} = ${val}";;"*")val=`expr ${x} \* ${y}`echo "${x} * ${y} = ${val}";;"/")val=`expr ${x} / ${y}`echo "${x} / ${y} = ${val}";;*)echo "Unknown oper!";;
esac

每种情况都是匹配了某个模式的表达式。|用来分割多个模式,)用来结束一个模式序列。第一个匹配上的模式对应的命令将会被执行。*代表任何不匹配以上给定模式的模式。命令块儿之间要用;;分隔。

循环语句

循环其实不足为奇。跟其它程序设计语言一样,bash 中的循环也是只要控制条件为真就一直迭代执行的代码块。

Bash 中有四种循环:

  • for
  • while
  • until
  • select
for 循环

for 与它在 C 语言中的姊妹非常像。看起来是这样:

for arg in elem1 elem2 ... elemN
do### 语句
done

在每次循环的过程中,arg 依次被赋值为从 elem1 到 elemN。这些值还可以是通配符或者大括号扩展。

当然,我们还可以把 for 循环写在一行,但这要求 do 之前要有一个分号,就像下面这样:

for i in {1..5}; do echo $i; done

还有,如果你觉得 for…in…do 对你来说有点奇怪,那么你也可以像 C 语言那样使用 for,比如:

for (( i = 0; i < 10; i++ )); doecho $i
done

当我们想对一个目录下的所有文件做同样的操作时,for 就很方便了。举个例子,如果我们想把所有的.bash 文件移动到 script 文件夹中,并给它们可执行权限,我们的脚本可以这样写:

💻 “示例源码”:

DIR=/home/zp
for FILE in ${DIR}/*.sh; domv "$FILE" "${DIR}/scripts"
done
# 将 /home/zp 目录下所有 sh 文件拷贝到 /home/zp/scripts
while 循环

while 循环检测一个条件,只要这个条件为 ,就执行一段命令。被检测的条件跟 if…then 中使用的基元并无二异。因此一个 while 循环看起来会是这样:

while [[ condition ]]
do### 语句
done

跟 for 循环一样,如果我们把 do 和被检测的条件写到一行,那么必须要在 do 之前加一个分号。

💻 “示例源码”:

### 0到9之间每个数的平方
x=0
while [[ ${x} -lt 10 ]]; doecho $((x * x))x=$((x + 1))
done
#  Output:
#  0
#  1
#  4
#  9
#  16
#  25
#  36
#  49
#  64
#  81
until 循环

until 循环跟 while 循环正好相反。它跟 while 一样也需要检测一个测试条件,但不同的是,只要该条件为 假 就一直执行循环:

💻 “示例源码”:

x=0
until [[ ${x} -ge 5 ]]; doecho ${x}x=`expr ${x} + 1`
done
#  Output:
#  0
#  1
#  2
#  3
#  4
select 循环

select 循环帮助我们组织一个用户菜单。它的语法几乎跟 for 循环一致:

select answer in elem1 elem2 ... elemN
do### 语句
done

select 会打印 elem1…elemN 以及它们的序列号到屏幕上,之后会提示用户输入。通常看到的是 $?(PS3 变量)。用户的选择结果会被保存到 answer 中。如果 answer 是一个在 1…N 之间的数字,那么语句会被执行,紧接着会进行下一次迭代 —— 如果不想这样的话我们可以使用 break 语句。

💻 “示例源码”:

#!/usr/bin/env bashPS3="Choose the package manager: "
select ITEM in bower npm gem pip
do
echo -n "Enter the package name: " && read PACKAGE
case ${ITEM} inbower) bower install ${PACKAGE} ;;npm) npm install ${PACKAGE} ;;gem) gem install ${PACKAGE} ;;pip) pip install ${PACKAGE} ;;
esac
break # 避免无限循环
done

这个例子,先询问用户他想使用什么包管理器。接着,又询问了想安装什么包,最后执行安装操作。

运行这个脚本,会得到如下输出:

$ ./my_script
1) bower
2) npm
3) gem
4) pip
Choose the package manager: 2
Enter the package name: gitbook-cli
break 和 continue

如果想提前结束一个循环或跳过某次循环执行,可以使用 shell 的 break 和 continue 语句来实现。它们可以在任何循环中使用。

break 语句用来提前结束当前循环。

continue 语句用来跳过某次迭代。

💻 “示例源码”:

# 查找 10 以内第一个能整除 2 和 3 的正整数
i=1
while [[ ${i} -lt 10 ]]; doif [[ $((i % 3)) -eq 0 ]] && [[ $((i % 2)) -eq 0 ]]; thenecho ${i}break;fii=`expr ${i} + 1`
done
# Output: 6

💻 “示例源码”:

# 打印10以内的奇数
for (( i = 0; i < 10; i ++ )); doif [[ $((i % 2)) -eq 0 ]]; thencontinue;fiecho ${i}
done
#  Output:
#  1
#  3
#  5
#  7
#  9

函数

bash 函数定义语法如下:

[ function ] funname [()] {action;[return int;]
}

💡 说明:
函数定义时,function 关键字可有可无。
函数返回值 - return 返回函数返回值,返回值类型只能为整数(0-255)。如果不加 return 语句,shell 默认将以最后一条命令的运行结果,作为函数返回值。
函数返回值在调用该函数后通过 $? 来获得。
所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 shell 解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。

💻 “示例源码”:

#!/usr/bin/env bashcalc(){PS3="choose the oper: "select oper in + - \* / # 生成操作符选择菜单doecho -n "enter first num: " && read x # 读取输入参数echo -n "enter second num: " && read y # 读取输入参数execcase ${oper} in"+")return $((${x} + ${y}));;"-")return $((${x} - ${y}));;"*")return $((${x} * ${y}));;"/")return $((${x} / ${y}));;*)echo "${oper} is not support!"return 0;;esacbreakdone
}
calc
echo "the result is: $?" # $? 获取 calc 函数返回值

执行结果:

$ ./function-demo.sh
1) +
2) -
3) *
4) /
choose the oper: 3
enter first num: 10
enter second num: 10
the result is: 100

位置参数

位置参数是在调用一个函数并传给它参数时创建的变量。

位置参数变量表:

变量描述
$0脚本名称
$1 … $9第 1 个到第 9 个参数列表
${10} … ${N}第 10 个到 N 个参数列表(N 是一个正整数)
$*$@除了 $0 外的所有位置参数
$#不包括 $0 在内的位置参数的个数
$FUNCNAME函数名称(仅在函数内部有值)

💻 “示例源码”:

#!/usr/bin/env bashx=0
if [[ -n $1 ]]; thenecho "第一个参数为:$1"x=$1
elseecho "第一个参数为空"
fiy=0
if [[ -n $2 ]]; thenecho "第二个参数为:$2"y=$2
elseecho "第二个参数为空"
fiparamsFunction(){echo "函数第一个入参:$1"echo "函数第二个入参:$2"
}
paramsFunction ${x} ${y}

执行结果:

$ ./function-demo2.sh
第一个参数为空
第二个参数为空
函数第一个入参:0
函数第二个入参:0$ ./function-demo2.sh 10 20
第一个参数为:10
第二个参数为:20
函数第一个入参:10
函数第二个入参:20

执行 ./variable-demo4.sh hello world ,然后在脚本中通过 $1$2 … 读取第 1 个参数、第 2 个参数。。。

函数处理参数

另外,还有几个特殊字符用来处理参数:

参数处理说明
$#返回参数个数
$*返回所有参数
$$脚本运行的当前进程 ID 号
$!后台运行的最后一个进程的 ID 号
$@返回所有参数(与 $* 相同)
$-返回 Shell 使用的当前选项,与 set 命令功能相同。
$?函数返回值(或上一个命令的退出状态)

💻 “示例源码”:

runner() {return 0
}name=zp
paramsFunction(){echo "函数第一个入参:$1"echo "函数第二个入参:$2"echo "传递到脚本的参数个数:$#"echo "所有参数:"printf "+ %s\n" "$*"echo "脚本运行的当前进程 ID 号:$$"echo "后台运行的最后一个进程的 ID 号:$!"echo "所有参数:"printf "+ %s\n" "$@"echo "Shell 使用的当前选项:$-"runnerecho "runner 函数的返回值:$?"
}
paramsFunction 1 "abc" "hello, \"zp\""
#  Output:
#  函数第一个入参:1
#  函数第二个入参:abc
#  传递到脚本的参数个数:3
#  所有参数:
#  + 1 abc hello, "zp"
#  脚本运行的当前进程 ID 号:26400
#  后台运行的最后一个进程的 ID 号:
#  所有参数:
#  + 1
#  + abc
#  + hello, "zp"
#  Shell 使用的当前选项:hB
#  runner 函数的返回值:0

Shell 扩展

扩展 发生在一行命令被分成一个个的 记号(tokens) 之后。换言之,扩展是一种执行数学运算的机制,还可以用来保存命令的执行结果,等等。

感兴趣的话可以阅读关于 shell 扩展的更多细节。

大括号扩展

大括号扩展让生成任意的字符串成为可能。它跟 文件名扩展 很类似,举个例子:

echo beg{i,a,u}n ### begin began begun

大括号扩展还可以用来创建一个可被循环迭代的区间。

echo {0..5} ### 0 1 2 3 4 5
echo {00..8..2} ### 00 02 04 06 08

命令置换

命令置换允许我们对一个命令求值,并将其值置换到另一个命令或者变量赋值表达式中。当一个命令被``或 $()包围时,命令置换将会执行。举个例子:

now=`date +%T`
### or
now=$(date +%T)echo $now ### 19:08:26

算数扩展

在 bash 中,执行算数运算是非常方便的。算数表达式必须包在 $(( ))中。算数扩展的格式为:

result=$(( ((10 + 5*3) - 7) / 2 ))
echo $result ### 9

在算数表达式中,使用变量无需带上 $ 前缀:

x=4
y=7
echo $(( x + y ))     ### 11
echo $(( ++x + y++ )) ### 12
echo $(( x + y ))     ### 13

单引号和双引号

单引号和双引号之间有很重要的区别。在双引号中,变量引用或者命令置换是会被展开的。在单引号中是不会的。举个例子:

echo "Your home: $HOME" ### Your home: /Users/<username>
echo 'Your home: $HOME' ### Your home: $HOME

当局部变量和环境变量包含空格时,它们在引号中的扩展要格外注意。随便举个例子,假如我们用 echo 来输出用户的输入:

INPUT="A string  with   strange    whitespace."
echo $INPUT   ### A string with strange whitespace.
echo "$INPUT" ### A string  with   strange    whitespace.

调用第一个 echo 时给了它 5 个单独的参数 —— $INPUT 被分成了单独的词,echo 在每个词之间打印了一个空格。第二种情况,调用 echo 时只给了它一个参数(整个 $INPUT 的值,包括其中的空格)。

来看一个更严肃的例子:

FILE="Favorite Things.txt"
cat $FILE   ### 尝试输出两个文件: `Favorite` 和 `Things.txt`
cat "$FILE" ### 输出一个文件: `Favorite Things.txt`

尽管这个问题可以通过把 FILE 重命名成 Favorite-Things.txt 来解决,但是,假如这个值来自某个环境变量,来自一个位置参数,或者来自其它命令(find, cat, 等等)呢。因此,如果输入 可能 包含空格,务必要用引号把表达式包起来。

流和重定向

Bash 有很强大的工具来处理程序之间的协同工作。使用流,我们能将一个程序的输出发送到另一个程序或文件,因此,我们能方便地记录日志或做一些其它我们想做的事。

管道给了我们创建传送带的机会,控制程序的执行成为可能。

学习如何使用这些强大的、高级的工具是非常非常重要的。

输入、输出流

Bash 接收输入,并以字符序列或 字符流 的形式产生输出。这些流能被重定向到文件或另一个流中。

有三个文件描述符:

代码描述符描述
0stdin标准输入
1stdout标准输出
2stderr标准错误输出

重定向

重定向让我们可以控制一个命令的输入来自哪里,输出结果到什么地方。这些运算符在控制流的重定向时会被用到:

OperatorDescription
>重定向输出
&>重定向输出和错误输出
&>>以附加的形式重定向输出和错误输出
<重定向输入
<<Here 文档语法
<<<Here 字符串

以下是一些使用重定向的例子:

### ls的结果将会被写到list.txt中
ls -l > list.txt### 将输出附加到list.txt中
ls -a >> list.txt### 所有的错误信息会被写到errors.txt中
grep da * 2> errors.txt### 从errors.txt中读取输入
less < errors.txt

/dev/null 文件

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:

$ command > /dev/null

/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到”禁止输出”的效果。

如果希望屏蔽 stdout 和 stderr,可以这样写:

$ command > /dev/null 2>&1

Debug

shell 提供了用于 debug 脚本的工具。

如果想采用 debug 模式运行某脚本,可以在其 shebang 中使用一个特殊的选项:

#!/bin/bash options

options 是一些可以改变 shell 行为的选项。下表是一些可能对你有用的选项:

ShortNameDescription
-fnoglob禁止文件名展开(globbing)
-iinteractive让脚本以交互模式运行
-nnoexec读取命令,但不执行(语法检查)
-t执行完第一条命令后退出
-vverbose在执行每条命令前,向 stderr 输出该命令
-xxtrace在执行每条命令前,向 stderr 输出该命令以及该命令的扩展参数

举个例子,如果我们在脚本中指定了 -x 例如:

#!/bin/bash -xfor (( i = 0; i < 3; i++ )); doecho $i
done

这会向 stdout 打印出变量的值和一些其它有用的信息:

$ ./my_script
+ (( i = 0 ))
+ (( i < 3 ))
+ echo 0
0
+ (( i++  ))
+ (( i < 3 ))
+ echo 1
1
+ (( i++  ))
+ (( i < 3 ))
+ echo 2
2
+ (( i++  ))
+ (( i < 3 ))

有时我们值需要 debug 脚本的一部分。这种情况下,使用 set 命令会很方便。这个命令可以启用或禁用选项。使用-启用选项,+禁用选项:

💻 “示例源码”:

# 开启 debug
set -x
for (( i = 0; i < 3; i++ )); doprintf ${i}
done
# 关闭 debug
set +x
#  Output:
#  + (( i = 0 ))
#  + (( i < 3 ))
#  + printf 0
#  0+ (( i++  ))
#  + (( i < 3 ))
#  + printf 1
#  1+ (( i++  ))
#  + (( i < 3 ))
#  + printf 2
#  2+ (( i++  ))
#  + (( i < 3 ))
#  + set +xfor i in {1..5}; do printf ${i}; done
printf "\n"
#  Output: 12345

往期精彩文章

Oracle 一键巡检自动生成 Word 报告
Oracle 一键安装合集
Oracle一键安装脚本的 21 个疑问与解答
Oracle一键巡检脚本的 21 个疑问与解答
全网首发:Oracle 23ai 一键安装脚本(非 RPM)
Oracle 19C 最新 RU 补丁 19.24 ,一键安装!
Oracle Linux 7.9 一键安装 Oracle 19C
RedHat 9.4(aarch64) 一键安装 Oracle 19C
openEuler 22.03 LTS SP4 一键安装 Oracle 19C RAC
RHEL 7.9 一键安装 Oracle 19C 19.23 RAC
Oracle DataGuard GAP 修复手册
优化 Oracle:最佳实践与开发规范
DBA 必备:Linux 软件源配置全攻略
Linux 一键配置时钟同步全攻略


感谢您的阅读,这里是 Lucifer三思而后行,欢迎点赞+关注,我会持续分享数据库知识、运维技巧。

相关文章:

一篇文章让你彻底掌握 Shell

大家好&#xff0c;这里是 Lucifer三思而后行&#xff0c;专注于提升数据库运维效率。 文章目录 一篇文章让你彻底掌握 Shell简介什么是 shell什么是 shell 脚本Shell 环境指定脚本解释器 模式交互模式非交互模式 基本语法解释器注释echoprintfprintf 的转义符 变量变量命名原则…...

Java中的Collection集合:深入理解与应用

在Java中&#xff0c;Collection接口是Java集合框架&#xff08;Java Collections Framework&#xff09;的基石之一&#xff0c;它定义了一系列操作对象集合的方法&#xff0c;如添加、删除、遍历等。Collection接口是List、Set和Queue等具体集合类型的父接口&#xff0c;为Ja…...

Kubernetes-K8S

Kubernetes由于单词太长&#xff0c;省略掉中间8个字母简称为K8S。它介于应用服务和服务器之间。能够通过策略协调和管理多个服务&#xff0c;只需要一个YAML文件配置。定义应用的部署顺序等信息&#xff0c;自动部署应用到各个服务器&#xff0c;还可以自动扩容缩容。 架构原理…...

简化文本处理流程,通用文字识别助力提升信息采集效率

随着信息技术的发展、移动设备使用的普及和全球化的商业需求&#xff0c;非结构化数据转换为结构化数据的需求日益增长&#xff0c;数字化成为信息存储和管理的主流趋势。在此背景下&#xff0c;OCR技术应运而生&#xff0c;该技术可以将图像中文本信息转化为计算机等设备可以使…...

【网络】TCP协议通信的重要策略——滑动窗口,快重传,流量控制,拥塞控制,延时应答

目录 MSS值 滑动窗口 滑动窗口与重发机制 快重传机制 滑动窗口与流量控制 滑动窗口与拥塞控制 延时应答 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 相关文章 【网络】传输层TCP协议的报头和传输机制-CSDN博客 【网络】详解TCP协议通信时客户/服务端的状态-CSDN博…...

极狐GitLab CI/CD 如何构建镜像并推送到 azure 镜像仓库?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门面向中国程序员和企业提供企业级一体化 DevOps 平台&#xff0c;用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规&#xff0c;而且所有的操作都是在一个平台上进行&#xff0c;省事省心省钱。可以一键安装极狐GitL…...

Leetcode—1143. 最长公共子序列【中等】

2024每日刷题&#xff08;155&#xff09; Leetcode—1143. 最长公共子序列 实现代码 class Solution { public:int longestCommonSubsequence(string text1, string text2) {int m text1.length();int n text2.length();vector<vector<int>> dp(m 1, vector&…...

ZBrush笔刷介绍

根据使用的方法和效果&#xff0c;ZBrush的笔刷可以分类如下&#xff1a; 基本功能笔刷 这些笔刷用于常规的雕刻、移动和平滑操作。 纹理应用笔刷 一般需要自己额外购买的是这三种笔刷 Alpha Brushes&#xff1a;使用灰度图&#xff08;alpha&#xff09;来定义笔刷形状和纹…...

React+AntDesign做一个日历,展示节假日,节气,并且在某几个时间上添加活动备注

直接贴效果图&#x1f604; 首先日历是用的AntDesign提供的Calendar组件&#xff0c;这个组件还是蛮强大的&#xff0c;可以自定义头部时间下拉&#xff1b;渲染每个时间段&#xff0c;或者重置时间段内容&#xff0c;玩的空间是很大的 直接贴代码&#xff0c;结尾最后我会将…...

排序算法之梳排序

title: 梳排序 date: 2024-7-30 14:46:27 0800 categories: 排序算法 tags:排序算法梳排序 description: 梳排序&#xff08;Comb Sort&#xff09;是一种由弗拉基米尔多博舍维奇&#xff08;Wlodzimierz Dobosiewicz&#xff09;于1980年所发明的不稳定排序算法&#xff0c;并…...

ESP8266 创建TCP连接

TCP Client 使用WiFiClient类可以实现TCP Client 基本方法 连接Server&#xff0c;connect WiFiClient client; client.connect(host, port) 检测客户端是否存在数据流 client.available() 读取客户端的一个字符 client.read(); 检查连接状态 client.connected() 使用…...

OceanBase内存管理小窍门

本文来自OceanBase热心用户的实践分享。 本文主要是对OceanBase内存管理的实用技巧分享&#xff0c;而并非直接深入OceanBase的代码层面进行阐述。​​​​​​​ 阅读本文章你将了解&#xff1a; 重载运算符new 与malloc在返回值上区别&#xff1f;在ceph 双向链表新用法&am…...

【问题解决】git status中文文件名乱码

问题复现 解决办法 在git bash中直接执行如下命令 git config --global core.quotepath false原因 通过 git config --help 可以查看到以下内容&#xff1a; core.quotePath Commands that output paths (e.g. ls-files, diff), will quote “unusual” characters in the p…...

探索数据结构:AVL树的分析与实现

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. AVL树的介绍 在前面我们学习二叉搜索树时知道&#xff0c;在数据有序…...

使用 C++ 实现简单的插件系统

使用 C 实现简单的插件系统 在现代软件开发中&#xff0c;插件系统是一种常见的架构模式&#xff0c;它允许开发者在不修改主程序的情况下&#xff0c;扩展应用程序的功能。通过插件&#xff0c;用户可以根据需要添加或移除功能模块&#xff0c;从而提高软件的灵活性和可维护性…...

使用Python创建省份城市地图选择器

在这篇博客中&#xff0c;我们将探讨如何使用Python创建一个简单而实用的省份城市地图选择器。这个项目不仅能帮助我们学习Python的基础知识&#xff0c;还能让我们了解如何处理JSON数据和集成网页浏览器到桌面应用程序中。 C:\pythoncode\new\geographicgooglemap.py 全部代码…...

【Java 数据结构】Stack和Queue介绍

Stack和Queue StackStack是什么Stack的使用构造方法常用方法 栈的模拟实现初始化和基本方法入栈出栈查看栈顶 栈的应用链栈的简单介绍 QueueQueue是什么Queue的使用队列的模拟实现初始化入队出队查看队头元素 循环队列循环队列的定义及其注意点循环队列的实现初始化和基本方法获…...

Docker基本语法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、更新yum镜像仓库&#xff08;一&#xff09;查看本地yum镜像源地址&#xff08;二&#xff09;设置docker的镜像仓库&#xff08;1&#xff09;安装必要工具…...

uniapp 对于scroll-view滑动和页面滑动的联动处理

需求 遇到一个需求 解决方案 这个时候可以做一个内页面滑动判断 <!-- scroll-y 做true或者false的判断是否滑动 --> <view class"u-menu-wrap" style"background-color: #fff;"><scroll-view :scroll-y"data.isGo" scroll-wit…...

opencv基础的图像操作

1.读取图像&#xff0c;显示图像&#xff0c;保存图像 #图像读取、显示与保存 import numpy as np import cv2 imgcv2.imread(./src/1.jpg) #读取 cv2.imshow("img",img) #显示 cv2.imwrite("./src/2.jpg",img) #保存 cv2.waitKey(0) #让程序进入主循环(让…...

Java | Leetcode Java题解之第337题打家劫舍III

题目&#xff1a; 题解&#xff1a; class Solution {public int rob(TreeNode root) {int[] rootStatus dfs(root);return Math.max(rootStatus[0], rootStatus[1]);}public int[] dfs(TreeNode node) {if (node null) {return new int[]{0, 0};}int[] l dfs(node.left);i…...

本地查看的Git远程仓库分支与远程仓库分支数量不一致

说明&#xff1a;一次&#xff0c;在IDEA中想切换到某分支&#xff0c;但是查看Remote没有找到要切换的分支&#xff0c;但是打开GitLab&#xff0c;查看远程仓库&#xff0c;是有这个分支的。 解决&#xff1a;1&#xff09;在IDEA的Git中&#xff0c;点下面Fatch获取一下远程…...

opencv-python实战项目九:基于拉普拉斯金字塔的图像融合

文章目录 一&#xff0c;简介&#xff1a;二&#xff0c;拉普拉斯金字塔介绍&#xff1a;三&#xff0c;算法实现步骤3.1 构建融合拉普拉斯金字塔3.2 融合后的拉普拉斯金字塔复原&#xff1a; 四&#xff0c;整体代码实现&#xff1a;五&#xff0c;效果&#xff1a; 一&#x…...

浅谈JDK

JDK(Java Development Kit) JDK是Java开发工具包&#xff0c;是Java编程语言的核心软件开发工具。 JDK包含了一系列用于开发、编译和运行Java应用程序的工具和资源。其中包括&#xff1a; 1.Java编译器&#xff08;javac&#xff09;&#xff1a;用于将Java源代码编译成字节…...

爬虫案例3——爬取彩票双色球数据

简介&#xff1a;个人学习分享&#xff0c;如有错误&#xff0c;欢迎批评指正 任务&#xff1a;从500彩票网中爬取双色球数据 目标网页地址&#xff1a;https://datachart.500.com/ssq/ 一、思路和过程 目标网页具体内容如下&#xff1a; ​​​​​ 我们的任务是将上图中…...

C++ | Leetcode C++题解之第337题打家劫舍III

题目&#xff1a; 题解&#xff1a; struct SubtreeStatus {int selected;int notSelected; };class Solution { public:SubtreeStatus dfs(TreeNode* node) {if (!node) {return {0, 0};}auto l dfs(node->left);auto r dfs(node->right);int selected node->val…...

软件架构设计师-UML知识导图

软件架构设计师-UML知识导图&#xff0c;包含如下内容&#xff1a; 结构化设计&#xff0c;包含结构化设计的概念、结构化设计的主要内容、概要设计、详细设计及模块设计原则&#xff1b;UML是什么&#xff1a;介绍UML是什么&#xff1b;UML的结构&#xff1a;构造块、公共机制…...

在使用transformers和pytorch时出现的版本冲突的问题

在使用transformers和torch库的时候&#xff0c;出现了以下问题&#xff1a; 1、OSError: [WinError 126] 找不到指定的模块。 Error loading "D:\Program Files\anaconda3\envs\testenv\Lib\site-packages\torch\lib\fbgemm.dll" or one of its dependencies. 2、…...

uniapp粘贴板地址识别

1&#xff1a; 插件安装 主要是依靠 address-parse 这个插件&#xff1a; 官网 收货地址自动识别 支持pc、h5、微信小程序 - DCloud 插件市场 // 首先需要引入插件 npm install address-parse --save 2&#xff1a;html部分 <view class""><view class&quo…...

C语言 | Leetcode C语言题解之第335题路径交叉

题目&#xff1a; 题解&#xff1a; bool isSelfCrossing(int* distance, int distanceSize){if (distance NULL || distanceSize < 4) {return false;}for (int i 3; i < distanceSize; i) {if ((distance[i] > distance[i - 2]) && (distance[i - 1] &l…...

国内设计师个人网站欣赏/百度提交网站

HCNP要复习多久&#xff1f; HCNP我们现在都成为HCIP&#xff0c;华为认证资深级别的考试&#xff0c;小伙伴们对于它的学习都是有很多疑惑的&#xff0c;包括学习的时间到底需要多久&#xff0c;从开始学&#xff0c;要多久才能拿到证书&#xff1f;对于没有基础的小伙伴来说&…...

服务器搭建网站能ping t/抖音seo招商

题目链接 数据过大&#xff0c;需要用字符串处理数据。 再一位一位进行除法&#xff0c;注意余数。 #include <stdio.h> int main(){char a[1001];int h[1001];int b, c, d,e,f,g;scanf("%s %d", a, &b);d 0;for (c 0; a[c] ! \0; c) {d ((a[c] - 48) …...

推荐网站在线看兄弟们/广州疫情最新动态

2019独角兽企业重金招聘Python工程师标准>>> 一、框架原理 sqlMapConfig.xml&#xff1a;(是mybatis的全局配置文件&#xff0c;名称不固定的)配置了数据源、事务等mybatis运行环境 mapper.xml&#xff1a;配置sql语句 SqlSessionFactory&#xff1a;&#xff08;会…...

织梦网站必须下载/google搜索引擎下载

对MySQL数据进行备份&#xff0c;常见的方式如以下三种&#xff0c;可能有很多人对备份时数据一致性并不清楚 1、直接拷贝整个数据目录下的所有文件到新的机器。优点是简单、快速&#xff0c;只需要拷贝&#xff1b;缺点也很明显&#xff0c;在整个备份过程中新机器处于完全不…...

好的wordpress主题/网店代运营公司靠谱吗

当我们使用类型number的input输入框的时候&#xff0c;我们可能需要限制输入的位数&#xff0c;这个时候通常会想到maxlength&#xff0c;但是maxlength是在number类型的时候是不支持的&#xff0c;下面是一些解决这种问题的方法。1&#xff09;max和min max和min是number输入框…...

佛山新网站建设平台/推广注册app拿佣金

这几天搞Windows离线断网环境下安装Python包&#xff0c;配置环境&#xff0c;各种坑&#xff01;做个记录&#xff0c;供以后查询吧。# 生产环境 windows 7# python 2.7.9# pip 1.5.2友情提示&#xff1a;当你遇到无法安装包的不明错误时&#xff0c;可以回头来考虑如下建议了…...