Shell脚本的学习

编写脚本文件

定义以开头:#!/bin/bash

#!用来声明脚本由什么shell解释,否则使用默认shel

第一步:编写脚本文件
复制代码
#!/bin/bash
#注释
echo "这是输出"
第二步:加上执行权限:chmod +x 脚本文件名.sh
第三步:运行程序

./xxx.sh :先按照 文件中#!指定的解析器解析,如果#!指定指定的解析器不存在 才会使用系统默认的解析器

bash xxx.sh:指明先用bash解析器解析,如果bash不存在 才会使用默认解析器

. xxx.sh 直接使用默认解析器解析(不会执行第一行的#!指定的解析器)但是第一行还是要写的

shell学习

变量

定义变量:变量名=变量值

如:num=10 #等号两侧避免使用空格

引用变量:$变量名

echo $num #输出10

echo ${num} #推荐给所有变量加上花括号

unset :清除变量值

unset $num #unset 命令不能删除只读变量

echo $num #输出空

read:从键盘获取输入

read num

echo "num=$num"

在一行上显示和添加提示 需要加上-p

read -p "请输入Num的值:" num

echo "num=$num"

readonly:只读变量,只读变量的值不能被改变

环境变量

这些是由操作系统或用户设置的特殊变量,用于配置 Shell 的行为和影响其执行环境。

例如,PATH 变量包含了操作系统搜索可执行文件的路径:

复制代码
echo $PATH

特殊变量:

$0 表示脚本的名称

1, 2, 等表示脚本的参数

$#表示传递给脚本的参数数量

$? 表示上一个命令的退出状态等。

整数变量

在一些Shell中,你可以使用 declaretypeset 命令来声明整数变量。

复制代码
declare -i my_integer=42

这样的声明告诉 Shell 将 my_integer 视为整数,如果尝试将非整数值赋给它,Shell会尝试将其转换为整数。

数组变量:

数组可以是整数索引数组或关联数组

bash支持一维数组(不支持多维数组),并且没有限定数组的大小。

类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。

定义数组:

在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:

复制代码
数组名=(值1 值2 ... 值n)
复制代码
array_name=(value0 value1 value2 value3)
复制代码
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
复制代码
#整数索引数组
my_array=(1 2 3 4 5)
#关联数组
declare -A associative_array
associative_array["name"]="John"
associative_array["age"]=30

读取数组:

读取数组元素值的一般格式是:

复制代码
${数组名[下标]}
valuen=${array_name[n]}
#使用 @ 符号可以获取数组中的所有元素,例如:
echo ${array_name[@]}

获取数组的长度:

获取数组长度的方法与获取字符串长度的方法相同,例如:

取得数组元素的个数

length=${#array_name[@]}

或者

length=${#array_name[*]}

取得数组单个元素的长度

length=${#array_name[n]}

关联数组:

Bash 支持关联数组,可以使用任意的字符串、或者整数作为下标来访问数组元素。

关联数组使用 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["runoob"]}

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

www.runoob.com

获取数组中的所有元素:

使用 @ 或 * 可以获取数组中的所有元素,例如:

#!/bin/bash

my_array[0]=A

my_array[1]=B

my_array[2]=C

my_array[3]=D

echo "数组的元素为: ${my_array[*]}"

echo "数组的元素为: ${my_array[@]}"

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

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 字符串

单引号

复制代码
str='this is a string'

单引号字符串的限制:

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
  • 单引号字符串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。

双引号

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} !

获取字符串长度

变量为字符串时,{#string} 等价于 {#string[0]}:

string="abcd"

echo ${#string} # 输出 4

echo ${#string[0]} # 输出 4

提取子字符串

从字符串索引值为 1字符开始截取 4 个字符:

string="runoob is a great site"

echo ${string:1:4} # 输出 unoo

查找子字符串

查找字符 io 的位置(哪个字母先出现就计算哪个):脚本中 ` 是反引号,而不是单引号 '

string="runoob is a great site"

echo `expr index "$string" io` # 输出 4(第四个不是下标)

Shell 注释

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

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

多行注释

使用 Here 文档

多行注释还可以使用以下格式:

:<<EOF

注释内容...

注释内容...

注释内容...

EOF

: <<'COMMENT'

这是注释的部分。

可以有多行内容。

COMMENT

:<<'

注释内容...

注释内容...

注释内容...

'

:<<!

注释内容...

注释内容...

注释内容...

!

Shell 传递参数

向脚本传递三个参数,并分别输出,其中 **0** 为执行的文件名(包含文件路径),1 表示第一个参数,$2 表示第二个参数,依此类推:

#!/bin/bash

echo "Shell 传递参数实例!";

echo "执行的文件名:$0";

echo "第一个参数为:$1";

echo "第二个参数为:$2";

echo "第三个参数为:$3";

为脚本设置可执行权限,并执行脚本,输出结果如下所示:

复制代码
$ chmod +x test.sh 
$ ./test.sh 1 2 3
Shell 传递参数实例!
执行的文件名:./test.sh
第一个参数为:1
第二个参数为:2
第三个参数为:3

特殊字符用来处理参数:

echo "Shell 传递参数实例!";

echo "第一个参数为:$1";

echo "参数个数为:$#";

echo "传递的参数作为一个字符串显示:$*";

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

复制代码
$ chmod +x test.sh 
$ ./test.sh 1 2 3
Shell 传递参数实例!
第一个参数为:1
参数个数为:3
传递的参数作为一个字符串显示:1 2 3

\* 与 @ 区别:

  • 相同点:都是引用所有参数。
  • 不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,则 " * " 等价于 "1 2 3"(传递了一个参数),而 "@" 等价于 "1" "2" "3"(传递了三个参数)。

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 基本运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。

expr 是一款表达式计算工具,使用它能完成表达式的求值操作。

(注意使用的是反引号 ` 而不是单引号 '):

#!/bin/bash

val=`expr 2 + 2`

echo "两数之和为 : $val"

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

两数之和为 : 4

两点注意:

  • 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
  • 完整的表达式要被 ` ` 包含,注意这个字符不是常用的单引号,在 Esc 键下边。

算术运算符

假定变量 a 为 10,变量 b 为 20:

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

#!/bin/bash

a=10

b=20

val=`expr a + b`

echo "a + b : $val"

val=`expr a - b`

echo "a - b : $val"

val=`expr a \\\* b`

echo "a * b : $val"

val=`expr b / a`

echo "b / a : $val"

val=`expr b % a`

echo "b % a : $val"

if [ a == b ]

then

echo "a 等于 b"

fi

if [ a != b ]

then

echo "a 不等于 b"

fi

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

复制代码
a + b : 30
a - b : -10
a * b : 200
b / a : 2
b % a : 0
a 不等于 b

注意:

  • 乘号(*)前边必须加反斜杠(\)才能实现乘法运算;
  • if...then...fi 是条件语句。
  • 在 MAC 中 shell 的 expr 语法是:$((表达式)),此处表达式中的 "*" 不需要转义符号 "\" 。

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

关系运算符

假定变量 a 为 10,变量 b 为 20:

#!/bin/bash

a=10

b=20

if [ a -eq b ]

then

echo "a -eq b : a 等于 b"

else

echo "a -eq b: a 不等于 b"

fi

if [ a -ne b ]

then

echo "a -ne b: a 不等于 b"

else

echo "a -ne b : a 等于 b"

fi

if [ a -gt b ]

then

echo "a -gt b: a 大于 b"

else

echo "a -gt b: a 不大于 b"

fi

if [ a -lt b ]

then

echo "a -lt b: a 小于 b"

else

echo "a -lt b: a 不小于 b"

fi

if [ a -ge b ]

then

echo "a -ge b: a 大于或等于 b"

else

echo "a -ge b: a 小于 b"

fi

if [ a -le b ]

then

echo "a -le b: a 小于或等于 b"

else

echo "a -le b: a 大于 b"

fi

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

复制代码
10 -eq 20: a 不等于 b
10 -ne 20: a 不等于 b
10 -gt 20: a 不大于 b
10 -lt 20: a 小于 b
10 -ge 20: a 小于 b
10 -le 20: a 小于或等于 b

布尔运算符

假定变量 a 为 10,变量 b 为 20:

#!/bin/bash

a=10

b=20

if [ a != b ]

then

echo "a != b : a 不等于 b"

else

echo "a == b: a 等于 b"

fi

if [ a -lt 100 -a b -gt 15 ]

then

echo "a 小于 100 且 b 大于 15 : 返回 true"

else

echo "a 小于 100 且 b 大于 15 : 返回 false"

fi

if [ a -lt 100 -o b -gt 100 ]

then

echo "a 小于 100 或 b 大于 100 : 返回 true"

else

echo "a 小于 100 或 b 大于 100 : 返回 false"

fi

if [ a -lt 5 -o b -gt 100 ]

then

echo "a 小于 5 或 b 大于 100 : 返回 true"

else

echo "a 小于 5 或 b 大于 100 : 返回 false"

fi

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

复制代码
10 != 20 : a 不等于 b
10 小于 100 且 20 大于 15 : 返回 true
10 小于 100 或 20 大于 100 : 返回 true
10 小于 5 或 20 大于 100 : 返回 false

逻辑运算符

假定变量 a 为 10,变量 b 为 20:

#!/bin/bash

a=10

b=20

if [[ a -lt 100 \&\& b -gt 100 ]]

then

echo "返回 true"

else

echo "返回 false"

fi

if [[ a -lt 100 \|\| b -gt 100 ]]

then

echo "返回 true"

else

echo "返回 false"

fi

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

复制代码
返回 false
返回 true

字符串运算符

假定变量 a 为 "abc",变量 b 为 "efg":

#!/bin/bash

a="abc"

b="efg"

if [ a = b ]

then

echo "a = b : a 等于 b"

else

echo "a = b: a 不等于 b"

fi

if [ a != b ]

then

echo "a != b : a 不等于 b"

else

echo "a != b: a 等于 b"

fi

if [ -z $a ]

then

echo "-z $a : 字符串长度为 0"

else

echo "-z $a : 字符串长度不为 0"

fi

if [ -n "$a" ]

then

echo "-n $a : 字符串长度不为 0"

else

echo "-n $a : 字符串长度为 0"

fi

if [ $a ]

then

echo "$a : 字符串不为空"

else

echo "$a : 字符串为空"

fi

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

复制代码
abc = efg: a 不等于 b
abc != efg : a 不等于 b
-z abc : 字符串长度不为 0
-n abc : 字符串长度不为 0
abc : 字符串不为空

文件测试运算符

其他检查符:

  • -S: 判断某文件是否 socket。
  • -L: 检测文件是否存在并且是一个符号链接。

变量 file 表示文件 /var/www/runoob/test.sh ,它的大小为 100 字节,具有 rwx 权限。下面的代码,将检测该文件的各种属性:

#!/bin/bash

file="/var/www/runoob/test.sh"

if [ -r $file ]

then

echo "文件可读"

else

echo "文件不可读"

fi

if [ -w $file ]

then

echo "文件可写"

else

echo "文件不可写"

fi

if [ -x $file ]

then

echo "文件可执行"

else

echo "文件不可执行"

fi

if [ -f $file ]

then

echo "文件为普通文件"

else

echo "文件为特殊文件"

fi

if [ -d $file ]

then

echo "文件是个目录"

else

echo "文件不是个目录"

fi

if [ -s $file ]

then

echo "文件不为空"

else

echo "文件为空"

fi

if [ -e $file ]

then

echo "文件存在"

else

echo "文件不存在"

fi

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

复制代码
文件可读
文件可写
文件可执行
文件为普通文件
文件不是个目录
文件不为空
文件存在

自增和自减操作符

尽管 Shell 本身没有像 C、C++ 或 Java 那样的 ++ 和 -- 操作符,但可以通过其他方式实现相同的功能。以下是一些常见的方法:

使用 let 命令

let 命令允许对整数进行算术运算。

#!/bin/bash

初始化变量

num=5

自增

let num++

自减

let num--

echo $num

使用 $(( )) 进行算术运算

$(( )) 语法也是进行算术运算的一种方式。

#!/bin/bash

初始化变量

num=5

自增

num=$((num + 1))

自减

num=$((num - 1))

echo $num

使用 expr 命令

expr 命令可以用于算术运算,但在现代脚本中不如 let$(( )) 常用。

#!/bin/bash

初始化变量

num=5

自增

num=(expr num + 1)

自减

num=(expr num - 1)

echo $num

使用 (( )) 进行算术运算

与 $(( )) 类似,(( )) 语法也可以用于算术运算。

#!/bin/bash

初始化变量

num=5

自增

((num++))

自减

((num--))

echo $num

Shell echo命令

Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式:

复制代码
echo string

您可以使用echo实现更复杂的输出格式控制。

1.显示普通字符串:

复制代码
echo "It is a test"
#这里的双引号完全可以省略,以下命令与上面实例效果一致:
echo It is a test

2.显示转义字符

复制代码
echo "\"It is a test\""
结果将是:
"It is a test"
同样,双引号也可以省略

3.显示变量

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

复制代码
#!/bin/sh
read name 
echo "$name It is a test"

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

复制代码
[root@www ~]# sh test.sh
OK                     #标准输入
OK It is a test        #输出

4.显示换行

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

#输出结果:

复制代码
OK!

It is a test

5.显示不换行

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

#输出结果:

复制代码
OK! It is a test

6.显示结果定向至文件

复制代码
echo "It is a test" > myfile

7.原样输出字符串,不进行转义或取变量(用单引号)

复制代码
echo '$name\"'
#输出结果:
$name\"

8.显示命令执行结果

复制代码
echo `date`
结果将显示当前日期
Thu Jul 24 10:08:46 CST 2014

注意: 这里使用的是反引号 `, 而不是单引号 '。

Shell printf 命令

默认的 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。

printf 命令的语法:

复制代码
printf  format-string  [arguments...]

参数说明:

  • format-string: 一个格式字符串,它包含普通文本和格式说明符。
  • arguments: 用于填充格式说明符的参数列表。。

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

  • %s:字符串
  • %d:十进制整数
  • %f:浮点数
  • %c:字符
  • %x:十六进制数
  • %o:八进制数
  • %b:二进制数
  • %e:科学计数法表示的浮点数

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

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

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

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

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

printf 的转义序列

$ printf "a string, no processing:<%s>\n" "A\nB"

a string, no processing:<A\nB>

$ printf "a string, no processing:<%b>\n" "A\nB"

a string, no processing:<A

B>

$ printf "www.runoob.com \a"

www.runoob.com $ #不换行

Shell test 命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

num1=100

num2=100

if test \[num1\] -eq [num2]

then

echo '两个数相等!'

else

echo '两个数不相等!'

fi

输出结果:

两个数相等!

代码中的 [] 执行基本的算数运算,如:

#!/bin/bash

a=5

b=6

result=$[a+b] # 注意等号两边不能有空格

echo "result 为: $result"

结果为:

result 为: 11
num1="ru1noob"

num2="runoob"

if test num1 = num2

then

echo '两个字符串相等!'

else

echo '两个字符串不相等!'

fi

#输出结果:

复制代码
两个字符串不相等!

cd /bin

if test -e ./bash

then

echo '文件已存在!'

else

echo '文件不存在!'

fi

输出结果:

复制代码
文件已存在!

Shell 流程控制

if else-if else

if condition1

then

command1

elif condition2

then

command2

else

commandN

fi

if else 的 [...] 判断语句中大于使用 -gt,小于使用 -lt。

复制代码
if [ "$a" -gt "$b" ]; then
    ...
fi

如果使用 ((...)) 作为判断语句,大于和小于可以直接使用 > 和 <。

复制代码
if (( a > b )); then
    ...
fi

for 循环

for var in item1 item2 ... itemN

do

command1

command2

...

commandN

done

当变量值在列表里,for 循环即执行一次所有命令,使用变量名获取列表中的当前取值。命令可为任何有效的 shell 命令和语句。in 列表可以包含替换、字符串和文件名。

in列表是可选的,如果不用它,for循环使用命令行的位置参数。

for loop in 1 2 3 4 5

do

echo "The value is: $loop"

done

#输出结果:

复制代码
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5

顺序输出字符串中的字符:

复制代码
#!/bin/bash

for str in This is a string
do
    echo $str
done

#输出结果:

复制代码
This
is
a
string

while 语句

while condition

do

command

done
#!/bin/bash

int=1

while(( $int<=5 ))

do

echo $int

let "int++"

done

#运行脚本,输出:

复制代码
1
2
3
4
5

echo '按下 <CTRL-D> 退出'

echo -n '输入你最喜欢的网站名: '

while read FILM

do

echo "是的!$FILM 是一个好网站"

done

#运行脚本,输出类似下面:

复制代码
按下 <CTRL-D> 退出
输入你最喜欢的网站名:菜鸟教程
是的!菜鸟教程 是一个好网站

无限循环

无限循环语法格式:

复制代码
while :
do
    command
done

#或者

复制代码
while true
do
    command
done

#或者

复制代码
for (( ; ; ))

until 循环

condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。

以下实例我们使用 until 命令来输出 0 ~ 9 的数字:

#!/bin/bash

a=0

until [ ! $a -lt 10 ]

do

echo $a

a=`expr $a + 1`

done

case ... esac

多选择语句,语法格式如下:

case 值 in

模式1)

command1

command2

...

commandN

;;

模式2)

command1

command2

...

commandN

;;

esac

取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。

下面的脚本提示输入 1 到 4,与每一种模式进行匹配:

echo '输入 1 到 4 之间的数字:'

echo '你输入的数字为:'

read aNum

case $aNum in

  1. echo '你选择了 1'

;;

  1. echo '你选择了 2'

;;

  1. echo '你选择了 3'

;;

  1. echo '你选择了 4'

;;

*) echo '你没有输入 1 到 4 之间的数字'

;;

esac

#输入不同的内容,会有不同的结果,例如:

复制代码
输入 1 到 4 之间的数字:
你输入的数字为:
3
你选择了 3

跳出循环

break 命令

break 命令允许跳出所有循环(终止执行后面的所有循环)。

下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。

#!/bin/bash

while :

do

echo -n "输入 1 到 5 之间的数字:"

read aNum

case $aNum in

1|2|3|4|5) echo "你输入的数字为 $aNum!"

;;

*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"

break

;;

esac

done

#执行以上代码,输出结果为:

复制代码
输入 1 到 5 之间的数字:3
你输入的数字为 3!
输入 1 到 5 之间的数字:7
你输入的数字不是 1 到 5 之间的! 游戏结束

continue

continue 命令与 break 命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

Shell 函数

function \] funname \[()

{

action;

return int;

}

说明:

  • 1、可以带 function fun() 定义,也可以直接 fun() 定义,不带任何参数。
  • 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return 后跟数值 n(0-255).

#!/bin/bash

funWithReturn(){

echo "这个函数会对输入的两个数字进行相加运算..."

echo "输入第一个数字: "

read aNum

echo "输入第二个数字: "

read anotherNum

echo "两个数字分别为 aNum 和 anotherNum !"

return ((aNum+$anotherNum))

}

funWithReturn

echo "输入的两个数字之和为 $? !"
#输出类似下面:

复制代码
这个函数会对输入的两个数字进行相加运算...
输入第一个数字: 
1
输入第二个数字: 
2
两个数字分别为 1 和 2 !
输入的两个数字之和为 3 !

函数返回值在调用该函数后通过 $? 来获得。

**注意:**所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。

注意: return 语句只能返回一个介于 0 到 255 之间的整数,而两个输入数字的和可能超过这个范围。

要解决这个问题,可以修改 return 语句,直接使用 echo 输出和而不是使用 return:

funWithReturn(){

echo "这个函数会对输入的两个数字进行相加运算..."

echo "输入第一个数字: "

read aNum

echo "输入第二个数字: "

read anotherNum

sum=((aNum + $anotherNum))

echo "两个数字分别为 aNum 和 anotherNum !"

echo $sum # 输出两个数字的和

}

函数参数

#!/bin/bash

funWithParam(){

echo "第一个参数为 $1 !"

echo "第二个参数为 $2 !"

echo "第十个参数为 $10 !"

echo "第十个参数为 ${10} !"

echo "第十一个参数为 ${11} !"

echo "参数总数有 $# 个!"

echo "作为一个字符串输出所有参数 $* !"

}

funWithParam 1 2 3 4 5 6 7 8 9 34 73

#输出结果:

复制代码
第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11 个!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !

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

Shell 输入/输出重定向

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

>:以覆盖的方式写入,>>:以追加的方式写入

输出重定向

重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:

复制代码
command1 > file1

上面这个命令执行command1然后将输出的内容存入file1。

注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。

复制代码
$ echo "菜鸟教程:www.runoob.com" > users
$ cat users
菜鸟教程:www.runoob.com

输入重定向

和输出重定向一样,Unix 命令也可以从文件获取输入,语法为:

复制代码
command1 < file1

这样,本来需要从键盘获取输入的命令会转移到文件读取内容。

接着以上实例,我们需要统计 users 文件的行数,执行以下命令:

复制代码
$ wc -l users #wc:用于统计文件的行数、单词数或字节数。-l:选项(lines),表示统计行数。
2 users       #2:表示文件 users中共有 2 行内容。users:被统计的文件名。

#也可以将输入重定向到 users 文件:

复制代码
$  wc -l < users
2          #2:表示文件 users中共有 2 行内容

注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。

复制代码
command1 < infile > outfile

同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。

重定向深入讲解

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

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

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

复制代码
$ command 2>file
$ command 2>>file

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

复制代码
$ command > file 2>&1

#或者

$ command >> file 2>&1

其中 2>&1

  • 2> 表示重定向标准错误(stderr,文件描述符2)

  • &1 表示将标准错误指向标准输出当前的位置 (即 file)。

  • 最终效果是:标准错误也被写入 file,且和标准输出混合在一起。


等效写法

以下两种写法与 command > file 2>&1 完全等价:

  1. command &> file

    (Bash 等现代 Shell 支持的简洁写法,直接重定向所有输出)

  2. command >> file 2>&1

    >> 表示追加到文件末尾,而非覆盖)

Here Document

复制代码
command << delimiter
    document
delimiter

它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。

注意:

  • 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
  • 开始的delimiter前后的空格会被忽略掉。

在命令行中通过 wc -l 命令计算 Here Document 的行数:

复制代码
$ wc -l << EOF
    欢迎来到
    菜鸟教程
    www.runoob.com
EOF
3          # 输出结果为 3 行
$
复制代码
我们也可以将 Here Document 用在脚本中,例如:
复制代码
#!/bin/bash
cat << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF

#执行以上脚本,输出结果:

复制代码
欢迎来到
菜鸟教程
www.runoob.com

/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 文件包含

Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。

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

或

source filename

创建两个 shell 脚本文件。

test1.sh 代码如下:

复制代码
#!/bin/bash
url="http://www.runoob.com"

test2.sh 代码如下:

复制代码
#!/bin/bash
#使用 . 号来引用test1.sh 文件
. ./test1.sh

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

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

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

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

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

相关推荐
阿图灵1 小时前
文章记单词 | 第34篇(六级)
学习·学习方法
小王努力学编程1 小时前
贪心算法学习C++
开发语言·c++·学习·算法·leetcode·贪心算法
-曾牛1 小时前
Git完全指南:从入门到精通版本控制 ------- Git 工作区、暂存区和版本库(4)
java·git·学习·个人开发
superior tigre2 小时前
C++学习:六个月从基础到就业——面向对象编程:封装、继承与多态
c++·学习·microsoft
Suckerbin2 小时前
Pikachu靶场——Cross-Site Scripting
学习·安全·网络安全
大雄野比2 小时前
【scikit-learn基础】--『监督学习』之 贝叶斯分类
学习·分类·scikit-learn
Dovis(誓平步青云)4 小时前
【数据结构】励志大厂版·初阶(复习+刷题):线性表(顺序表)
c语言·数据结构·经验分享·笔记·学习·算法·学习方法
pumpkin845145 小时前
学习笔记十三—— 理解 Rust 闭包:从语法到 impl Fn vs Box<dyn Fn>
笔记·学习·rust
·醉挽清风·5 小时前
学习笔记—C++—模板初阶
开发语言·c++·笔记·学习
虾球xz6 小时前
游戏引擎学习第216天
服务器·c++·学习·游戏引擎