赞
踩
# 以下两种方式都可以指定 shell 解释器为 bash,第二种方式更好
#!/bin/bash
#!/usr/bin/env bash
#!/usr/bin/env bash
- 1
这样做的好处是,系统会自动在
PATH
环境变量中查找你指定的程序(本例中的bash
)。相比第一种写法,你应该尽量用这种写法,因为程序的路径是不确定的。这样写还有一个好处,操作系统的PATH
变量有可能被配置为指向程序的另一个版本。比如,安装完新版本的bash
,我们可能将其路径添加到PATH
中,来“隐藏”老版本。如果直接用#!/bin/bash
,那么系统会选择老版本的bash
来执行脚本,如果用#!/usr/bin/env bash
,则会使用新版本。
#
开头,到行尾结束。:<<EOF
开头,到 EOF
结束。:<<EOF
echo '这是多行注释'
echo '这是多行注释'
echo '这是多行注释'
EOF
输出普通字符串:
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
# NO
echo -e "YES\c" # -e 开启转义 \c 不换行
echo "NO"
# Output:
# YESNO
输出重定向至文件
echo "test" > test.txt
输出执行结果
echo `pwd`
# Output:(当前目录路径)
printf 用于格式化输出字符串。
# 单引号 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 # def printf "%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
Bash 中没有数据类型,bash 中的变量可以保存一个数字、一个字符、一个字符串等等。同时无需提前声明变量,给变量赋值会直接创建变量。
访问变量的语法形式为:${var}
和 $var
。
变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,所以推荐加花括号。
变量赋值不能带空格,word = "hello"
错误
word="hello"
echo ${word}
# Output: hello
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
rword="hello"
echo ${rword}
readonly rword
# rword="bye" # 如果放开注释,执行时会报错
使用 unset 命令可以删除变量。变量被删除后不能再次使用。unset 命令不能删除只读变量。
dword="hello" # 声明变量
echo ${dword} # 输出变量值
# Output: hello
unset dword # 删除变量
echo ${dword}
# Output: (空)
export
关键字,shell 脚本也可以定义环境变量。常见的环境变量:
变量 | 描述 |
---|---|
$HOME | 当前用户的用户目录 |
$PATH | 用分号分隔的目录列表,shell 会到这些目录中查找命令 |
$PWD | 当前工作目录 |
$RANDOM | 0 到 32767 之间的整数 |
$UID | 数值类型,当前用户的用户 ID |
$PS1 | 主要系统输入提示符 |
$PS2 | 次要系统输入提示符 |
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 bash
text="hello"
echo `expr index "${text}" ll`
# Execute: ./str-demo5.sh
# Output:
# 3
查找 ll
子字符在 hello
字符串中的起始位置。
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 blue
echo ${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
下表列出了常用的算术运算符,假定变量 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 为 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 为 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。 |
以下介绍 Shell 的逻辑运算符,假定变量 x 为 10,变量 y 为 20:
运算符 | 说明 | 举例 |
---|---|---|
&& | 逻辑的 AND | [[ ${x} -lt 100 && ${y} -gt 100 ]] 返回 false |
|| | 逻辑的 OR | [[ ${x} -lt 100 || ${y} -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。 | [ $a ] 返回 true |
文件测试运算符用于检测 Unix 文件的各种属性。
属性检测描述如下:
操作符 | 说明 | 举例 |
---|---|---|
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] 返回 false。 |
-d file | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] 返回 false。 |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] 返回 true。 |
-g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] 返回 false。 |
-k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] 返回 false。 |
-p file | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] 返回 false。 |
-u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] 返回 false。 |
-r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回 true。 |
-w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
-x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回 true。 |
-s file | 检测文件是否为空(文件大小是否大于 0),不为空返回 true。 | [ -s $file ] 返回 true。 |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true。 |
(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" ]]
then
echo ""abc" -eq "abc" result is: true"
fi
# Output: abc -eq abc result is: true
(2)if else
语句
同样,我们可以使用if..else
语句,例如:
if [[ 2 -ne 1 ]]; then
echo "true"
else
echo "false"
fi
# Output: true
(3)if elif else
语句
有些时候,if..else
不能满足我们的要求。别忘了if..elif..else
,使用起来也很方便。
x=10
y=20
if [[ ${x} > ${y} ]]; then
echo "${x} > ${y}"
elif [[ ${x} < ${y} ]]; then
echo "${x} < ${y}"
else
echo "${x} = ${y}"
fi
# Output: 10 < 20
(4)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
for arg in elem1 elem2 ... elemN
do
### 语句
done
for i in {1..5}; do echo $i; done
for (( i = 0; i < 10; i++ )); do
echo $i
done
while [[ condition ]]
do
### 语句
done
until
循环跟while
循环正好相反。它跟while
一样也需要检测一个测试条件,但不同的是,只要该条件为 假 就一直执行循环:
x=0
until [[ ${x} -ge 5 ]]; do
echo ${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 bash
PS3="Choose the package manager: "
select ITEM in bower npm gem pip
do
echo -n "Enter the package name: " && read PACKAGE
case ${ITEM} in
bower) 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
语句用来跳过某次迭代。
bash 函数定义语法如下:
[ function ] funname [()] {
action;
[return int;]
}
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/449726
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。