1:基本知识
主要介绍shell语法
参数$符号
$0 :这个程式的执行名字
$n :这个程式的第n个参数值,n=1..9
$* :返回所有参数字符串,此选项参数可超过9个。
$@ :跟$*类似,参数数组
$# :参数个数,不包含脚本
$$ :这个程式的PID(脚本运行的当前进程ID号)
$! :执行上一个背景指令的PID(后台运行的最后一个进程的进程ID号)
$? :执行上一个指令的返回值 ,0:正确,其他值:有错误
$- :显示shell使用的当前选项,与set命令功能相同
单、反单、双引号
单引号:使任何字符失去意义:普通字符,特殊字符:\,反引号`,通配符。。。;
如何 引用变量:但通过单引号嵌套可使用变量:如;'he is '${name}''
双引号:可以引用变量,使用转义\,
反单引号:代表引用命令,作用与$(xxx)作用相同,
$(cmd1;cmd2;...) 与 `cmd1;cmd2;...`起到同样作用;都是命令替换。
判断、条件组合、嵌套
基本用法
文件测试 | -e/-s: 文件存在/存在且size不为0为真; -r/-w/-x: 权限判断,前提:文件存 -z (长度为0真); -n(长度非0真) | -d/-f: 存在且为目录/文件为真; -c/-b: 文件存在且为字符/块设备为真 |
比较 | 比较符号:2类: A类: -eq, -ne, -gt, -ge, -lt, -le B类: =, ==, !=, >, >=, <, <= 数字比较: [A类] 或 [[A类]] 或 ((B类)) 字符串比较: [B类] 或 [[B类]] 注意: 1:[[]] 中触发模式匹配,结合=~,模式(如*hello*)不能在引号内 2:[]中<,>主要转义:\<, \> 3: 在判断、比较中 [[]] 更通用 | s1=abc s2=abc* if [ "${s1}" =(或==或!=) “${s2}” ] 或 if [ "${s1}" =(或==或!=) ${s2} ] // []中s2有无引号均为普通字符串 if [[ "${s1}" == ${s2} ]] // true,[[]]中s2不加引号为模式,否则为字串 if [ "${s1}" \> “${s2}” ] 或 if [[ "${s1}" > ${s2} ]] // false |
逻辑组合 | 只能用在 []: -a, -o, ! 只能用在 [[]]: &&, || |
统一规则:字符串统一用引号包括,使用=或==,数字统一不用引号,使用-eq等判断;
条件嵌套(子条件组合)
#!/bin/bash
t=$(date +%s)
touch "${t}.bz2"
n=0
path="."
for f in $path/*;do
if [[ ("$f" == *${t}*) || ("$n" -lt "3" && "$f" == ${t}*) ]] && [[ "${f: -4}" == ".bz2" ]]; then
echo "find $f"
fi
done
# or
for f in $path/*;do
if [[ ("$f" == *${t}*) || ("$n" -lt "3" && "$f" == ${t}*) && ("${f: -4}" == ".bz2") ]]; then
echo "find $f"
fi
done
rm -f "${t}.bz2"
注意:
1: 上面例子中有模式匹配、条件嵌套,条件嵌套必须用小括号()
2: ||和&&可以在[[]]内部,也可以在外部链接[[]]
通配符、转义符、-e
=~ 模糊匹配操作符
判断左侧的参数是否能被右侧pattern匹配,匹配返回真(0),否则返回假(1)
用法:[[ $str =~ pattern ]] 必须在 [[]] 内才触发模式匹配
模糊匹配语法
通配符 | ^ 匹配行首 $ 匹配行尾 . 任意单个字符 (有些命令工具里 ? 表示任意单个 ) * 匹配0或多个前面单个字符 [] 匹配字符集内任意一个字符,如:[xyz]、[0-9] [^] 匹配字符集内任意一个字符,如:[^xyz]、[^0-9] a\{1\} 匹配前面的 (a) 字符出现 (1) 次。若用 egrep 可以去掉斜线。 a\{1,\} 匹配前面的 (a) 字符至少出现 (1) 次。若用 egrep 可以去掉斜线。 a\{,2\} 匹配前面的 (a) 字符最多出现 (2) 次。若用 egrep 可以去掉斜线。 a\{1,2\} 匹配前面的 (a) 字符出现 (1~2) 次。若用 egrep 可以去掉斜线。 [:alnum:] 匹配任意一个字母或数字,类似:[:alpha:]、[:digit:] 、[:lower:]、[:upper:] \ 转义,屏蔽元字符的特殊意义 a{abc,xyz,123}b 匹配换括号里任意一个子串 |
-e 开启转义
shell转义同c语言,不过需要显示开启:如:echo -e "\n" 打印换行
2:变量与函数
变量与作用域
作用域:shell所有变量(包含函数里的)默认都是全局;仅在函数里可以使用local定义局部变量
引用:正确方式${var},这种方式可以使用更多场景:如: b="${a}_pch"
注意:变量值 含空格 必须用引号(单或双引号)
定义局部变量(一般用在函数里):local val=9
declare声明类型变量
- declare [+/-] [选项] 变量名
+ : 取消变量类型的属性
- :给变量设定类型属性
- 选项类型:
a : 将变量声明为数组型
i : 将变量声明为整数型
x : 将变量声明为环境变量
r : 将变量声明为只读变量
p : 显示指定变量的被声明的类型
变量判空与处理
${var:-string} var不空用var,否则用string,string可以是:常量、变量、cmd输出;
${var:=string} var不空用var,否则用string,同时将var赋值string;
${var:+string} var为空用var,否则用string,不改变var值;
${var:?string} var不空用var,否则将string打印到标准错误输出,终止程序;
函数-参数-return-exit-返回值
函数定义、参数:
[function ]Hello() //function可不加
{
echo "param num=$#, $2"
local num=0 //函数里变量默认也是全局的,这里用local定义局部变量
}
函数调用不加(),后面跟参数:Hello zhangsan //参数可以不用加引号
1:函数体 不可空;且 只返回数字,要返回子串可用变量接收,如右:
2:$0仍表示脚本名,$n(n>0) 表示函数的实际参数, $# 表示参数个数(不含函数名)
函数返回值及使用(均当作字符串):
使用echo 返回:
function test() {
name=$1
echo "123213"
}
ret=$(test "aaaa")
return、exit及返回值捕获
1: return 仅在函数里使用、仅返回0-255的数字(超255从0开始)、return后语句不执行
或者: source b.sh 用source 方式执行代码,b.sh 脚本中可以在非函数中使用 return 数值
2: exit 退出整个脚本
3: 函数、shell命令、shell脚本返回值均可用 $? 捕获
3:命令组、管道
一行多条命令
分隔符 | 格 式 | 作 用 |
; | cmd1 ; cmd2 | 顺序执行:cmd1,cmd2...;无论cmd1成功失败,cmd2都执行 |
&& | cmd1 && cmd2 | cmd1成功($?=0),cmd2才执行;cmd1失败($?≠0)cmd2不执行 |
|| | cmd1 || cmd2 | cmd1失败($?≠0),cmd2才执行;cmd1成功($?=0)cmd2不执行 |
命令组、命令组返回值
() | 1:命令组:(cmd1;cmd2),开新shell;()内变量外部不可见; 2:值替换:$(cmd),将输出放到原位置 |
(()) | 1:整数扩展:+、-、*、/、%,以及数字逻辑判断,不支持浮点;sum=$(($sum+4)) 不能有空格; 2:C语言扩展:$((exp)),exp必须符合C语言规范,甚至三目运算。 |
{} | 1:命令组:{ cmd1;cmd2;},不开新shell,而是创建函数,内部变量外部可见;格式:cmd1与左{有空格,最cmd后有分号; 2:名称扩展:ls {ex{1..3},ex4}.sh,ls {ex[1-3],ex4}.sh |
管道|
如下命令:echo $xxx | hi 中hi 相当于一个进程在管理内执行,exit只退出当前hi函数
#!/bin/sh
hi()
{
read filename
echo $filename
exit 1 //仅退出函数,和return 1一样
}
echo $file | hi //hi函数在管理执行的,相当于一个进程
4:数字与字符串
数字运算
let运算符:
let i++; let ++i; let i+=j; let i*=9
let x=7/3 //取整
let x=7%3 // 取余单、双引号均可
类型转换: shell自动转换,但类型不对报错
字符串->数字:age="100";let iage=${age}
字符串操作
变量:str="123abc123"
取长度:echo "${#str}"
左截取:${string:position:length} :从字符串 string 的 position 位置截取 length 个字符串,length 可选
echo ${str:3:3} // 输出:abc
echo "${str:0:${#str}-3}" // 输出:123abc
右截取:${string:空格 -lenth} 截取字符串 string 的后 lenth 个位置
echo ${str: -4} // 输出:c123
echo ${str:0-4} // 输出:c123,空格可以看成 0
echo ${str:1-4} // 输出:123,1-4就相当于 空格-3 或者说 0-3
从左开始匹配删除:# 和 ##,${string#pattern},${string##pattern}, #(尽可能少删字符) 和 ##(贪婪模式:尽可能多删字符)
其中 pattern 可以是一个正则表达式
echo "${str##*1}" // 输出:23
echo "${str#*1}" // 输出:23abc123
echo "${str##1}" // 输出:23abc123
echo "${str#1}" // 输出:23abc123
从右开始匹配删除 % 和 %%
echo ${str%%2*3} // 输出:1
echo ${str%2*3} // 输出:123abc1
普通替换: ${string/pattern /new},${string//pattern /new} 其中: /表示只替换一个,// 表示全部替换
echo "${str/123/r}" // 输出:rabc123
echo "${str//123/r}" // 输出:rabcr
echo "${str/3*1/r}" // 输出:12r23, 3*1为正则表达式,中间*表示任意多字符
含有转义字符替(贪婪模式):
s=abc/123/abc
echo ${s/*\//} // 输出:abc
前后缀替换: ${string/#pattern/new}, ${string/%pattern/new} 其中:#表示匹配开头子串,%匹配结尾子串,他们只替换一个匹配的项
echo "${str/#123/r}" // 输出:rabc123
echo "${str/%123/r}" // 输出:123abcr
echo "${str/%c*123/r}" // 输出:123abr 如果替换中有*,则进行贪婪匹配,并替换第一个最大匹配项。
5:数组或列表
数组定义
list=("value1" "value2" "value3")
vec="ni hao xiao ming" // 字符串数组,${vec},返回里面单词列表
数组操作
${list[n]} //n从0开始,第一个也可是:${list},越界返回空
echo ${!list[*]} //打印所有下标
echo ${list[*]} //打印所有元素值:连接成字符串
${list[@]} //所有元素,返回列表(数组)
list=("${list[@]}" "value4") //增加一个元素
${#list[@]}或${#list[*]} //长度
${#list[0]} //元素长度
unset list[0] // 删除0号元素
unset list // 删除数组
遍历数组
for i in ${list[*]}
do
xxx
done
6:字典
定义字典
declare -A dic
dic=([key1]="value1" [...Kv对空格分开]") //key也可以带引号
dic=( // 可以分行
["k1"]="v1"\
["k2"]="v2"\
)
操作
echo ${dic[key]} //返回k-v对,key可带引号
echo ${!dic[*]} //打印所有key
echo ${dic[*]} //打印所有value
dic["k3"]="v3" // 添加元素
遍历key值
for key in $(echo ${!dic[*]})
do
echo "$key : ${dic[$key]}"
done
7:流程控制
循环与分支均可嵌套,循环中用break, continue,同C语言
for循环与for...in
分号问题: do在for同一行必须加,否则可选
命令行循环:for((a=1;a<=100000;a++));do lua stat.lua;done //双()里空格可选
其他形式:
for i in f1 f2 f3 // 字符串枚举
for file in /proc/* // 目录文件枚举
for file in $(ls *.sh) // 文件列表枚举
for i in `ls` // 遍历文件夹
for i in {1..10} // 数字序列
for i in $(seq 1 10) // 数字序列
for i in `seq 0 4`
while
分号问题: do在while同一行必须加,否则可选
条件两边无空格,>0:永远循环;或 i<j形式;
命令行: while ((条件));do xxx done //while与(之间必须有空格,双()里空格可选
其他形式:
while [[ $num -lt 2 ]];do xxx done //双[]与里面字符及内部操作必须加空格
while read n;do xxx done
until
特点: 条件假时执行
i=0
until [[ "$i" -gt 5 ]] //大于5
do
let "square=i*i"
let "i++"
done
if、elfi、else、fi
分号问题:条件后必须有分号
if (($i<5));then // if 后 有空格,不过最好用 -lt; (())内可没有空格
xxxx
fi
多个if-elfif-else
if [ $1x == "ab"x ]; then
xxx
elif [ $1x == "cd"x ]; then
xxx
else
xxx
fi
if-else也可以嵌套
case...in
多匹配,双分号
read n
case $n in
1)
echo "get num 1" ;; //换行自定义对齐,每个匹配后都需有双分号
4|5)
echo 'get num 4 or 5' ;; // 可以不换行
*)
echo "any num else 1";;
esac
select...in
作用:显示可选择编号的菜单,用户选择序号,赋值给变量,并执行响应的逻辑,常和case联合使用,示例如下:
select color in "red" "blue" "green"
do
if [[ "" == "$color" ]];then
break // break可以退出select...in交互,否则一直循环
fi
case "$color" in
"red")
xxx
break
;;
"blue")
echo "$color"
break
;;
"green")
echo "$color"
break
;;
"*")
echo "$color"
break
;;
esac
done
运行后显示:
若用户选择不在菜单内,color赋空值。
8: 常见工具命令
1: chown (一般只能root管理执行)
chown x:x /tmp 只改变 /tmp 这一层文件夹归属和群组,下面的字目录和文件,不变,
chmod -R x;x /tmp 递归改变 /tmp 及下面 文件夹和文件的归属和群组
2: chmod (一般只能root管理执行)
chmod 777 /tmp 只改变 /tmp 这一层文件夹权限属性,下面的字目录和文件,不变,
chmod -R 777 /tmp 递归改变 /tmp 及下面 文件夹和文件的权限属性