Shell脚本常用语法与命令
记录我工作中写脚本经常用到的命令和用法
1. 循环数组
arrs=("1" "2")
for arr in ${arrs[@]};do
echo $arr
done
- 通过下标取数组某个值 ${arrs[i]}
- ${arrs[@]} 表示整个数组序列
- 其中arrs[0] 也可以表示为arrs
- 数组中元素也可以重新赋值, 比如执行完
arrs=3
之后数组就变成了(“3” “2”), 再执行arrs[1]=4
, 数组就变成了(“3” “4”)
2. if else语句
name=nick
if [[ $name == "nick" ]];then
echo "It's me."
elif [[ $name == "chun" ]];then
echo "My wife."
else
echo "Others"
fi
3. 删除变量
之前遇到个情况, 我先定义了个变量arrs=("1" "2" "3")
, 然后又执行arrs="a b c"
。我以为字符串"a b c"会覆盖原来的数组, 但是事实并没有。只是把数组第一个元素值替换成了"a b c"。所以在使用之前用过的变量名之前,需要先删除变量, 执行unset arrs
4. 字符串操作
4.1 字符串截取
假设有变量var=https://blog.csdn.net/scl323
- 4.2 通过
#
号截取
保留左数第一个分隔符右边的字符串
echo ${var#*:}
结果是//blog.csdn.net/scl323
- 这里分隔符是
:
,var
是变量名,#
是操作符,*:
表示保留除分隔符右边的所有字符- 分隔符可以是多个字符,
echo ${var#*net}
的结果是/scl323
- 4.3 通过
##
号截取
保留右数第一个分隔符右边的字符串
echo ${var##*/}
结果是scl323
- 4.4 通过
%
号截取
保留右数第一个分隔符左边的字符串
echo ${var%/*}
结果是https://blog.csdn.net
- 4.5 通过
%%
号截取
保留左数第一个分隔符左边的字符串
echo ${var%%/*}
结果是https:
%
和#
截然相反, 一个#
是左数第一个分隔符,两个是右数第一个; 但是一个%
是右数第一个分隔符, 两个是左数第一个。
- 4.6 通过字符串下标截取
${变量名:开始字符位置:字符个数}
var=https://blog.csdn.net/scl323 # 从左边第几个字符开始, 4表示字符总个数 $ echo ${var:8:4} blog # 从左边第几个字符开始, 直到结束 $ echo ${var:8} blog.csdn.net/scl323 # 从右边第几个字符开始,3表示字符总个数 $ echo ${var:0-6:3} scl # 从右边第几个字符开始, 直到结束 $ echo ${var:0-6} scl323
4.2 字符串比较
操作符 | 含义 | 例子(值都为true ) |
---|---|---|
== | 相等 | “a” == “a” |
!= | 不等于 | “a” != “b” |
=~ | 包含 | “nicksong” =~ “nick” |
> | 大于 | “2” > “1234” |
< | 小于 | “1234” < “2” |
注意:
- 操作符左右都必须要有一个空格
- 字符串排序是正常的Unicode字符排序规则
5. 远程执行命令
在搭集群的时候经常需要远程执行命令
5.1 前提条件
配置ssh免密登陆
5.2 命令格式
ssh [options] [user@]host [command]
- 注意: 如果command里面如果有
;
则需要用双引号括起来
6. 循环遍历目录文件
对目录里面的文件进行循环遍历
#! /bin/bash
for file in `ls`; do
echo "文件名:"$file
done
7. 对文件逐行打印
对文件内容进行逐行打印
#! /bin/bash
cat /home/hdfs/data/t_s006_qh_bs_customer/cust_info.csv | while read line
do
echo $line
sleep 3
done
8. 修改文件格式(CR、CRLF)
在 vim 中用set ff?
命令可以查看当前文件是什么格式的。
set ff=unix
:修改文件格式为unix
,则换行为CR
。set ff=dos
:修改文件格式为dos
,则换行为CRLF
。
9. 用 AWK 命令计算文件某一列的总和
awk -F' ' -v sum=0 '{sum += $3} END{print sum}'
10. wait 等待所有子进程结束
#! /bin/bash
# 这样能做到让 test1 和 test2 并行运行
bash test1.sh &
bash test2.sh &
wait
echo 'all is done'
11. shell 控制并发数执行
使用 FIFO 命名管道对并发数进行控制,原理是将管道当做一个线程池。
#!/bin/bash
# 需要并发执行的主代码放到这个 function 里面
function fun {
echo $1
# 模拟代码执行 5 秒
sleep 5
}
################
# 创建命名管道 #
################
# 随便定义个文件名 $$ 表示当前进程号
fifo_file="/tmp/$$.fifo"
# 用 mkfifo 命令创建管道文件
mkfifo $fifo_file
# 并将文件描述符 6 和管道文件绑定。
exec 6<>$fifo_file
# 绑定完成就可以删除该管道文件了。
rm -f $fifo_file
#################
# 初始化池子大小 #
#################
# 线程数
thread_num=10
for i in $(seq 1 $thread_num); do
# 往这个文件描述符里写 10 个 1
# 从这里可以看到,前十个进程其实可以跟后十个进程通信
echo 1 >&6
done
###########
# 执行主体 #
###########
# 待执行的数组
arr=$(seq 1 100)
# 遍历数组
for a in ${arr[@]}; do
# 读取文件描述符里面一行,该操作类似于队列的 pop,读完就删除该行。
# 如果文件描述符里面没有内容了,则会阻塞在这。所以读完 10 个之后
# 就会在这里阻塞,直到前面的进程执行完并往描述符里增加一行。
read -u6
{
# 执行主方法
fun $a
# 执行完之后往描述符中增加一行,相当于把自己拿到的 solt 再放回去。
# 这一步想放啥进去就放啥进去,可以把当前的执行结果放进去,这样后面的
# 线程就知道执行结果了。
echo 1 >&6
} &
# 注意,执行主方法和释放锁应该放一起并放到后台执行。
done
# 等待所有进程执行完成
wait
# 关闭该文件描述符
exec 6>&-
持续更新, 欢迎关注