bash shell基本知识

bash shell执行命令前的动作
  1. 分割管道前后的命令
  2. 分割命令中的单词
  3. 替换别名命令
  4. 将括号扩展为指代字符串
  5. "~"符号扩展为实际路径
  6. 替换变量
  7. 替换"$()"和"``"形式的命令
  8. 替换运算符"$(())"为其结果
  9. 替换通配符"*"和"?"为指代本地文件或目录
  10. 根据"函数","内部命令","外部程序"的顺序搜索路径找到命令
  11. 运行替换后的整体程序
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;

标签: shell

添加新评论