bash shell基本知识
bash shell执行命令前的动作
- 分割管道前后的命令
- 分割命令中的单词
- 替换别名命令
- 将括号扩展为指代字符串
- "~"符号扩展为实际路径
- 替换变量
- 替换"$()"和"``"形式的命令
- 替换运算符"$(())"为其结果
- 替换通配符"*"和"?"为指代本地文件或目录
- 根据"函数","内部命令","外部程序"的顺序搜索路径找到命令
- 运行替换后的整体程序
bash shell脚本程序变量
- 参数位置变量
$0-$9
,${10}-${n}
- 所有参数位置视为一个字符串
$*
- 所有参数位置视为一个串行多个字符串
$@
- 参数个数
$#
特殊的内置变量
- 上一条命令执行结束后的返回值
$?
- 目前bash shell的进程号
$$
- 上一个后台程序的进程号
$!
测试变量存在性及空值
${变量名称:-默认值}变量为空时回传默认值
echo ${S1:-is empty!!!}
echo ${S1}
echo 111
输出
[root@jin test]# bash test.sh
is empty!!!
111
${变量名称:+默认值}变量为空时设置成默认值
#!/bin/bash
echo ${S1:=is empty!!!}
echo ${S1}
echo 111
输出
[root@jin test]# bash test.sh
is empty!!!
is empty!!!
111
${变量名称:?提示信息}变量为空时停止显示程序显示提示信息
echo ${S1:?is empty!!!}
echo ${S1}
echo 111
输出
[root@jin test]# bash test.sh
test.sh: line 2: S1: is empty!!!
字符串切片,长度
${变量:位置起点},由指定的位置开始截取字符串到结尾
${变量:位置起点:长度},由指定的位置开始截取指定长度的子字符串
${#变量},传回子变量的长度
例子
逐个输出字符串
#!/bin/bash
STRING="Three words,eight letters."
echo ${STRING:0};
echo ${STRING:0:11};
echo ${#STRING};
# 逐字输出
for ((i=0;i<${#STRING};i++))
do
echo ${STRING:${i}:1};
done
对比样式
${变量#样式},从左开始对比变量,删除最短相符字串
${变量##样式},从左开始对比变量,删除最长相符字串
${变量%样式},从右开始对比变量,删除最短相符字串
${变量%%样式},从右开始对比变量,删除最长相符字串
${变量/样式/替换字串},如果变量中右匹配样式(取最长),则使用替代字串替换第一个匹配
${变量//样式/替换字串},如果变量中右匹配样式(取最长),则使用替代字串替换所有匹配
例子
#!/bin/bash
STRING="Three words,eight letters."
echo ${STRING#T*e};
echo ${STRING##T*e};
echo ${STRING%e*.};
echo ${STRING%%e*.};
echo ${STRING/s/ss};
echo ${STRING//s/ss};
输出
[root@jin test]# bash test.sh
e words,eight letters.
rs.
Three words,eight lett
Thr
Three wordss,eight letters.
Three wordss,eight letterss.
取变量名列表,数组索引列表
${!开头字串@}或者${!开头字串
},取当前匹配的变量名列表,各变量之前用$IFS第一个分割字符(默认为空格)隔开 ${!数组变量名[@]}或${!数组变量名[]},取出数组索引列表,各索引之间用$IFS第一个分隔符(默认为空格)隔开
算术运算
- 算数扩展 $((算数式))
- 外部命令 expr 算数式
- 内置命令 declare -i 变量=算数式
- 内置命令 let 算数式
- $[]方式 $[算数式]
- 浮点运算用
bc
命令,最慢
例子
#!/bin/bash
echo $(( 1 + 1));
expr 1 + 1;
declare -i A=1+1;
echo A;
let B=1+1;
echo B;
echo $[ 1 + 1]
echo "scale=22;2.2+6.6" | bc
输出
[root@jin test]# bash test.sh
2
2
A
B
2
8.8
流程控制
#!/bin/bash
STRING=1;
# if基本用法
if [ ${STRING} = "1" ];
then
echo "\$STRING='1'"
elif [ ${STRING} -eq 2 ];
then
echo "\$STRING=1"
fi
# case 基本用法
case $1 in
1)
echo "\$1=1";;
2)
echo "\$1=2";;
*)
echo "\$1i!=1 or 2";;
esac;
# for 基本使用
MAX_NUM=10;
# 使用 {1..n} 的形式时不能使用变量
#for i in {1..10}
# 下面两种方式能使用变量
#for i in `seq 0 ${MAX_NUM}`
for ((i=0;i<=${MAX_NUM};i++))
do
echo ${i};
done;
# while 基本使用
while [ ${MAX_NUM} -gt 0 ]
do
# break使用
break 1;
echo ${MAX_NUM};
MAX_NUM=$[MAX_NUM - 1]
done;
# 逐行读取
while read line
do
# continue使用
continue 1;
sleep 1;
echo ${line};
done < /etc/passwd;
# until 当条件表达式为假才会运行
until [ $MAX_NUM -gt 10 ]
do
echo ${MAX_NUM};
MAX_NUM=$((MAX_NUM+1));
done;
函数的基本使用
#!/bin/bash
function fun1()
{
# 使用local关键字避免和函数外的变量冲突
local STRING=string
echo "func1 \$1=${1}";
# 最大返回255
return 25;
}
fun2()
{
echo fun2;
}
# 传参
fun1 aaa;
# 上个函数的返回值
echo $?;
# 包含其他文件脚本
#source ./test2.sh
. ./test2.sh
fun3;
文件的重定向
# 获取当前进程pid
[root@jin test]# echo $$
8897
# 进入proc下对应pid的目录,里面有进程的相关信息
[root@jin test]# cd /proc/8897/
[root@jin 8897]# ls
attr clear_refs cpuset fd loginuid mounts numa_maps pagemap schedstat stat task
autogroup cmdline cwd fdinfo maps mountstats oom_adj personality sessionid statm wchan
auxv comm environ io mem net oom_score root smaps status
cgroup coredump_filter exe limits mountinfo ns oom_score_adj sched stack syscall
# fd目录下为文件的描述符
[root@jin 8897]# cd fd
# 文件句柄,为正整数
[root@jin fd]# ls -l
总用量 0
lrwx------ 1 root root 64 12月 9 15:19 0 -> /dev/pts/2
lrwx------ 1 root root 64 12月 9 15:19 1 -> /dev/pts/2
lrwx------ 1 root root 64 12月 9 15:19 2 -> /dev/pts/2
lrwx------ 1 root root 64 12月 9 15:19 255 -> /dev/pts/2
文件描述符
一个非负整数,用来索引和追踪文件,可以认为是打开文件的编号
在/proc/PID/fd中标识
# 创建文件
[root@jin fd]# echo aaa > ~/test_file
# 打开文件句柄
[root@jin fd]# exec 6<>~/test_file
# 在fd目录下会生成一个文件,操作这个文件和直接操作那个文件一样
[root@jin fd]# ls
0 1 2 255 6
[root@jin fd]# cat 6
aaa
[root@jin fd]# cat ~/test_file
aaa
[root@jin fd]# echo bbb > 6
[root@jin fd]# cat ~/test_file
bbb
[root@jin fd]# cat 6
bbb
# 把文件删除,还可以把文件直接copy回去还原
[root@jin fd]# \rm ~/test_file
[root@jin fd]# file ~/test_file
/root/test_file: cannot open `/root/test_file' (No such file or directory)
[root@jin fd]# ls -l
总用量 0
lrwx------ 1 root root 64 12月 9 15:32 0 -> /dev/pts/2
lrwx------ 1 root root 64 12月 9 15:32 1 -> /dev/pts/2
lrwx------ 1 root root 64 12月 9 15:32 2 -> /dev/pts/2
lrwx------ 1 root root 64 12月 9 15:32 255 -> /dev/pts/2
lrwx------ 1 root root 64 12月 9 15:32 6 -> /root/test_file (deleted)
[root@jin fd]# cp 6 ~/test_file
# 虽然文件还原了,但是文件其实已经不是之前的文件了
[root@jin fd]# file ~/test_file
/root/test_file: ASCII text
[root@jin fd]# echo ccc > 6
[root@jin fd]# cat 6
ccc
[root@jin fd]# cat ~/test_file
bbb
# 关闭
exec 6<&-
打开文件
exec 6<>~/test_file
关闭文件
fd<&-
或者fd>&-
exec 6<&-
作用
如下情况,逐行读取会跳过一行
#!/bin/bash
FILE_PATH=/etc/passwd
while read line
do
sleep 1;
read ;
echo ${line};
done < ${FILE_PATH}
如下就可避免这种情况
#!/bin/bash
FILE_PATH=/etc/passwd
exec 6<>${FILE_PATH}
while read -u 6 line
do
sleep 1;
read ;
echo ${line};
done < ${FILE_PATH}
exec 6<&-;
限制程序指定进程数运行
- 使用
read -u
文件描述符方式读取字符串内容 - 设置文件描述符中的回车符号个数为预设的进程数
- 通过循环完成进程的创建
例子
#!/bin/bash
sub()
{
echo "------${$}------begin------";
SLEEP_TIME=$[ 1 % 7 ];
sleep ${SLEEP_TIME};
echo "------${$}------end------";
}
# 总执行次数
TOTAL_NUMBER=88;
# 同时最多执行次数
MAX_THREAD=8;
# 临时文件地址
TEMP_FILE="/tmp/.fifo-${$}";
# 创建管道文件,管道文件在内存中,更快
mkfifo ${TEMP_FILE};
# 打开文件句柄
exec 6<>${TEMP_FILE}
# 删除临时文件
rm -f ${TEMP_FILE};
# 填充回车到文件句柄使程序能执行
for((i=0;i<${MAX_THREAD};i++))
do
echo
done >&6;
# 循环把程序执行完
for((i=0;i<${TOTAL_NUMBER};i++))
do
# 从文件句柄中读取,遇到回车才会执行下一步
read -u 6
# 定义程序块,在程序块中执行要执行的操作,把程序块放到后台执行
{
sub ${i};
echo >&6;
}&
# 查看后台运行的程序
jobs -r;
jobs -r | wc -l;
done;
# 等到所有程序执行完
wait;
# 关闭文件句柄
exec 6<&-;
exit 0;