Shell脚本

一、${}

引用变量

当${}用来引用变量时,其等价于$,只不过${}可以指定变量边界

$ a=123
$ b=$a
$ echo $b
123
$ b=${a}
$ echo $b
123
$ A=B
# 打算先将 $A 的结果替换出来,然后再补一个 B 字母于其后
$ echo $AB
# 但结果只会打印变量AB的值
$ echo ${A}B
BB

对字符串进行截取及替换处理

变量设定方式说明
${string#substring} 从$string的开头位置截掉最短匹配的$substring
${string##substring}从$string的开头位置截掉最长匹配的$substring
${string%substring}从$string的结尾位置截掉最短匹配的$substring
${string%%substring}从$string的结尾位置截掉最长匹配的$substring
${string/substring/replacement}使用$replacement来替换第一个匹配的$substring
${string//substring/replacement}使用$replacement来替换所有匹配的$substring
${string/#substring/replacement}如果$substring匹配$string的开头部分, 那么就用$replacement来替换$substring
${string/%substring/replacement}如果$substring匹配$string的结尾部分, 那么就用$replacement来替换$substring

${string#substring} -->从$string的开头位置截掉最短匹配的$substring

$ test=xyzXYZ123456xyzXYZ
$ echo ${test#x*Z}
123456xyzXYZ 

${string#substring} -->从$string的开头位置截掉最长匹配的$substring

$ test=xyzXYZ123456xyzXYZ
$ echo ${test##x*Z}
 
$ 

${string%substring} -->从$string的结尾位置截掉最短匹配的$substring

$ test=xyzXYZ123456xyzXYZ
$ echo ${test%x*Z}
xyzXYZ123456

${string%%substring} -->从$string的结尾位置截掉最长匹配的$substring   

$ test=xyzXYZ123456xyzXYZ
$ echo ${test%%x*Z}
 
$ 

${string/substring/replacement} -->使用$replacement来替换第一个匹配的$substring

$ test=xyzXYZ123456xyzXYZ
$ echo ${test/xyz/ztj}
ztjXYZ123456xyzXYZ

${string//substring/replacement} -->使用$replacement来替换所有匹配的$substring

$ test=xyzXYZ123456xyzXYZ
$ echo ${test//xyz/ztj}
ztjXYZ123456ztjXYZ

${string/#substring/replacement} -->如果$substring匹配$string的开头部分, 那么就用$replacement来替换$substring

$ echo ${test/#xyz/ztj}
ztjXYZ123456xyzXYZ

${string/%substring/replacement} -->如果$substring匹配$string的结尾部分, 那么就用$replacement来替换$substring

$ test=xyzXYZ123456XYZxyz
$ echo ${test/%xyz/ztj}
xyzXYZ123456XYZztj
# 注意上下对比
$ test=xyzXYZ123456xyzXYZ
$ echo ${test/%xyz/ztj}
xyzXYZ123456xyzXYZ
$ var=testcase    
$ echo $var    
testcase    
$ echo ${var%s*e}   
testca    
$ echo $var    
testcase   
$ echo ${var%%s*e}   
te  
$ echo ${var#?e}    
stcase  
$ echo ${var##?e}    
stcase  
$ echo ${var##*e}    
  
$ echo ${var##*s}    
e    
$ echo ${var##test}    
case   
file=/dir1/dir2/dir3/my.file.txt
我们可以用 ${ } 分别替换获得不同的值:
${file#*/}:拿掉第一条 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt
${file##*/}:拿掉最后一条 / 及其左边的字符串:my.file.txt
${file#*.}:拿掉第一个 .  及其左边的字符串:file.txt
${file##*.}:拿掉最后一个 .  及其左边的字符串:txt
${file%/*}:拿掉最后条 / 及其右边的字符串:/dir1/dir2/dir3
${file%%/*}:拿掉第一条 / 及其右边的字符串:(空值)
${file%.*}:拿掉最后一个 .  及其右边的字符串:/dir1/dir2/dir3/my.file
${file%%.*}:拿掉第一个 .  及其右边的字符串:/dir1/dir2/dir3/my

对变量进行判断赋值

变量设定方式说明
${parameter-default}如果变量parameter没被声明, 那么就使用默认值
${parameter:-default}如果变量parameter没被设置, 那么就使用默认值
${parameter+alt_value}如果变量parameter被声明了, 那么就使用alt_value, 否则就使用null字符串
${parameter:+alt_value}如果变量parameter被设置了, 那么就使用alt_value, 否则就使用null字符串
${parameter=default}如果变量parameter没声明, 那么就把它的值设为default
${parameter:=default}如果变量parameter没设置, 那么就把它的值设为default
${parameter?err_msg}如果parameter已经被声明, 那么就使用设置的值, 否则打印err_msg错误消息
${parameter:?err_msg}如果parameter已经被设置, 那么就使用设置的值, 否则打印err_msg错误消息

    对于${parameter-default}和${parameter:-default}、${parameter+alt_value}和${parameter:+alt_value}、${parameter=default}和${parameter:=default}、${parameter?err_msg}和${parameter:?err_msg},99.99%情况下,${parameter-default}和${parameter:-default}、${parameter+alt_value}和${parameter:+alt_value}、${parameter=default}和${parameter:=default}、${parameter?err_msg}和${parameter:?err_msg}绝大多数情况下,输出都是一样的。只有在parameter被声明并设置为null值的时候, 多出来的:才会引起这两种形式的不同。

$ fruit=peach
$ echo ${fruit:-plum}
peach           
$ echo ${newfruit:-apple}
apple           
$ echo $newfruit
                
$ echo $EDITOR
                
$ echo ${EDITOR:-/bin/vi}
/bin/vi         
$ echo $EDITOR
                
$ name=
$ echo ${name-Joe}
                
$ echo ${name:-Joe}
Joe             

获取变量长度

    ${var:num},${var:num1:num2},${var/pattern/pattern},${var//pattern/pattern}

  • ${var:num},shell在var中提取第num个字符到末尾的所有字符。若num为正数,从左边0处开始;若num为负数,从右边开始提取字串,但必须使用在冒号后面加空格或一个数字或整个num加上括号,如${var: -2}、${var:1-3}或${var:(-2)}。         
  • ${var:num1:num2},num1是位置,num2是长度。表示从$var字符串的第$num1个位置开始提取长度为$num2的子串。不能为负数。
  • ${var/pattern/pattern}表示将var字符串的第一个匹配的pattern替换为另一个pattern。
  • ${var//pattern/pattern}表示将var字符串中的所有能匹配的pattern替换为另一个pattern。 
$ var=/home/centos  
$ echo $var  
/home/centos  
$ echo ${var:5}  
/centos  
$ echo ${var: -6}  
centos  
$ echo ${var:(-6)}  
centos  
$ echo ${var:1:4}  
home  
$ echo ${var/o/h}  
/hhme/centos  
$ echo ${var//o/h}  
/hhme/cenths  
$ ztj=(ab cd ef ghij123)    -定义数组
$ echo ${ztj[@]}            -输出数组全部元素
ab cd ef ghij123
$ echo ${ztj[*]}            -输出数组全部元素
ab cd ef ghij123
$ 
$ echo ${ztj[0]}            -输出数组第一个元素,默认数组下标从0开始
ab
$ echo ${#ztj[@]}           -统计数组元素的个数
4
$ echo ${#ztj[*]}           -统计数组元素的个数
4
$ echo ${#ztj[3]}           -计算数组第四个元素的长度,即:ghij123的长度
7
$ ztj[0]=ztj                -将数组第一个元素的数值重新赋值为ztj
$ echo ${ztj[@]}            -输出数组全部元素进行验证
ztj cd ef ghij123

创建匿名函数

不会新开进程,括号内变量余下仍可使用。括号内的命令间用分号隔开,最后一个也必须有分号。{}的第一个命令和左括号之间必须要有一个空格。

二、$()

用作命令替换(command substitution)`` (反引号) 效果相同,结果为shell命令cmd的输出。例如 version=$(uname -r)和version=`uname -r`都可以是version得到内核的版本号,但是在shell脚本,我们一般使用$(),以增加脚本的可读性。

$ echo $(date +%Y%m%d)
20230706
$ echo `date +%Y%m%d`
20230706

这两者之间仍有区别:

  • 在多层次的复合替换中,``(反引号)必须要额外的转义字符处理(反斜线\),而$( )比较直观。
[bob@centos home]$ echo date           //直接当成字符串输出
date
[bob@centos home]$ echo `date`         //相当于函数调用,先执行date命令
Tue Sep 3 16:10:43 CST 2019
[bob@centos home]$ echo $(date)       //作用与上面的命令一样,但不是所有版本的linux系统都支持
Tue Sep 3 16:11:45 CST 2019


命令嵌套
[bob@centos home]$ echo $(echo $(date))
Tue Sep 3 16:10:43 CST 2019
[bob@centos home]$ echo `echo `date``
date
[bob@centos home]$ echo `echo \`date\``
Tue Sep 3 16:10:43 CST 2019
  • ``(反引号)基本上可用在全部的 unix shell 中使用,若写成 shell script ,其移植性比较高。但反单引号容易打错或看错。
  • $() 并不是所有shell都支持, 如tcsh。

三、$[]

进行数学运算,其等价于$(()),支持 + - * / %,分别为 “加、减、乘、除、取模”,并且在使用中变量可以不使用$,直接使用变量名即可

$ echo $[1+2]
3
$ a=1
$ b=2
$ echo $[a+b]
3
$ echo $[$a+$b]
3

四、$(())

进行数学运算,其等价于$[],支持+-*/%,并且在使用中变量可以不使用$,直接使用变量名即可。

$ echo $((1+2))
3
$ a=1
$ b=2
$ echo $((a+b))
3
$ echo $(($a+$b))
3 
  • $((expression))`exprexpression` 效果相同, 计算数学表达式exp的数值, 其中exp只要符合C语言的运算规则即可, 甚至三目运算符和逻辑表达式都可以计算。
  • $(()) 还可作不同进制(如二进制、八进位、十六进制)的运算,只是,输出结果皆为十进制而已:echo $((16#2a)) 结果为 42 (16进位转十进制)。

五、[]

用于判断表达式的是0或非0,以决定程序的执行顺序,其等价于test命令(几乎不常用)

  • 字符串比较==:相等;!=不相等:;-z:空串;-n:非空串;
  • 整数比较-gt:大于;-lt :小于;-eq:等于;-ne:不等于;-ge:大于等于;-le:小于等于
  • 布尔运算-a:and;-o:or;!:相反状态
  • 文件状态-e:是否存在;-d:是目录;-f:是文件;-L:符号连接;-s:文件非空;-r:可读;-w:可写;-x:可执行

逻辑判断

$ [ ! -d ztj ] && mkdir ztj     --目录ztj不存在,则进行创建
$ ls -ld ztj
drwxr-xr-x 2 root root 6 Jul  6 10:04 ztj 

if命令判断

$ {
> a=java
> if test $a == "linux"
> then
> echo "i am linux"
> elif [ $a == "java" ] 
> then
> echo "i am java"
> fi
> }
i am java
$ {
> a=linux
> if test $a == "linux"
> then
> echo "i am linux"
> elif [ $a == "java" ] 
> then
> echo "i am java"
> fi
> }
i am linux

注意:

  1. 你必须在左括号的右侧和右括号的左侧各加一个空格,否则会报错。
  2. test命令使用标准的数学比较符号来表示字符串的比较,而用文本符号来表示数值的比较。很多人会记反了。使用反了,shell可能得不到正确的结果。
  3. 大于符号或小于符号必须要转义,否则会被理解成重定向

六、[[]]

[[]] 是 [] 的增强版,其返回值也是0或者非0,并且,在 [[ ]] 中使用> 、< 等符号不需要转义。

$ {
> a=4
> if [ $a \> 5 ]
> then
>         echo "$a的值大于5"
> else
>         echo "$a的值小于5"
> fi
> }
4的值小于5
$
$ {
> a=4
> if [[ $a > 5 ]]
> then
>         echo "$a的值大于5"
> else
>         echo "$a的值小于5"
> fi
> }
4的值小于5 
  • 字符串比较支持正则匹配和通配符匹配。比如:在[[ ]]中进行 == 或者 != 或=~比较时可以进行通配符匹配。它可以把右边的作为一个模式,而不仅仅是一个字符串,比如[[ hello == hell? ]],结果为真。[[ ]] 中匹配字符串或通配符,不需要引号
$ {
> a=linux
> if [[ $a == li* ]]
> then
> echo "hello i am ztj"
> fi
> }
hello i am ztj
$ 
$ {
> arch_version=$(arch)
> os_version=$(cat /etc/redhat-release)
> echo $os_version
> 
> if [[ "$arch_version" == "x86_64" ]] &&  [[ "$os_version" =~ ^"Red Hat Enterprise Linux Server release 7".* ]]
> then
>      echo "OS is ok"
> fi
> }
Red Hat Enterprise Linux Server release 7.7 (Maipo)
OS is ok
  • 支持逻辑运算符,除了支持&&和||,也支持==和!=和=~的连接或组合判断。防止脚本许多逻辑错误,比如,&&、||、<和> 操作符能够正常存在于[[ ]]条件判断结构中,但是如果出现在[ ]结构中的话,会报错。比如可以直接使用if [[ $a != 1 && $a != 2 ]], 如果不使用双中括号, 则为if [ $a -ne 1] && [ $a != 2 ]或者if [ $a -ne 1 -a $a != 2 ]。
$ {
> a=1
> if [[ $a != 4 && $a != 5 ]] 
> then
> echo "hello i am ztj"
> fi
> }
hello i am ztj
$ 
$ {
> a=1
> if [[ $a != 4 ]] && [[ $a != 5 ]] 
> then
> echo "hello i am ztj"
> fi
> }
hello i am ztj
$ 
$ {
> a=1
> if [ $a != 4 -a  $a != 5 ]
> then
> echo "hello i am ztj"
> fi
> }
hello i am ztj
$ 
$ {
> arch_version=$(arch)
> os_version=$(cat /etc/redhat-release)
> echo $os_version
> 
> if [[ "$arch_version" == "x86_64" ]] &&  [[ "$os_version" =~ "Red Hat Enterprise Linux Server release 7.7 (Maipo)" ]]
> then
>      echo "OS is ok"
> fi
> }
Red Hat Enterprise Linux Server release 7.7 (Maipo)
OS is ok
 

七、(())

除了结合$进行数学运算之外,还可以用于forwhile循环命令中控制循环,类似于c语言,当改变变量的值时,且不需要使用$。在 (()) 中使用> 、< 等符号也不需要转义。

$ {
> for((i=1;i<5;i++))
> do
>         echo "this is $i"
> done
> }
this is 1
this is 2
this is 3
this is 4
$ 
$ {
> i=0
> while [ $i -le 5 ]
> do
>         echo "this is $i"
>         ((i++))
> done
> }
this is 0
this is 1
this is 2
this is 3
this is 4
this is 5
$ 
$ {
> i=0
> while [ $i -le 5 ]
> do
>         echo "this is $i"
>         ((i=i+1))
> done
> }
this is 0
this is 1
this is 2
this is 3
this is 4
this is 5
$ 
$ {
> i=0
> while [ $i -le 5 ]
> do
>         echo "this is $i"
>         ((++i))
> done
> }
this is 0
this is 1
this is 2
this is 3
this is 4
this is 5
if ($i<5)    
if [ $i -lt 5 ]    
if [ $a -ne 1 -a $a != 2 ]    
if [ $a -ne 1] && [ $a != 2 ]    
if [[ $a != 1 && $a != 2 ]]    
     
for i in $(seq 0 4);do echo $i;done    
for i in `seq 0 4`;do echo $i;done    
for ((i=0;i<5;i++));do echo $i;done    
for i in {0..4};do echo $i;done  

八、()与{}的区别

相同点:

  • ()和{}都是把一串的命令放在括号里面,如果命令在一行,则命令之间用;隔开

不同点:

  • ()只是把一串命令重新开一个子shell进行执行,不影响当前shell环境;{}对一串命令在当前shell执行,影响当前shell环境
  • ()最后一个命令不用分号,{}最后一个命令要用分号
  • ()里的第一个命令和左边括号不必有空格,{}的第一个命令和左括号之间必要要有一个空格
  • ()和{}中括号里面的某个命令的重定向只影响改名了,但括号外的重定向则影响到括号里的所有命令
$ var=test
$ echo $var
test
$ (var=notest;echo $var)
notest
$ echo $var
test
$ { var=notest;echo $var;}
notest
$ echo $var
notest

九、:= ?= += =的区别

有如下makefile:

ifdef DEFINE_VRE
    VRE = “Hello World!”
else
endif

ifeq ($(OPT),define)
    VRE ?= “Hello World! First!”
endif

ifeq ($(OPT),add)
    VRE += “Kelly!”
endif

ifeq ($(OPT),recover)
    VRE := “Hello World! Again!”
endif

all:
    @echo $(VRE)

敲入以下make命令:

  • make DEFINE_VRE=true OPT=define    输出:Hello World!
  • make DEFINE_VRE=true OPT=add        输出:Hello World! Kelly!
  • make DEFINE_VRE=true OPT=recover  输出:Hello World! Again!
  • make DEFINE_VRE= OPT=define           输出:Hello World! First!
  • make DEFINE_VRE= OPT=add               输出:Kelly!
  • make DEFINE_VRE= OPT=recover         输出:Hello World! Again!

从上面的结果中我们可以清楚的看到他们的区别:

  • 是最基本的赋值
  • := 是覆盖之前的值
  • ?= 是如果没有被赋值过就赋予等号后面的值
  • += 是添加等号后面的值

十、= 和 := 的区别

  •       =:make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。看例子:
x = foo
y = $(x) bar
x = xyz

# y的值将会是 xyz bar ,而不是 foo bar 
  • :=:表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。看例子:
x := foo
y := $(x) bar
x := xyz

# y的值将会是 foo bar ,而不是 xyz bar 了。

十一、shell变量

  • 变量与变量内容以等号(=)来连接
  • 等号两边不能直接接xx
  • 变量名称只能是英文字母与数字或下划线,但是数字不能是开头符
  • 引用变量名用“$变量名
  • 若有空格符可以使用双引号或单引号将变量内容结合起来,但必须注意,双引号的特殊字符可以保有变量的特性,但是单引号内的特殊字符则仅为一般字符。即:
  1. 双引号里可以有变量
  2. 双引号里可以出现转义字符
  3. 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
  4. 单引号字符串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
name="$LOGNAME is hh" //root is hh
name='$LOGNAME is hh' //$LOGNAME is hh

your_name="runoob"
str="Hello, I know you are \"$your_name\"! \n"
echo -e $str

#输出结果
Hello, I know you are "runoob"! 

your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting  $greeting_1

# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2  $greeting_3

#输出结果
hello, runoob ! hello, runoob !
hello, runoob ! hello, ${your_name} !
  • 通常大写字符为系统预设变量,自行设定的变量可以使用小写字符

只读变量

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

#!/bin/bash

myUrl="https://www.google.com"
readonly myUrl
myUrl="https://www.runoob.com"

运行脚本,结果如下:

/bin/sh: NAME: This variable is read only.

删除变量

使用 unset 命令可以删除变量。语法:

unset variable_name

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

#!/bin/sh

myUrl="https://www.runoob.com"
unset myUrl
echo $myUrl

以上实例执行将没有任何输出。

特殊变量

  • $0:执行的脚本的文件名
  • $1:脚本文件的第一个参数名
  • $#:传递到脚本的参数个数
  • $*:以一个单字符串显示所有向脚本传递的参数。如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
  • $$:脚本运行的当前进程ID号
  • $! :Shell最后运行的后台Process的PID
  • $?:显示最后命令的退出状态,0表示没有错误,其它值表示有错误
  • $@:传给脚本的所有参数的列表,与$*相同,但是使用时加引号,并在引号中返回每个参数。如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
  • @这个符串通常用在“规则”行中,表示不显示命令本身,而只显示它的结果
  • $^:表示所有的依赖文件(components)
  • $<:代表第一个依赖文件(components中最左边的那个)
  • $- :使用 set 命令设定的Flag一览,即显示Shell使用的当前选项,与 set 命令功能相同。
------file a.sh------

#!/bin/bash
echo "the file is $0"
echo "the parameter is $1 $2"
echo "number of parameter is $#"
echo "all parameter is $*"
echo $?

#加权限后执行./a.sh hh mm gg
#结果
the file is ./a.sh
the parameter is hh mm
number of parameter is 3
all parameter is hh mm gg0

注意$@ 和 $* 都是引用所有参数,它们的区别只有在双引号中体现出来。假设在脚本运行时写了三个参数(分别存储在$1 $2 $3)则"$*" 等价于 “$1 $2 $3"(传递了一个参数);而“$@" 等价于 "$1" "$2" "$3"(传递了三个参数)

#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

echo "-- \$* 演示 ---"
for i in "$*"; do
    echo $i
done

echo "-- \$@ 演示 ---"
for i in "$@"; do
    echo $i
done

执行脚本,输出结果如下所示:

$ chmod +x test.sh 
$ ./test.sh 1 2 3
-- $* 演示 ---
1 2 3
-- $@ 演示 ---
1
2
3

十二、shell条件语句

if...fi语句结构

if [条件1];then
执行程序
fi

写成一行(适用于终端命令提示符):

if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
#!/bin/bash
declare -i a=10
declare -i b=20
if [ $a == $b ];then
echo "a is equal to b"
fi
if [ $a != $b ];then
echo "a is not equal to b"
fi

输出结果为:

a is not equal to b
  • if...else...fi语句结构

if [条件1];then
执行程序1
else
执行程序2
fi

#!/bin/bash
declare -i a
echo "input 1 or 2"
read a
if [ $a -eq 1 ];then
echo "1"
else
echo "2"
fi

打印为input1 or 211

  • if...elif...else...fi语句结构

if [条件1];then
执行程序1
elif[条件2];then
执行程序2
else
执行程序3
fi

#!/bin/bash
declare -i a=10
declare -i b=20
if [ $a == $b ];then
echo "a is equal to b"
elif [ $a -gt $b ];then
echo "a is greater than b"
else
echo "a is less than b"
fi

输出结果为:

a is less than b
  • case...esac语句结构

case $变量名称 in
“第一个变量内容”)
程序1
;;
“第二个变量内容”)
程序2
;;
*)
其它程序
exit 1
esac

#!/bin/bash
echo "you like"
echo "1 is apple"
echo "2 is orange"
echo "input your choice"
read a
case $a in
1)
echo "you like apple"
;;
2)
echo "you like orange"
;;
*)
echo "you like nothing"
exit 1
esac

执行后结果为:

you like
1 is apple
2 is orange
input your choice1you like apple

十三、shell循环类型

  • for循环

语法一
for ((初始值;限制值;执行步阶))
do
程序
done
初始值:变量在循环中的起始值
限制值:当变量值在这个限制范围内时,就继续进行循环
执行步阶:每作一次循环时,变量的变化量

#!/bin/bash
declare -i s
s=0
for (( i=1; i<=100; i++ ))
do
s=s+i
done
echo "the sum is $s"

输出结果为:

the sum is 50

语法二
for var in con1 con2 con3 ...//var是一个变量
do
程序
done

#!/bin/bash
declare -a A
A=(1 2 3 4 5 6 7 8 9 10)
declare -i c
for i in 1 3 5 7 9
do
c=c+A[$i];
done
echo "the count is $c"

输出结果为:

the count is 30
  • while循环

while [条件]
do
程序
done

#!/bin/bash
declare -i a=0
while [ $a -lt 10 ]
do
echo $a
a=a+1
done

这将产生以下结果:
0~9

  • until循环

until命令
do
程序
done

#!/bin/bash
declare -i a=0
until [ $a -gt 10 ]
do
echo $a
a=a+1
done

这将产生以下结果:

0~10

  • select循环

select var in word1 word2 ...
do
程序
done

#!/bin/bash
echo "what is this?"
select i in tea cofee water apple orange none
do
case $i in
tea|cofee|water)
echo "drink";;
apple|orange)
echo "fruit";;
none)
break;;
*) echo "ERROR:
 Invalid selection";;
esac
done

打印信息为:

what is this?
1) tea
2) cofee
3) water
4) apple
5) orange
6) none
#? 1
drink
#? 6

十四、shell数组

数组名=(值1 值2 ... 值n)

可以不使用连续的下标,而且下标的范围没有限制。使用 @ 或 * 可以获取数组中的所有元素。

$ a=(123 34 3 5)
$ echo $a //默认获取第一个元素
123
$ echo ${a[1]}//通过下标访问
34
$ echo ${a[@]}//访问整个数组,@或者*获取整个数组
123 34 3 5
$ echo ${#a[@]}//获取数组的长度
4
$ echo ${#a[3]}//获取字符串长度
1
$ echo ${a[@]:1:2}//切片方式获取一部分数组内容
34 3
$ echo ${a[@]:2}//从第二个元素开始
3 5
$ echo ${a[@]::2}//到第二个元素

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
length=${#array_name[n]}

关联数组

可以使用任意的字符串、或者整数作为下标来访问数组元素。关联数组使用 declare 命令来声明:

declare -A array_name

-A 选项就是用于声明一个关联数组。关联数组的键是唯一的。访问关联数组元素可以使用指定的键。

declare -A site=(["google"]="www.google.com" ["runoob"]="www.runoob.com" ["taobao"]="www.taobao.com")

#我们也可以先声明一个关联数组,然后再设置键和值:
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"

在数组前加一个感叹号 ! 可以获取数组的所有键,例如:

declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"

echo "数组的键为: ${!site[*]}"
echo "数组的键为: ${!site[@]}"

执行脚本,输出结果如下所示:

数组的键为: google runoob taobao
数组的键为: google runoob taobao

十五、shell函数

  • 创建函数

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

说明:

  • 1、可以带 function fun() 定义,也可以直接 fun() 定义,不带任何参数。
  • 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return 后跟数值 n(0-255).
#!/bin/bash
# Define your function here
Hello () {
echo "Hello World"
}
# Invoke your function
Hello

当你想执行上面的脚本,它会产生以下结果:

Hello World

注意:

  • 所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
  • return 语句只能返回一个介于 0 到 255 之间的整数,而两个输入数字的和可能超过这个范围。要解决这个问题,您可以修改 return 语句,直接使用 echo 输出和而不是使用 return:
funWithReturn(){
    echo "这个函数会对输入的两个数字进行相加运算..."
    echo "输入第一个数字: "
    read aNum
    echo "输入第二个数字: "
    read anotherNum
    sum=$(($aNum + $anotherNum))
    echo "两个数字分别为 $aNum 和 $anotherNum !"
    echo $sum  # 输出两个数字的和
}
  • 参数传递给函数

你可以定义一个函数,它接受参数,而调用这些函数。将这些参数代表$1,$2,依此类推。

#!/bin/bash
# Define your function here
Hello () {
echo "Hello World $1 $2"
}
# Invoke your function
Hello Zhang lisi

这将产生以下结果:

Hello World Zhang lisi

注意:$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。

  • 从函数的返回值

#!/bin/bash
# Define your function here
Hello () {
echo "Hello World $1 $2"
return 1
}
# Invoke your function
Hello Zhangsan lisi
r=$?
echo "Return value is $r"

这将产生以下结果:

Hello World Zhangsan lisi
Return value is 1
  • 嵌套函数

函数更有趣的功能之一是,他们可以调用本身以及调用其他函数。被称为递归函数调用自身的函数。

#!/bin/bash
# Calling one function from another
one () {
echo "This is the first function"
two
}
 
two () {
echo "This is the second function"
}
# Calling function one.
one

这将产生以下结果:

This is the first function
This is the second function

十六、常用shell内嵌命令

echo

  • 显示普通字符串
echo "It is a test"
#双引号完全可以省略,以下命令与上面实例效果一致:
echo It is a test
  • 显示转义字符

echo "\"It is a test\""
#结果
"It is a test"
#同样,双引号也可以省略
  • 显示变量

read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量

#!/bin/sh
read name 
echo "$name It is a test"

以上代码保存为 test.sh,name 接收标准输入的变量,结果将是:

$ sh test.sh
OK                     #标准输入
OK It is a test        #输出
  • 显示换行

echo -e "OK! \n" # -e 开启转义
echo "It is a test"

输出结果:

OK!

It is a test
  • 显示不换行

#!/bin/sh
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"

输出结果:

OK! It is a test
  • 显示结果定向至文件

echo "It is a test" > myfile
  • 原样输出字符串,不进行转义或取变量(用单引号)

echo '$name\"'
#结果
$name\"
  • 显示命令执行结果

echo `date`
#结果
Thu Jul 24 10:08:46 CST 2014

printf

语法:printf format-string [arguments...]

  • format-string: 一个格式字符串,它包含普通文本和格式说明符。
  • arguments: 用于填充格式说明符的参数列表。
  • 默认的 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。

格式说明符由 % 字符开始,后跟一个或多个字符,用于指定输出的格式。常用的格式说明符包括:

  • %s:字符串
  • %d:十进制整数
  • %f:浮点数
  • %c:字符
  • %x:十六进制数
  • %o:八进制数
  • %b:二进制数
  • %e:科学计数法表示的浮点数
$ echo "Hello, Shell"
Hello, Shell
$ printf "Hello, Shell\n"
Hello, Shell
$
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com
 
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

执行脚本,输出结果如下所示:

姓名     性别   体重kg
郭靖     男      66.12
杨过     男      48.65
郭芙     女      47.99

%s %c %d %f 都是格式替代符,%s 输出一个字符串,%d 整型输出,%c 输出一个字符,%f 输出实数,以小数形式输出。

%-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。

%-4.2f 指格式化为小数,其中 .2 指保留 2 位小数。

#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com
 
# format-string为双引号
printf "%d %s\n" 1 "abc"

# 单引号与双引号效果一样 
printf '%d %s\n' 1 "abc" 

# 没有引号也可以输出
printf %s abcdef

# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def

printf "%s\n" abc def

printf "%s %s %s\n" a b c d e f g h i j

# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"

执行脚本,输出结果如下所示:

1 abc
1 abc
abcdefabcdefabc
def
a b c
d e f
g h i
j  
 and 0

env

显示系统中已存在的环境变量

set

显示系统中已经存在的 shell 变量,以及设置 shell 变量的新变量值。使用 set 更改 shell 特性时,符号 + 和 - 的作用分别是打开和关闭指定的模式。set 命令不能够定义新的 shell 变量。如果要定义新的变量,可以使用 declare 命令以 变量名=值 的格式进行定义即可。

语法:set [-options] [var1 var2 …]

  •         -a:标示已修改的变量,以供输出至环境变量
  •         -b:使被中止的后台程序立刻回报执行状态
  •         -C:转向所产生的文件无法覆盖已存在的文件。
  •         -d:Shell预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消
  •         -e:若指令传回值不等于0,则立即退出shell
  •         -f:取消使用通配符
  •         -h:自动记录函数的所在位置
  •         -H shell:可利用 ! 加 <指令编号> 的方式来执行  history 中记录的指令
  •         -k:指令所给的参数都会被视为此指令的环境变量
  •         -l:记录 for 循环的变量名称
  •         -m:使用监视模式
  •         -n:测试模式,只读取指令,而不实际执行
  •         -p:启动优先顺序模式
  •         -P:启动 -P 参数后,执行指令时,会以实际的文件或目录来取代符号连接
  •         -t:执行完随后的指令,即退出shell
  •         -u:当执行时使用到未定义过的变量,则显示错误信息
  •         -v:显示shell所读取的输入值
  •         -H shell:可利用"!"加<指令编号>的方式来执行 history 中记录的指令
  •         -x:执行指令后,会先显示该指令及所下的参数
  •         +<参数>:取消某个set曾启动的参数。与-<参数>相反
  •         -o option:特殊属性还有很多,大部分与上面的可选参数功能相同
declare Mylove=“C++” #定义新环境变量
set -a Mylove        #执行该命令后,将会新添加对应的环境变量
echo | grep Mylove   #显示和搜索环境变量

 调用 set 设置一个或多个参数时,set会赋值位置参数,从 $1 开始赋值:

$cat test.sh

#!/bin/bash
set first second third
echo $3 $2 $1

$./test.sh
third second first

如上,在执行 test.sh 脚本时并没有输入参数,但是使用 set 指令后会对位置参数进行赋值。

重点参数

最常用的两个参数就是 -e-x ,一般写在 shell 代码逻辑之前,这两个组合在一起用,可以在 debug 的时候替你节省许多时间 。

  • set -x 会在执行每一行 shell 脚本时,把执行的内容输出来。它可以让你看到当前执行的情况,里面涉及的变量也会被替换成实际的值。
  • set -e 会在执行出错时结束程序,就像其他语言中的“抛出异常”一样。(准确说,不是所有出错的时候都会结束程序,见下面的注意)

注意set -e 结束程序的条件比较复杂,在man bash里面,足足用了一段话描述各种情景。大多数执行都会在出错时退出,除非 shell 命令位于以下情况:

  1. 一个 pipeline 的非结尾部分,比如error | ok
  2. 一个组合语句的非结尾部分,比如ok && error || other
  3. 一连串语句的非结尾部分,比如error; ok
  4. 位于判断语句内,包括test、if、while等等。

set -e

功能是遇到错误后脚本会直接退出,不会继续往下执行

cat test.sh
#!/bin/bash

aa=123
cat $aa
necho $aa
echo $aa

执行

cat: 123: No such file or directory
test.sh: line 5: necho: command not found
123

因为不存在名子为123的文件,所以cat命令执行报错,同样的,因为不存在necho命令,所以也会报错,关键是报错后都会继续向下执行,所以

echo $aa

还是会成功输出123, 但是很多时候我们希望在执行错误后能够立即退出,而不是继续向下执行,那么可以在代码前加一个 set -e

$ cat test.sh

#!/bin/bash

set -e
aa=123
cat $aa
necho $aa
echo $aa

执行

cat: 123: No such file or directory

这样就ok了,遇到错误后脚本会直接退出,不会继续往下执行。

如果你只是想对其中的一段代码做这种的设置,那么你可以这样做

$ cat -n test.sh
     1	#!/bin/bash
     2
     3	aa=123
     4	cat $aa
     5	set -e
     6	echo $aa
     7	set +e
     8	necho $aa
     9	echo $aa

执行

$ bash test.sh
cat: 123: No such file or directory
123
test.sh: line 8: necho: command not found
123

对于上面的文件set的作用域只是在第5-7行,因为第6行可以正确执行,所以整个脚本也就可以正确执行了。
这种局部开启的方式很多时候没有必要,尽量在文件头部加一个就好了,当然在有些时候还是很有必要的,比如你要判断某个子shell的执行结果,使用了$? 变量,那么就要关闭set了。

set -o pipefail

这个命令主要是对上面的 set -e 的补充,因为 set -e 对于管道符是无效的,比如上面的脚本假如变成

$ cat -n test.sh
     1	#!/bin/bash
     2
     3	set -e
     4	aa=123
     5	cat $aa | echo
     6	echo $aa
     7
     8

执行

$ bash test.sh

cat: 123: No such file or directory
123

可以看到使用管道符号的话set -e 也不好用了,这个时候假如使用 set -o pipefail 则可以解决这个问题

$ cat -n test.sh
     1	#!/bin/bash
     2
     3	set -e
     4	set -o pipefail
     5
     6	aa=123
     7	cat $aa | echo
     8	echo $aa
     9
    10

执行

$ bash test.sh

cat: 123: No such file or directory

set -u

执行脚本的时候,如果遇到不存在的变量,Bash 默认忽略它,set -u 可以在需要的变量不存在的时候直接报错退出

 cat -n test.sh
     1	#!/bin/bash
     2
     3	aa=123
     4	echo $bb
     5	echo $aa

执行

$ bash test.sh

123

可以看到,即使变量 bb不存在也不会报错,如果想要避免这种情况,可以这样做

$ cat -n test.sh
     1	#!/bin/bash
     2
     3	set -u
     4	aa=123
     5	echo $bb
     6	echo $aa

执行

$ bash test.sh
test.sh: line 5: bb: unbound variable

同样的,你也可以通过

set +u

关闭这个模式设置

set -x

  有些时候我们写的脚本比价长也比较复杂,引起最终结果出错的原因可能是前面多个步骤运算出错导致的(程序没有语法错误,可能是赋值计算等出错)
  这个时候如果通过echo的方式去调试每一步的值会非常麻烦,而且后面还要注释掉大量的echo语句。这个时候就可以通过set -x 来打开调试,让调试变得十分简单。


$ cat -n test.sh
     1	#!/bin/bash
     2
     3
     4	ip=`ifconfig eth0| awk  'NR==2{print $0}'|awk -F "[:]" '{print $2}'|awk '{print $1}'`
     5	set -x
     6	k=$ip"aaa"
     7	set +x
     8	echo "$k"
     9
    10

执行

$ bash test.sh
+ k=10.76.0.27aaa
+ set +x
10.76.0.27aaa

要是能够显示哪一行执行的结果就更好了,别怕,可以这样设置:

$ cat -n test.sh
     1	#!/bin/bash
     2
     3	export PS4='+{$LINENO:${FUNCNAME[0]}} '
     4
     5	ip=`ifconfig eth0| awk  'NR==2{print $0}'|awk -F "[:]" '{print $2}'|awk '{print $1}'`
     6	set -x
     7	k=$ip"aaa"
     8	set +x
     9	echo "$k"
    10
    11

再执行

$ bash test.sh
+{7:} k=10.76.0.27aaa
+{8:} set +x
10.76.0.27aaa

可以看到行号也显示出来了,其实冒号后面是要显示方法名的,因为这里没有使用方法,所以没有显示。下面给一个方法使用的样例。

$ cat -n test.sh
     1	#!/bin/bash
     2
     3	export PS4='+{$LINENO:${FUNCNAME[0]}} '
     4
     5
     6	function tool()
     7	{
     8
     9	ip=`ifconfig eth0| awk  'NR==2{print $0}'|awk -F "[:]" '{print $2}'|awk '{print $1}'`
    10	set -x
    11	k=$ip"aaa"
    12	set +x
    13	echo "$k"
    14
    15	}
    16
    17	tool
    18

执行

$ bash test.sh
+{11:tool} k=10.76.0.27aaa
+{12:tool} set +x
10.76.0.27aaa

read

从键盘读入变量内容

语法:read [-options] [var1 var2 …]

options和var都是可选的,如果没有提供变量名,那么读取的数据将存放在环境变量REPLY变量中;

  •         -a:把读取的数据赋值给数组array,从下标0开始
  •         -d:用字符串delimiter指定读取结束的位置
  •         -e:在获取用户输入的时候,对功能键进行编码转换
  •         -p:显示提示信息,提示内容为prompt
  •         -r: 原样读取(Raw mode),不把反斜杠字符解释为转义字符
  •         -n:读取num个字符,而不是整行字符
  •         -s:静默模式(Slient mode),不会在屏幕上显示输入的字符,输入密码或其它确认信息时常用
  •         -t:设置超时时间
  •         -u:使用文件描述符fd作为输入源,而不是标准输入,类似于重定向

在交互式shell中的例子:

#!/bin/bash
read
echo $REPLY

执行后先输入什么后输出什么

同时给多个变量赋值的例子:

#!/bin/bash
read -p "请输入姓名:" name 
read -p "请输入性别:" sex
read -p "请输入年龄:" age
 
#打印上面输入的变量内容
echo "姓名:${name}"
echo "性别:${sex}"
echo "年龄:${age}"

执行后先输入什么后根据变量输出什么

时间限制和静默模式的例子:

#!/bin/bash
read -t 10 -sp "请输入密码(10s内):" pwd1
echo   # 这是换行(使用了-s静默模式才需要这个换行符)
read -t 10 -sp "请再次输入密码(10s内):" pwd2
printf "\n"
# 校验密码两次是否一致
if [ $pwd1 == $pwd2 ]   # 这里变量名前后一定都要有空格
then
        echo "两次密码一致,认证通过~"
else
        echo "密码不一致,验证失败!"
fi

执行后根据判断输出echo的结果

declare

声明变量内容

语法:declare [x/-][aArxif] [变量名称=具体值]

  •         +/-:-用来指定变量有该属性,+是取消该属性;
  •         -a:定义为数组array,格式为  数组名=(值1 值2 ...) 或 数组名=([0]=值1 [1]=值2 ...)
  •         -A:Array,设置为key-value关联数组(索引是字符串),格式 数组名=([字符串key1]=值1 [字符串key2]=值2 ...)
  •         -f:定义为函数function
  •         -i:定义为整数integer
  •         -r:定义为“只读”
  •         -x:定义为透过环境输出变量 

一般格式例子: 

#!/bin/bash
a=1+1
declare -i b=1+1
echo $a
echo $b

数组格式例子:

#!/bin/bash
# 普通数组
declare -a arr1=("李明" 33 "hobbit1")
echo ${arr1[*]}   # 千万别忘了这里的花括号
echo ${arr1[1]}
 
declare -a arr2=([0]="王强" 55 "hobbit2")
echo ${arr2[@]}
 
# 关联数组
declare -A arr3=(["name"]="王武" [age]=34 [hobbit]="hobbit3")
echo ${arr3[@]}
echo ${arr3["age"]}

执行后输出结果为

李明 33 hobbit1
33
王强 55 hobbit2
王武 34 hobbit3
34

exit

用于退出当前shell环境进程结束运行,并且可以返回一个状态码.一般使用$?可以获取状态码。

使用场景:

  1. 结束当前shell进程
  2. 当shell进程执行出错退出时,可以返回不同的状态值代表不同的错误.

比如执行一个脚本文件里面操作一个文件时,可以返回1表示文件不存在,2表示文件没有读取权限,3表示文件类型不对.

例子:

vim exit.sh
 
#!/bin/bash
echo 'hello'
exit 2
echo 'word'

执行后输出结果为

hello

十七、Shell 输入/输出重定向

大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回​​到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。

重定向命令列表如下:

命令说明
command > file将输出重定向到 file。
command < file将输入重定向到 file。
command >> file将输出以追加的方式重定向到 file。
n > file将文件描述符为 n 的文件重定向到 file。
n >> file将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m将输出文件 m 和 n 合并。
n <& m将输入文件 m 和 n 合并。
<< tag将开始标记 tag 和结束标记 tag 之间的内容作为输入。

需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

重定向深入讲解

一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:

  • 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
  • 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
  • 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。

默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。

如果希望 stderr 重定向到 file,可以这样写:

$ command 2>file

如果希望 stderr 追加到 file 文件末尾,可以这样写:

$ command 2>>file

2 表示标准错误文件(stderr)。

如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:

$ command > file 2>&1

或者

$ command >> file 2>&1

如果希望对 stdin 和 stdout 都重定向,可以这样写:

$ command < file1 >file2

command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。

/dev/null 文件

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

$ command > /dev/null

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

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

$ command > /dev/null 2>&1

注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出

十八、Shell 文件包含

. filename # 注意点号(.)和文件名中间有一空格

source filename

创建两个 shell 脚本文件。

test1.sh 代码如下:

#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

url="http://www.runoob.com"

test2.sh代码如下:

#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

#使用 . 号来引用test1.sh 文件
. ./test1.sh

# 或者使用以下包含文件代码
# source ./test1.sh

echo "菜鸟教程官网地址:$url"

接下来,我们为 test2.sh 添加可执行权限并执行:

$ chmod +x test2.sh 
$ ./test2.sh 
菜鸟教程官网地址:http://www.runoob.com

注:被包含的文件 test1.sh 不需要可执行权限。

十九、Shell 注释

以 # 开头的行就是注释,会被解释器忽略。

通过每一行加一个 # 号设置多行注释。

多行注释

使用 Here 文档

:<<EOF
注释内容...
注释内容...
注释内容...
EOF

以上例子中,: 是一个空命令,用于执行后面的 Here 文档,<<'EOF' 表示开启 Here 文档,COMMENT 是 Here 文档的标识符,在这两个标识符之间的内容都会被视为注释,不会被执行。

EOF 也可以使用其他符号:

: <<'COMMENT'
这是注释的部分。
可以有多行内容。
COMMENT

:<<'
注释内容...
注释内容...
注释内容...
'

:<<!
注释内容...
注释内容...
注释内容...
!

直接使用 : 号

我们也可以使用了冒号 : 命令,并用单引号 ' 将多行内容括起来。由于冒号是一个空命令,这些内容不会被执行。

格式为:: + 空格 + 单引号。

: '
这是注释的部分。
可以有多行内容。
'

参考

linux下${}、$()、$[]、$(())、[]、[[]]、(())的作用及用法说明_linux ${}-CSDN博客

shell中:= ?= += =的区别_shell ?=-CSDN博客

https://zhuanlan.zhihu.com/p/82112596

Linux Shell脚本攻略:shell中各种括号()、(())、[]、[[]]、{}的作用_shell脚本替换文件中某个字符串-CSDN博客

shell脚本详解-CSDN博客

shell脚本命令set_shell set-CSDN博客

Shell 教程 | 菜鸟教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值