学习 Shell 脚本
Shell
Shell 编程跟 JavaScript、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
Linux 的 Shell 种类众多,常见的有:
- Bourne Shell(/usr/bin/sh或/bin/sh)
- Bourne Again Shell(/bin/bash)
- C Shell(/usr/bin/csh)
- K Shell(/usr/bin/ksh)
- Shell for Root(/sbin/sh)
- ...
在一般情况下,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh,它同样也可以改为 #!/bin/bash。
#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序。
#!/bin/bash
echo "hello world!"
运行 shell 方法有两种:
- 保存为文件然后执行。
- 直接调用解释器执行。此方法不需要指定解释器。
变量
first_name='f'
secname2='s'
lastName='l'
_private='p'
# 变量和等号间不能有空格
# 只能用数字字母下划线
# 开头不能数字
# 中间不能空格可以使用下划线
# 不嫩使用 bash 关键字
# 语句赋值
for file in `ls /etc/` # 或
for file in $(ls /ect/)
# 将 /etc/ 下文件名循环赋值给 file
# 使用变量
lang=''
echo $lang
echo ${lang}
# 花括号可选
# 为了方便帮助解释器识别边界
# 如果不加 则会识别为 langscript 变量
echo "$langscript"
echo "${lang}script"
# 重新定义变量
# 第二次赋值时不用加 $
# 使用变量时才加
f='f'
echo $f
f='a'
echo $f
# 只读变量
f='f'
readonly f
# 删除变量
f='f'
unset f
变量类型
运行shell时,会同时存在三种变量:
- 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
- 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
- shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行
字符串
str='str'
# 可用单引号、双引号以及不使用引号
# 单引号
# 任何字符都会原样输出 无法使用变量
# 不能出现单独单引号
# 转义也不行
# 但可以成对出现作为字符串拼接使用
# 双引号
# 可包含变量
# 可出现转义字符
# 字符串长度
echo ${#str} # 3
# 变量为数组时
${str}
# 等价于
${sti[0]}
# 截取字符串
echo ${str:0:2} # st
# 查找子字符串
# 查找 i 或 o 的位置 返回先出现的那个
echo `expr index "$string" io`
数组
# bash 支持 一维数组 不支持 多维数组
# 不限制数组长度
# 下标从零开始
arr=(1,2,3,4)
# 可使用不连续下标
arr[4]=5
# 获取元素
# ${array[index]}
${arr[0]} # 1
${arr[@]} # 1,2,3,4,5
# 数组长度
${#arr[@]}
# 或
${#arr[*]}
# 单个元素长度
${#arr[0]}
注释
# 单行注释
:<<EOF
注释内容
注释内容
注释内容
EOF
# EOF 可改为其他符号
:<<'
awdawd
awda
'
:<<!
awdawd
awdaw
!
传参
#!/bin/bash
$0 # 文件本身
$1 # 第一个参数
$2 # 第二个参数
$n # 第n个参数
# 特殊字符
$# # 传参数
$* # 单字符输出所有参数
$$ # 当前进程id
$! # 后台运行的最后一个进程 id
$@ # 数组输出 与 $* 相同 使用时加引号
$- # 显示当前 shell 使用选项
$? # 显示最后命令退出状态 0 无错 无值则有错
关联数组
declare -A arr=(['one']=1,['two']=2)
!arr # 输出所有键
运算符
支持算术、关系、布尔、字符串、文件测试等运算符。
条件表达式 需要放在方括号内 且 需要空格 [ $a == $b ]
数学运算符
原生 bash
不支持数学运算。可通过其他命令实现。
如 expr
或 awk
。
expr
最常用。可使用它完成表达式的求值操作。
val=`expr 2 + 2`
# 需要注意 用的是 反引号 ` 而不是 单引号 '
# + - * / %(取余)
# = 赋值
# == 相等
# != 不相等
比较运算符
-eq # 相等
-ne # 不等
-gt # 大于
-lt # 小于
-ge # 大于等于
-le # 小于等于
布尔运算符
! # 非
-o # 或
-a # 与
逻辑运算符
&& # 逻辑和
|| # 逻辑或
字符串运算符
= # 字符串相等
!= # 字符串不相等
-z # 长度是否为 0
-n # 是否长度不为 0
$ # 是否不为空
文件测试运算符
[ -b file ] # 检测文件是否是块设备文件
[ -c file ] # 检测文件是否是字符设备文件
[ -d file ] # 检测文件是否是目录
[ -f file ] # 检测文件是否是普通文件(即不是目录也非设备文件)
[ -g file ] # 检测文件是否设置 SGID 位
[ -k file ] # 检测文件是否粘着位(Sticky Bit)
[ -p file ] # 检测文件是否是有名管道
[ -u file ] # 检测文件是否设置 SUID 位
[ -r file ] # 检测文件是否可读
[ -w file ] # 检测文件是否可写
[ -x file ] # 检测文件是否可执行
[ -s file ] # 检测文件是否为空(即文件大小为 0 )
[ -e file ] # 检测文件是否存在
[ -S file ] # 检测文件是否 socket
[ -L file ] # 检测文件是否存在且是符号连接
输出
echo -e '\'az\'\n' # -e 开启转义
# 换行即 \n
# 不换行为 \c
# 命令执行结果
# 需要反引号而不是单双引号
echo `date`
# printf 由 POSIX 标准所定义
# 因此使用 printf 的脚本比使用 echo 移植性好
# printf 使用引用文本或空格分隔的参数
# 外面可以在 printf 中使用格式化字符串
# 还可以制定字符串的宽度、左右对齐方式等
# printf format-string [arguments...]
# format-string 格式化字符串
# arguments... 参数列表
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
# %s %c %d %f 都是格式替代符
# 字符串 字符 整型 小数
# -10 表示宽度为 10 左对齐(没有 - 则右对齐)
# 会自动空格补全
# %4.2f 格式化小数 保留 2 位小数
# 转义字符
# \a 警告字符
# \b 后退
# \c 抑制(不显示)输出中结尾的换行字符(只在%b控制下的参数有效), 任何参数字符、接下来参数及任何格式字符串中的字符,都忽略
# \f 换页
# \n 换行
# \r 回车
# \t 水平制表符
# \v 垂直制表符
# \\ 转义 \
# \ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
# \0ddd 表示1到3位的八进制值字符
test
用于检查某个条件是否成立。
num1=100
num2=200
# [] 执行基本数学运算
if test $[num1] -eq $[num2]
then
echo true
else
echo false
fi
流程控制
if else
# 如果 条件成立
if condition
then #执行
command1
...
# 否则如果
elif condition
then
command2
...
# 否则
else
command3
...
fi
# 可写成一行
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
# if else 中的 [] 的大于小于需要用 -gt 好 -lt 代替
# 如果是 (( ... )) 则可以直接 ><
循环
for var in item1 item2 ... itemN
do
command
...
done
# 或
for var in item1 item2 ... itemN; do command1; command2… done;
while condition
do
command
done
# 无限循环
while ;
# or
# while true;
do command
done
# for (( ; ; ))
# until循环
# 会执行到条件为 true
until condition
do
command
...
done
# case ... esac
# 取值后面必须为单词 in
# 每一模式必须以右括号结束
# 取值可以为变量或常数
# 匹配发现取值符合某一模式后
# 其间所有命令开始执行直至 ;;
# * 为默认匹配
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
# 跳出循环
for (( ; ; ))
do
break
continue
done
函数
[function] funname [()] {
command
...
[return]
}
# function 是可选的
# return 可选
# 默认返回最后一条语句输出结果
# 参数
# 函数内部使用 $1 $2 访问
# 超过 10 以 ${10} 访问
# 特殊符号与文件传参相同
# 函数与命令的执行结果可作条件语句使用
# shell 中 0 为 true 其他为 false
输入输出重定向
需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
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 之间的内容作为输入。 |
shell 文件引用
# a.sh
# b.sh
#!/bin/bash
. ./a.sh
学习 Shell 脚本
http://localhost:8080/archives/c6900fd8-0a5b-4992-8171-53d526f495e0