本帖最后由 小小糖 于 2019-2-25 15:24 编辑
默认一行处理一条命令,但也可以用;分割多条命令,按顺序执行。
脚本文件中第一行必须用#! path-to-shell指定所用的shell。
除了第一行之外的#都是注释的开始标记,注释此行中#之后的内容
脚本最好要有执行权限,最好以sh作为后缀
1. 变量
变量的值都是字符串,一个变量可以处于已定义和未定义两种状态,已定义的变量可以有值也可以为空。
定义变量时不要有空格:variable=value
命令替换
可以从命令输出中提取信息,并赋值给变量。有两种形式:
variable=commands: commands用反引号字符扩起来。
variable=$(commands)
命令替换中的命令在subshell中执行,其中对变量的改变并不会反映到当前shell脚本中。
2. 重定向
重定向其实是文件描述符的复制,实现通过一个描述符访问另一个文件。
输出重定向
把某个输出流的内容通过另一个输出流输出。 比如:
echo "hello world" > tempfile,其实是以截断只写方式打开tempfile文件(文件描述符fd),然后把文件描述符1(标准输出)作为fd的复制,并关闭fd(如果明白linux内核中file和inode的关系会更好理解),这样程序中输出给文件描述符1的数据都重定向给了tempfile。
echo "hello world" >> tempfile,与上面的区别是以添加的方式打开tempfile。
cat /etc/shadow 2> error,错误信息会放到error文件中。
输入重定向
把某个文件当作标准输入,从中读取数据。 比如:
cat <error
这里cat本应该从标准输入0接收数据然后输出到标准输出,输入重定向相当于在文件描述符0上打开文件error
内联输入重定向无需使用文件进行重定向,只需要在命令行中指定用于输入重定向的数据。必须指定文本标记来划分数据的开始和结尾。
- $ wc << flag
- > data
- > haha
- > flag
- 2 2 8
- $
复制代码 其本质是把你临时输入的内容缓存下来作为命令的输入。
更复杂的重定向
a.out 3<>temp: 在文件描述符3上打开temp文件进行读写。 <>左边是文件描述符,而右边是文件名,如果想要重定向到另一个文件描述符,可用&n的形式。
a.out 1>&2表示把原本输出给标准输出的内容交给标准错误。
3. 管道
管道其实是一个环形缓冲区,左边的程序把数据放入缓冲区,右边的程序把数据读走。表现出来就是右边的程序把左边程序的输出作为输入。
4. 数值计算
shell不擅长数值计算,它的所有变量都是字符串类型的。
把一个表达式结果赋值给某个变量可以这样:
variable=$[ operation ]
但是operation只能是整数计算。
bc计算器(bash calculator): bc是一个交互式计算器。可以定义变量,使用注释,创建函数和编程语句等,功能丰富。
bc中的scale变量定义了小数的位数,默认时为0,使用小数的话记得先设置这个变量。
可以通过variable=$(echo "options; expression" | bc)的形式在shell脚本中使用bc。在options中可以定义变量,expression中定义了需要执行的数学计算,输出结果赋值给variable。比如:
- $ cat test.sh
- #! /bin/bash
- var1=100
- var2=40
- var3=$(echo "scale=4; $var1 / $var2" | bc)
- echo ans is $var3
- $
复制代码 如果需要大量的运算,难以在一行内列出多个表达式,可以使用内联重定向:
- ➜ shell_scripts git:(dev) ✗ cat bc1.sh
- #! /bin/bash
- var1=10.34
- var2=21.33
- var3=33.2
- var4=87
- var5=$(bc EOF
- scale=4
- a1=($var1 * $var2)
- b1=($var3 + $var4)
- a1 + a2
- EOF
- )
- echo ans is $var5
- ➜ shell_scripts git:(dev) ✗ bash bc1.sh
- ans is 220.5522
- ➜ shell_scripts git:(dev) ✗
复制代码 5. 退出脚本shell中变量$?来保存上个已执行命令的退出状态码。默认情况下,shell脚本会以脚本中最后一个命令的退出状态码退出。 脚本中可以使用exit <status>来指定脚本的退出状态码。 6. 流程控制if-then语句- if command1;then
- commands
- elif comand2;then
- commands
- else
- commands
- fi
复制代码 if语句会运行if后面的命令,如果命令的退出状态码是0(正常退出),位于then部分的命令就会被执行;否则,else部分的命令会被执行。elif分支或者else分支可以省略。
test命令
用法:test [expression]
如果expression为true,test命令的返回状态为0,否则为1(包括无法识别的表达式)。
expression的形式多种多样:
(exp): 就是exp的值
!exp: exp取反
exp1 -a exp2: exp1和exp2相与的结果
exp1 -o exp2: exp1和exp2相或的结果
字符串比较
-n string: string的长度非0
string: 等价与-n string
-z string: string的长度为0
str1 = str2:这两个字符串是否相等
str1 != str2:
str1 < str2:
str1 > str2:
数值比较
int1 -eq int2:把两边当成整数,比较是否相同
int1 -ge int2: int1大于或等于 int2?
int1 -gt int2: int1 大于 int2 ?
int1 -le int2: 小于或等于
int1 -lt int2: 小于
int1 -ne int2: 不等于
文件比较
-d file: 文件是否存在且为目录
-e file: 是否存在
-f file: 是否存在且为文件
-r file: 是否存在且可读
-s file: 是否存在且非空
-w file: 是否存在且可写
-x file: 是否存在且可执行
-O file: 是否存在且属当前用户所有
-G file: 是否存在且默认组与当前用户相同
file1 -nt file2: 左边比右边新
file1 -ot file2: 左边比右边旧
test命令增强了if语句的判断能力,把逻辑表达式转换成命令执行状态。
bash shell提供了另一种条件测试方法,无需使用test指令。
- if [ condition ];then
- commands
- fi
复制代码 这种形式也可以使用 [ conditions1 ] && [condition2 ]和 [ condition1 ] || [condition2]这样的复合逻辑式。
在字符串比较时,>要进行转义,否则认为是重定向。
高级特性
双括号命令
$((expression))形式:这种形式的expression可以是任意的数学赋值或者比较表达式,其中的变量不用$展开。支持自增自减、移位、位运算等运算符。
可以在if语句中使用双括号命令,也可以在脚本的普通命令里使用它来进行数学赋值。
不需要将双括号里的大于号转义
双方括号
[[expression]]双方括号提供了字符串比较的高级特性。它拥有test命令中的标准字符串比较,还提供了模式匹配功能。比如:
- ➜ cat test_double_square_brackets.sh
- #! /bin/bash
- # using pattern matching
- #
- if [[ $USER == i* ]] ; then
- echo "Hello $USER"
- else
- echo "Sorry, I don't know you"
- fi
- ➜ bash test_double_square_brackets.sh
- Hello invoker
- ➜
复制代码注意:普通的if语句使用一个等号判断是否想等,而双括号和双方括号都是采用两个等号。
case命令
- case variable in
- pattern1 | pattern2)
- commands1;;
- pattern3)
- commands2;;
- *)
- default-commands;;
- esac
复制代码case命令从上到下寻找,如果变量与某个分支的模式匹配,则执行此分支的命令,然后退出case。由于*匹配所有字符串,可作为最后的默认分支。分支的命令块结束时要用两个分号。
循环for命令for命令允许你创建一个便利一系列值的循环。每次使用其中的一个值来执行定义好的一组命令。 - for var in list;do
- commands
- done
复制代码list是由IFS(internal field separator)分隔的字符串。默认是IFS是空格、制表符或者换行符。也可以改变IFS的值:IFS=' \n'表示换行和冒号都是字段分隔符。(换行和制表符用$'\n'和$'\t'表示) bash对for命令的扩展 - for ((variable assignment; condition; iteration process));do
- commands
- done
复制代码这种类似c语言风格的for循环并没有遵循shell标准:变量赋值可以有空格;条件中的变量不用$展开;迭代过程的算术表达式不需要expr。
while命令
- while test-command;do
- commands
- done
复制代码test-command可以是命令本身(判断返回状态),也可以是用test或者方括号封装的逻辑表达式。测试命令可以指定多个,只有最后一个巨额定判断结果。
until命令
- until test-command;do
- commands
- done
复制代码until命令与while类似,只是当条件成立是退出循环。
break和continue与C语言不同的是break n可以直接跳出n层循环。
处理循环的输出如果想要对某个循环中的输出进行重定向或者管道可以在done命令之后添加相应的处理。
|