赞
踩
:::danger
**推荐学习网站 **https://xukai.work/
中文版Bash脚本:https://linuxstory.gitbook.io/advanced-bash-scripting-guide-in-chinese/
:::
:::warning
参考文章:https://zhuanlan.zhihu.com/p/452686607
:::
Shell是一种命令解释器,它不仅分离了用户层与操作系统内核,更是一门强大的编程语言。我们称为shell编写的程序为脚本(script)。脚本是一种易于使用的工具,它能够将系统调用、工具软件、实用程序(utility)和已编译的二进制文件联系在一起构建程序。实际上,shell脚本可以调用所有的UNIX命令、实用程序以及工具软件。如果你觉得这还不够,使用像test命令和循环结构这样的shell内建命令能够让脚本更加灵活强大。Shell脚本特别适合完成系统管理任务和那些不需要复杂结构性语言实现的重复工作。
在shell中,每个脚本的开头都使用 #! ,就是告知系统文件的执行都需要指定一个解释器。指定一个文件类型的特殊标记。占用 2 字节 。
shell脚本都是以 **#! **开头,告知系统该文件的执行需要一个解释器。 常见的解释器类型如下:
#!/bin/sh
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/tcl
#!/bin/sed -f
#!/usr/awk -f
解释说明
执行脚本的三种方式:
#方式1
sh helloworld.sh
#方式2
bash helloworld.sh
bash +x helloworld.sh
第三种方式有一点特殊
./helloworld.sh #需给文件授予执行权限
#实授予权限的方式如下
chmod +x helloworld.sh #授予可执行权限
chmod +rx helloworld.sh #授予任何人可执行可读和可执行权限
chmod u+rx helloworld.sh #只给脚本的所有者可读和可执行权限
#!/bin/bash
echo "helloworld"
目前Linux/unix系统中,普遍的shell脚本的第一行是:#!/bin/sh 或者 #!/bin/bash。
#!/bin/bash
echo "全部参数:" $@
echo "命令行参数数量:" $#
echo '$0 = ' $0
echo '$1 = ' $1
echo '$2 = ' $2
echo '$3 = ' $3
运行
chmod u+rx script.sh #修改权限
./script.sh a b c #执行脚本
用户可以输入任意数量的参数,利用for循环,可以读取每一个参数。
#!bin/bash
# for循环来读取输入的参数
for i in "$@"; #$@返回一个全部参数的列表,然后使用for循环遍历
do echo $i
done
使用“ ”包括起来的命令会被认为是一个命令
**注释 :**Bash 脚本中,以#开头的行就是注释,会被解释器忽略。
# 本行是注释
echo 'Hello World!'
echo 'Hello World!' #是注释
多行注释还可以使用以下格式:
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
EOF也可以使用其他符号:
:<<'
注释内容...
注释内容...
注释内容...
'
:<<!
注释内容...
注释内容...
注释内容...
!
命令执行结束后,会有一个返回值。0表示执行成功,非0(通常是1)表示执行失败。环境变量$?可以读取前一个命令的返回值。利用这一点,可以在脚本中对命令执行结果进行判断。
cd /path/to/somewhere
if [ "$?" = 0 ]; then
rm *
else
echo "无法切换目录!" 1>&2
exit 1
fi
cd /path/to/somewhere这个命令如果执行成功(返回值等于0),就删除该目录里面的文件,否则退出脚本,整个脚本的返回值变为1,表示执行失败。
由于if可以直接判断命令的执行结果,执行相应的操作,上面的脚本可以改写成下面的样子。
if cd /path/to/somewhere; then
rm *
else
echo "Could not change directory! Aborting." 1>&2
exit 1
fi
更简洁的写法是利用两个逻辑运算符&&(且)和 ||(或)。
# 第一步执行成功,才会执行第二步
cd /path/to/somewhere && rm *
# 第一步执行失败,才会执行第二步
cd /path/to/somewhere || exit 1
echo 通常用于 shell 脚本中,用于显示消息或输出其他命令的结果。
$ echo [-neE] [ARGUMENTS]
:::danger
echo "Hello, World!"
Hello, World!
若要打印双引号,请将其包含在单引号内,或用反斜杠字符进行转义。
$ echo "hello"
hello
$ echo 'hello "hel"'
hello "hel"
模式匹配字符
echo 命令可以与模式匹配字符一起使用,比如通配符。 例如,下面的命令将返回所有。 工作目录中的 php 文件。
$ echo The PHP files are: *.php
The PHP files are: index.php contact.php functions.php2
显示变量
echo 还可以显示变量。在下面的示例中,我们将输出当前登录用户的名称:
$ echo $USER #$USER 是一个保存用户名的 shell 变量。
admin
显示命令的输出
使用 $(command)表达式将命令输出包含在 echo 的参数中。
$ echo "The date is: $(date +%D)"
The date is: 04/01/20
$ echo "Today is : $(date +%D)"
Today is : 07/13/23
$ echo "Today is : $(date +%Y%m%d-%H%M%S)"
Today is : 20230713-145600
$ echo "Today is : $(date +%Y-%m-%d-%H:%M:%S)"
Today is : 2023-07-13-14:56:17
env命令总是指向/usr/bin/env文件,或者说,这个二进制文件总是在目录/usr/bin。
:::danger
'!#/usr/bin/env NAME '这个语法的意思是,让 Shell 查找$PATH环境变量里面第一个匹配的NAME。如果不知道某个命令的具体路径,或者希望兼容其他用户的机器,这样的写法就很有用。
env
命令的参数如下。
i
, -ignore-environment
:不带环境变量启动。u
, -unset=NAME
:从环境变量中删除一个变量。-help
:显示帮助。-version
:输出版本信息。$ env -i /bin/sh
在shell中执行程序时,shell会提供一组环境变量。export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅及于该次登陆操作。
语法
export [-fnp][变量名称]=[变量设置值]
-f 代表[变量名称]中为函数名称。
-n 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
-p 列出所有的shell赋予程序的环境变量。
export -p 等同于 declare -x
export AA=2
export -p | grep AA
declare -x AA="2"
3. 更改变量的值
export aa=5
export -p | grep aa
declare -x aa="5"
export USER=LII
echo $USER #LII
只在当前终端里有效,当退出后就重新恢复。
unset aa
export -p | grep aa
cat test.sh
export aaa=1
echo $aaa
source test.sh
1
export -p | grep aaa
declare -x aaa="1"
/export.sh
export USER=LIU
/source.sh
source ./export.sh #LIU
shift命令可以改变脚本参数,每次执行都会移除脚本当前的第一个参数($1),使得后面的参数向前一位,即$2变成$1、$3变成$2、$4变成$3,以此类推。
while循环结合shift命令,也可以读取每一个参数。
#!/bin/bash
echo "一共输入了 $# 个参数"
while [ "$1" != "" ]; do
echo "剩下 $# 个参数"
echo "参数: $1"
shift #shift指令,每次都会移除第一个参数,从而循环遍历所有输入参数
done
shift命令可以接受一个整数作为参数,指定所要移除的参数个数,默认为1。
shift 3
上面的命令移除前三个参数,原来的$4变成$1。
注意:
如果shift的参数数量超过了原输入的参数个数,那么就会一直循环输出,而不停止。例如,将上面代码中的shift改成shift 20。重新运行,会发现一直在循环。
shift 20 || break #需加入break
echo "一共输入了 $# 个参数"
while [ "$1" != "" ]; do #可以没有[]
echo "剩下 $# 个参数"
echo "参数: $1"
shift 20 || break #shift指令,每次都会移除第一个参数,从而循环遍历所有输入参数
done
getopts命令用在脚本内部,可以解析复杂的脚本命令行参数,通常与while循环一起使用,取出脚本所有的带有前置连词线(-)的参数。
getopts optstring name
:::danger
它带有两个参数。
#!/bin/bash while getopts 'lha:' OPTION ; do case "$OPTION" in l) echo "linux config" ;; h) echo "h stands for h" ;; a) avalue="$OPTARG" echo "The value provided is $OPTARG" ;; ?) echo "script usage: $(basename $0) [-l] [-h] [-a somevalue]" >&2 exit 1 ;; esac done shift "$((($OPTIND - 1)))"
上面例子中,while循环不断执行getopts ‘lha:’ OPTION命令,每次执行就会读取一个连词线参数(以及对应的参数值),然后进入循环体。变量OPTION保存的是,当前处理的那一个连词线参数(即l、h或a)。如果用户输入了没有指定的参数(比如-x),那么OPTION等于?。循环体内使用case判断,处理这四种不同的情况。
:::danger
-和–开头的参数,会被 Bash 当作配置项解释。但是,有时它们不是配置项,而是实体参数的一部分,比如文件名叫做f或–file。
$ cat -f
$ cat --file
上面命令的原意是输出文件-f和–file的内容,但是会被 Bash 当作配置项解释。
**这时就可以使用配置项参数终止符–,告诉 Bash,在它后面的参数开头的-和–不是配置项,只能当作实体参数解释。 **
$ cat -- -f
$ cat -- --file
** 如果要确保某个变量不会被当作配置项解释,就要在它前面放上参数终止符–。 **
$ ls -- $myPath #--强制变量$myPath只能当作实体参数(即路径名)解释
如果变量不是路径名,就会报错:
$ myPath="-l"
$ ls -- $myPath
ls: 无法访问'-l': 没有那个文件或目录
如果想在文件里面搜索–hello,这时也要使用参数终止符–。
$ grep --hello example.txt //报错,没有--hello
$ grep -- hello example.txt //寻找hello
declare命令可以声明一些特殊类型的变量,为变量设置一些限制,比如声明只读类型的变量和整数类型的变量:
declare OPTION VARIABLE=value
:::danger
declare
命令的主要参数(OPTION)如下:
-a
:声明数组变量。
-f
:输出所有函数定义。
-F
:输出所有函数名。
-i
:声明整数变量。
-l
:声明变量为小写字母。
-p
:查看变量信息。
-r
:声明只读变量。
-u
:声明变量为大写字母。
-x
:该变量输出为环境变量。
:::
declare命令如果用在函数中,声明的变量只在函数内部有效,等同于local命令。不带任何参数时,declare命令输出当前环境的所有变量,包括函数在内,等同于不带有任何参数的set命令。
-i 参数声明整数变量以后,可以直接进行数学运算 【注意:赋值的时候直接不能有空格】
$ declare -i val1=12 val2=5
$ declare -i result
$ result=val1*val2
$ echo $result
60
如果变量result不声明为整数,val1*val2会被当作字面量,不会进行整数运算。另外,val1和val2其实不需要声明为整数,因为只要result声明为整数,它的赋值就会自动解释为整数运算。
注意,一个变量声明为整数以后,依然可以被改写为字符串。
$ declare -i var=12
$ var=foo
$ echo $var
0
上面例子中,变量var声明为整数,覆盖以后,Bash 不会报错,但会赋以不确定的值,可能输出0,也可能输出的是3。
$ declare -x foo
# 等同于
$ export foo
echo "-r 声明的变量是只读变量,不能修改----"
declare -r con=1
con=2
echo $? #看上一个指令的值
unset con
echo $?
./declare.sh: 行 2: con:只读变量
1
./declare.sh: 第 6 行: unset: con:无法取消设定: 只读 variable
1
上面例子中,后两个赋值语句都会报错,命令执行失败。
$ declare -u foo
$ foo=upper
$ echo $foo
UPPER
$ declare -l bar
$ bar=LOWER
$ echo $bar
lower
$ foo=hello
$ declare -p foo
declare -- foo="hello"
$ declare -p bar
bar:未找到
上面例子中,declare -p可以输出已定义变量的值,对于未定义的变量,会提示找不到。
如果不提供变量名,declare -p输出所有变量的信息。
$ declare -p
$ declare -f
$ declare -F
#!/bin/bash function hello() { echo "hello" } function alice() { echo "alice: $@" echo "$0: $1 $2 $3 $4" echo "$# arguments" #2个参数 } echo "--------------------f------------------------------" declare -f #显示函数的定义 echo "--------------------F------------------------------" declare -F #仅会显示函数名
readonly命令等同于declare -r,用来声明只读变量,不能改变变量值,也不能unset变量。
$ readonly foo=1
$ foo=2
$ echo $?
./declare.sh: 行 2: dd:只读变量
1
:::danger
readonly
命令有三个参数。
-f
:声明的变量为函数名。-p
:打印出所有的只读变量。-a
:声明的变量为数组。let命令声明变量时,可以直接执行算术表达式:
$ let foo=1+2
$ echo $foo #3
let命令的参数表达式如果包含空格,就需要使用引号:
$ let "foo = 1 + 2"
let可以同时对多个变量赋值,赋值表达式之间使用空格分隔:
$ let "v1 = 1" "v2 = v1++" #let声明变量v1和v2,其中v2等于v1++,表示先返回v1的值,然后v1自增
$ echo $v1,$v2 #2,1
#!/bin/bash # 未进行shellcheck检查 a=5 let a+=2 echo "plus : $a " # a = 7 和 let a-=2 echo "minus : $a" # a = 5 差 let a*=2 echo "multi : $a" # a = 10 积 let a/=2 echo "div : $a" # a = 5 商 let a%=2 echo "Modulo : $a" # a = 1 余数
expr命令支持算术运算,可以不使用((…))语法:
$ expr 3 + 2 #5
expr命令支持变量替换:
$ foo=3
$ expr $foo + 2 #5
expr命令也不支持非整数参数:
$ expr 3.5 + 2
expr: 非整数参数
$ echo `expr length $str`
$ expr index "$string" '$substring'
具体将在字符串操作篇细讲
exit命令用于终止当前脚本的执行,并向 Shell 返回一个退出值。
exit
上面命令中止当前脚本,将最后一条命令的退出状态,作为整个脚本的退出状态。
exit命令后面可以跟参数,该参数就是退出状态。
# 退出值为0(成功)
$ exit 0
# 退出值为1(失败)
$ exit 1
退出时,脚本会返回一个退出值。
:::danger
脚本的退出值,
source命令用于执行一个脚本,通常用于重新加载一个配置文件。
$ source .bashrc
source命令最大的特点是在当前 Shell 执行脚本,不像直接执行脚本时,会新建一个子Shell。所以,source命令执行脚本时,不需要export变量。
# 当前 Shell 新建一个变量 foo
$ foo=1
# 打印输出 1
$ source test.sh
1
# 打印输出空字符串
$ bash test.sh
上面例子中,当前 Shell 的变量foo并没有export,所以直接执行无法读取,但是source执行可以读取。
**source命令的另一个用途,是在脚本内部加载外部库。 **
#!/bin/bash
$ source ./lib.sh
function_from_lib
上面脚本在内部使用source命令加载了一个外部库,然后就可以在脚本里面,使用这个外部库定义的函数。
source有一个简写形式,可以使用一个点(.)来表示。
$ . .bashrc
变量表示数据的方法。是计算机为了保存数据项而在内存中分配的一个位置或一组位置的标识或名字。
变量名就是保存值的地方。shell变量有系统变量和自定义变量两种。
对于变量名的声明规则类似于其他编程语言。由字母、数字、下划线组成,但不能以数字开头。
hello_123 # 合法
123_hello # 不合法
主要记录实际工作中使用的,也不要记住。用到了再查即可。可以通过env指令显示所有的环境变量
$ env 或者
$ printenv
系统变量 | 说明 | 备注 |
---|---|---|
$BASH_VERSION | 查看bash的版本 | |
$BASH | bash的二进制程序文件的路径 | |
$USER | 当前用户 | 一般是root |
$EUID | 有效用户ID | E U I D 不一定与 EUID 不一定与 EUID不一定与UID相同 |
$FUNCTION | 在函数中,可直接打印当前函数的名字 | |
$GROUP | 当前用户所属的组 | 一个组ID列表 |
$HOSTNAME | 当前主机名字 | |
$HOSTTYPE | 当前主机类型 | |
$PATH | 可执行文件的搜索路径 | 以冒号分隔的目录列表 |
PWD | 当前工作目录 | |
BASHPID | Bash 进程的进程 ID | |
SHELL | shell名字 | |
DISPLAY | 图形环境的显示器名字,通常是:0,表示 X Server 的第一个显示器。 | |
EDITOR | 默认的文本编辑器 | |
HOME | 用户的主目录 | |
HOST | 当前主机的名称 |
很多环境变量很少发生变化,而且是只读的,可以视为常量。由于它们的变量名全部都是大写,所以传统上,如果用户要自己定义一个常量,也会使用全部大写的变量名。
:::danger
注:Bash变量名区分大小写,HOME和home是两个不同的变量。
:::
查看单个环境变量的值,可以使用printenv命令或echo命令。
$ printenv PATH #printenv命令后面的变量名,不用加前缀$
# 或者
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
:::danger
用户创建变量的时候,变量名必须遵守下面的规则:
cat helloworld.sh
_#直接使用
(
.
.
)
格
式
o
s
=
(..)格式_ os=
(..)格式os=(cat /etc/os-release)会影响用户接口和shell的行为。环境变量是一个全局变量。 用户创建的变量仅可用于当前Shell,子 Shell 默认读取不到父 Shell 定义的变量。为了把变量传递给子Shell,需要使用export命令。这样输出的变量,对于子Shell来说就是环境变量。
通过 export 命令将变量声明为环境变量即可。
export 变量名=变量值
# 方式 1 :直接export导入,命令行窗口重启后失效
export LD_LIBRARY_PATH=/usr/local/cuda/lib
# 方式 2
# 加入到 root目录下的 .bashrc 中
# 使用 source ./bashrc 使修改后的配置信息生效,命令行窗口重启或者机器重启均不会失效
# 查看环境变量是否生效
echo $变量名
echo $LD_LIBRARY_PATH
对于环境变量的查看
# 方式 1 :查看所有变量(包括环境变量和自定义变量)
set
# 方式 2 :只能查看环境变量
env
引用:将字符串使用双引号""扩起来。作用:保护字符串的特殊字符(通配符)不被shell重新解释或者扩展。
[root@centos8 data]# var01=3
[root@centos8 data]# echo $var01
3
在引用变量时,**需要使用 来进行引用变量的值 ∗ ∗ 。 : : : d a n g e r 变量在使用过程中,如果没有 来进行引用变量的值**。 :::danger 变量在使用过程中,如果没有 来进行引用变量的值∗∗。:::danger变量在使用过程中,如果没有作为前缀,需要思考如下情况:
bash$ ls -l [Vv]*
-rw-rw-r-- 1 bozo bozo 324 Apr 2 15:05 VIEWDATA.BAT
-rw-rw-r-- 1 bozo bozo 507 May 4 14:25 vartrace.sh
-rw-rw-r-- 1 bozo bozo 539 Apr 14 17:11 viewdata.sh
bash$ ls -l '[Vv]*'
ls: [Vv]*: No such file or directory
可以看到,提示不存在该文件。这里的’[Vv]*被当成了文件名。 在日常沟通和写作中,当我们引用一个短语的时候,我们会将它单独隔开并赋予它特殊的意义,而在bash脚本中,当我们_引用_一个字符串,意味着保留它的_字面量_。
:::danger
使用双引号可以防止字符串被分割。即使参数中拥有很多空白分隔符,被包在双引号中后依旧是算作单一字符。
List="one two three"
for a in $List # 空白符将变量分成几个部分。
do
echo "$a"
done
echo "---"
for a in "$List" # 在单一变量中保留所有空格。
do # ^ ^
echo "$a"
done
# one two three
:::danger
单引号(’ ')与双引号类似,但是在单引号中不能引用变量,因为 $ 不再具有特殊含义。在单引号中,除’之外的所有特殊字符都将会被直接按照字面意思解释。可以认为单引号(“全引用”)是双引号(“部分引用”)的一种更严格的形式。
:::
unset命令用来删除一个变量:
unset NAME
这个命令不是很有用。因为不存在的Bash变量一律等于空字符串,所以即使unset命令删除了变量,还是可以读取这个变量,值为空字符串。
所以,删除一个变量,也可以将这个变量设成空字符串。
$ foo=''
$ foo= #由于不存在的值默认为空字符串,所以可以在等号右边不写任何值
shell中参数: 0 、 0、 0、?、 ! 、 !、 !、 、 、 、*、KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲、@
字符符号 | 功能(用途) |
---|---|
$0 | shell文件本身的文件名 |
1 ~ 1~ 1~n | 添加到Shell的各参数值。$1是第1参数、 2 是第 2 参数、 … 、 2是第2参数、…、 2是第2参数、…、n表示第n个参数。⚠️注意:10以上要用大括号,如{10}。 |
$? | 最后运行命令的结束代码(返回值),执行成功返回0,不成功则返回非零值(一般解释为错误码)。 |
$$ | Shell本身的PID(ProcessID) |
$! | Shell最后运行的后台Process的PID |
$- | 使用Set命令设定的Flag一览 |
$* | 所有参数列表。如 "$*“用「”」括起来的情况、以"$1 $2 … $n" 的形式输出所有参数。 |
$@ | 所有参数列表。如 "$@“用「”」括起来的情况、以"$1" “ 2 " … " 2" … " 2"…"n” 的形式输出所有参数。 |
$# | 添加到Shell的参数个数 |
;;&, ;& | case条件语句终止符 |
"’ | 在参数替换中进行小写字母转换 |
` | 反引号 |
: | 空命令。 声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/676164 推荐阅读 相关标签 Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。 |