#!/bin/bash
# 脚本的第一行叫 shebang,用来告知系统如何执行该脚本:
# 参见: http://en.wikipedia.org/wiki/Shebang_(Unix)
# 如你所见,注释以 # 开头,shebang 也是注释。
#-------------Shell提示符($和#的区别)---------------
# 普通用户的提示符是$,root用户的提示符是#
su #切换到root用户(需输入密码)
su - username #切换回普通用户
# 显示 “Hello world!”
echo Hello world!
# 每一句指令以换行或分号隔开:
echo ‘This is the first line‘; echo ‘This is the second line‘
#--------------变量定义和使用--------------
# 定义变量三种方式(注意赋值符左右不能有空格!)
variable=value #方式一
variable=‘value‘ #方式二
variable="value" #方式三
# 声明一个变量:
Variable="Some string"
# 下面是错误的做法:
Variable = "Some string"
# Bash 会把 Variable 当做一个指令,由于找不到该指令,因此这里会报错。
# 也不可以这样:
Variable= ‘Some string‘
# Bash 会认为 ‘Some string‘ 是一条指令,由于找不到该指令,这里再次报错。
# (这个例子中 ‘Variable=‘ 这部分会被当作仅对 ‘Some string‘ 起作用的赋值。)
# 使用变量:
echo $Variable #方式一
echo ${Variable} #方式二(推荐)
echo "$Variable" #方式三
echo ‘$Variable‘ #方式四(错误)
# 当你赋值 (assign) 、导出 (export),或者以其他方式使用变量时,变量名前不加 $。
# 如果要使用变量的值, 则要加 $。
# 注意: ‘ (单引号) 不会展开变量(即会屏蔽掉变量)。
# 将命令的结果赋值给变量
variable=`command` #方式一
variable=$(command) #方式二(推荐)
# 只读变量
readonly variable
# 删除变量
unset variable_name
#变量类型分三种,局部变量、环境变量、shell变量
#特殊变量
变量 |
含义 |
$0 |
当前脚本文件名 |
$n |
传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。 |
$# |
传递给脚本或函数的参数个数。 |
$* |
传递给脚本或函数的所有参数。 |
$@ |
传递给脚本或函数的所有参数。被双引号(" ")包含时,与 $* 稍有不同,下面将会讲到。 |
$? |
上个命令的退出状态,或函数的返回值。 |
$$ |
当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。 |
#变量替换
形式 |
说明 |
${var} |
变量本来的值 |
${var:-word} |
如果变量 var 为空或已被删除(unset),那么返回 word,但不改变 var 的值。 |
${var:=word} |
如果变量 var 为空或已被删除(unset),那么返回 word,并将 var 的值设置为 word。 |
${var:?message} |
如果变量 var 为空或已被删除(unset),那么将消息 message 送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。 若此替换出现在Shell脚本中,那么脚本将停止运行。 |
${var:+word} |
如果变量 var 被定义,那么返回 word,但不改变 var 的值。 |
# 在变量内部进行字符串代换
echo ${Variable/Some/A}
# 会把 Variable 中首次出现的 "some" 替换成 “A”。
# 变量的截取
Length=7
echo ${Variable:0:Length}
# 这样会仅返回变量值的前7个字符
# 变量的默认值
echo ${Foo:-"DefaultValueIfFooIsMissingOrEmpty"}
# 对 null (Foo=) 和空串 (Foo="") 起作用; 零(Foo=0)时返回0
# 注意这仅返回默认值而不是改变变量的值
# 内置变量:
# 下面的内置变量很有用
echo "Last program return value: $?"
echo "Script‘s PID: $$"
echo "Number of arguments: $#"
echo "Scripts arguments: $@"
echo "Scripts arguments separated in different variables: $1 $2..."
#-------------------Shell运算符----------------------
#expr 是一款表达式计算工具,使用它能完成表达式的求值操作。表达式和运算符之间要有空格,完整的表达式要被 ` ` 包含
val=`expr 2 + 2`
a=10
b=20
val=`expr $a + $b`
#算术运算符列表
运算符 |
说明 |
举例 |
+ |
加法 |
`expr $a + $b` 结果为 30。 |
- |
减法 |
`expr $a - $b` 结果为 10。 |
* |
乘法 |
`expr $a \* $b` 结果为 200。 |
/ |
除法 |
`expr $b / $a` 结果为 2。 |
% |
取余 |
`expr $b % $a` 结果为 0。 |
= |
赋值 |
a=$b 将把变量 b 的值赋给 a。 |
== |
相等。用于比较两个数字,相同则返回 true。 |
[ $a == $b ] 返回 false。 |
!= |
不相等。用于比较两个数字,不相同则返回 true。 |
[ $a != $b ] 返回 true。 |
#关系运算符列表
运算符 |
说明 |
举例 |
-eq |
检测两个数是否相等,相等返回 true。 |
[ $a -eq $b ] 返回 true。 |
-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。 |
#布尔运算符列表
运算符 |
说明 |
示例 |
! |
非运算,表达式为 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。 |
#字符串运算符列表
运算符 |
说明 |
示例 |
= |
检测两个字符串是否相等,相等返回 true。 |
[ $a = $b ] 返回 false。 |
!= |
检测两个字符串是否相等,不相等返回 true。 |
[ $a != $b ] 返回 true。 |
-z |
检测字符串长度是否为0,为0返回 true。 |
[ -z $a ] 返回 false。 |
-n |
检测字符串长度是否为0,不为0返回 true。 |
[ -z $a ] 返回 true。 |
str |
检测字符串是否为空,不为空返回 true。 |
[ $a ] 返回 true。 |
#文件测试运算符列表
运算符 |
说明 |
示例 |
-b |
检测文件是否是块设备文件,如果是,则返回 true。 |
[ -b $file ] 返回 false。 |
-c |
检测文件是否是字符设备文件,如果是,则返回 true。 |
[ -b $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。 |
#----------------------字符串---------------------------
# 单引号(所有字符都原样输出)
str=‘this is a string‘
# 双引号
your_name=‘qinjx‘
str="Hello, I know your are \"$your_name\"! \n"
# 拼接字符串
your_name="qinjx"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
# 获取字符串长度
string="abcd"
echo ${#string} #输出 4
# 提取子字符串
string="alibaba is a great company"
echo ${string:1:4} #输出liba
# 查找子字符串
string="alibaba is a great company"
echo `expr index "$string" is`
#----------------Shell数组:shell数组的定义、数组长度----------------
# 定义数组
array_name=(value0 value1 value2 value3)
# 或者
array_name=(
value0
value1
value2
value3
)
# 还可以单独定义数组的各个分量
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
# 读取数组
# 读取数组元素值的一般格式是: ${array_name[index]}
valuen=${array_name[2]}
# 使用@ 或 * 可以获取数组中的所有元素
${array_name[*]}
${array_name[@]}
# 获取数组的长度
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}
#------------------printf函数-------------------------
# printf 命令的语法:
printf format-string [arguments...]
# 这里仅说明与C语言printf()函数的不同:
# 1)printf 命令不用加括号
# 2)format-string 可以没有引号,但最好加上,单引号双引号均可。
# 3)参数多于格式控制符(%)时,format-string 可以重用,可以将所有参数都转换。
# 4)arguments 使用空格分隔,不用逗号。
#--------------------读取变量--------------------------
# 读取输入:
echo "What‘s your name?"
read Name # 这里不需要声明新变量
echo Hello, $Name!
#方式二
read -p "Enter your name:"
read name
echo Hello,$name
#--------------------if条件语句------------------------
# Shell 有三种 if ... else 语句:
# 1)if ... fi 语句;
# 2)if ... else ... fi 语句;
# 3)if ... elif ... else ... fi 语句。
# 形式1:
if [ expression ]
then
Statement(s) to be executed if expression is true
fi
# 形式2:
if [ expression ]
then
Statement(s) to be executed if expression is true
else
Statement(s) to be executed if expression is not true
fi
# 形式3:
if [ expression 1 ]
then
Statement(s) to be executed if expression 1 is true
elif [ expression 2 ]
then
Statement(s) to be executed if expression 2 is true
elif [ expression 3 ]
then
Statement(s) to be executed if expression 3 is true
else
Statement(s) to be executed if no expression is true
fi
# if ... else 语句也经常与 test 命令结合使用
num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
echo ‘The two numbers are equal!‘
else
echo ‘The two numbers are not equal!‘
fi
# 通常的 if 结构看起来像这样:
# ‘man test‘ 可查看更多的信息
if [ $Name -ne $USER ]
then
echo "Your name isn‘t your username"
else
echo "Your name is your username"
fi
# 根据上一个指令执行结果决定是否执行下一个指令
echo "Always executed" || echo "Only executed if first command fails"
echo "Always executed" && echo "Only executed if first command does NOT fail"
# 在 if 语句中使用 && 和 || 需要多对方括号
if [ $Name == "Steve" ] && [ $Age -eq 15 ]
then
echo "This will run if $Name is Steve AND $Age is 15."
fi
if [ $Name == "Daniya" ] || [ $Name == "Zach" ]
then
echo "This will run if $Name is Daniya OR Zach."
fi
#------------------case分支语句----------------------------
# Bash 的 case 语句与 Java 和 C++ 中的 switch 语句类似:
case "$Variable" in
# 列出需要匹配的字符串
0) echo "There is a zero.";;
1) echo "There is a one.";;
*) echo "It is not null.";;
esac
#------------------for循环、while循环、until循环---------------------
# 循环遍历给定的参数序列:
# 变量$Variable 的值会被打印 3 次。
for Variable in {1..3}
do
echo "$Variable"
done
# 或传统的 “for循环” :
for ((a=1; a <= 3; a++))
do
echo $a
done
# 也可以用于文件
# 用 cat 输出 file1 和 file2 内容
for Variable in file1 file2
do
cat "$Variable"
done
# 或作用于其他命令的输出
# 对 ls 输出的文件执行 cat 指令。
for Output in $(ls)
do
cat "$Output"
done
#--------------------------------
# while 循环:
while [ true ]
do
echo "loop body here..."
break
done
#--------------------------------
# until 循环
until command
do
Statement(s) to be executed until command is true
done
#--------------------函数-------------------------------------
#在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...,当n>=10时,需要使用${n}来获取参数。
# 你也可以使用函数
# 定义函数:
function foo ()
{
echo "Arguments work just like script arguments: $@"
echo "And: $1 $2..."
echo "This is a function"
return 0
}
# 更简单的方法
bar ()
{
echo "Another way to declare functions!"
return 0
}
# 调用函数
foo "My name is" $Name
#--------------------其他-----------------------
# 表达式的格式如下:
echo $(( 10 + 5 ))
# 与其他编程语言不同的是,bash 运行时依赖上下文。比如,使用 ls 时,列出当前目录。
ls
# 指令可以带有选项:
ls -l # 列出文件和目录的详细信息
# 前一个指令的输出可以当作后一个指令的输入。grep 用来匹配字符串。
# 用下面的指令列出当前目录下所有的 txt 文件:
ls -l | grep "\.txt"
# 重定向输入和输出(标准输入,标准输出,标准错误)。
# 以 ^EOF$ 作为结束标记从标准输入读取数据并覆盖 hello.py :
cat > hello.py << EOF
#!/usr/bin/env python
from __future__ import print_function
import sys
print("#stdout", file=sys.stdout)
print("#stderr", file=sys.stderr)
for line in sys.stdin:
print(line, file=sys.stdout)
EOF
# 重定向可以到输出,输入和错误输出。
python hello.py < "input.in"
python hello.py > "output.out"
python hello.py 2> "error.err"
python hello.py > "output-and-error.log" 2>&1
python hello.py > /dev/null 2>&1
# > 会覆盖已存在的文件, >> 会以累加的方式输出文件中。
python hello.py >> "output.out" 2>> "error.err"
# 覆盖 output.out , 追加 error.err 并统计行数
info bash ‘Basic Shell Features‘ ‘Redirections‘ > output.out 2>> error.err
wc -l output.out error.err
# 运行指令并打印文件描述符 (比如 /dev/fd/123)
# 具体可查看: man fd
echo <(echo "#helloworld")
# 以 "#helloworld" 覆盖 output.out:
cat > output.out <(echo "#helloworld")
echo "#helloworld" > output.out
echo "#helloworld" | cat > output.out
echo "#helloworld" | tee output.out >/dev/null
# 清理临时文件并显示详情(增加 ‘-i‘ 选项启用交互模式)
rm -v output.out error.err output-and-error.log
# 一个指令可用 $( ) 嵌套在另一个指令内部:
# 以下的指令会打印当前目录下的目录和文件总数
echo "There are $(ls | wc -l) items here."
# 反引号 `` 起相同作用,但不允许嵌套
# 优先使用 $( ).
echo "There are `ls | wc -l` items here."
# 有很多有用的指令需要学习:
# 打印 file.txt 的最后 10 行
tail -n 10 file.txt
# 打印 file.txt 的前 10 行
head -n 10 file.txt
# 将 file.txt 按行排序
sort file.txt
# 报告或忽略重复的行,用选项 -d 打印重复的行
uniq -d file.txt
# 打印每行中 ‘,‘ 之前内容
cut -d ‘,‘ -f 1 file.txt
# 将 file.txt 文件所有 ‘okay‘ 替换为 ‘great‘, (兼容正则表达式)
sed -i ‘s/okay/great/g‘ file.txt
# 将 file.txt 中匹配正则的行打印到标准输出
# 这里打印以 "foo" 开头, "bar" 结尾的行
grep "^foo.*bar$" file.txt
# 使用选项 "-c" 统计行数
grep -c "^foo.*bar$" file.txt
# 如果只是要按字面形式搜索字符串而不是按正则表达式,使用 fgrep (或 grep -F)
fgrep "^foo.*bar$" file.txt
# 以 bash 内建的 ‘help‘ 指令阅读 Bash 自带文档:
help
help help
help for
help return
help source
help .
# 用 man 指令阅读相关的 Bash 手册
apropos bash
man 1 bash
man bash
# 用 info 指令查阅命令的 info 文档 (info 中按 ? 显示帮助信息)
apropos info | grep ‘^info.*(‘
man info
info info
info 5 info
# 阅读 Bash 的 info 文档:
info bash
info bash ‘Bash Features‘
info bash 6
info --apropos bash