shell 脚本实战笔记(10)--spark集群脚本片段念念碎
前言:
通过对spark集群脚本的研读, 对一些重要的shell脚本技巧, 做下笔记.
*). 取当前脚本的目录
1 2 | sbin=`dirname "$0" ` sbin=`cd "$sbin" ; pwd` |
代码评注:
# 以上代码为获取执行脚本所在的目录的常用技巧
# sbin=$(dirname $0) 返回可能是相对路径, 比如./
# sbin=$(cd $sbin; pwd) 采用pwd, 来返回脚本所在目录的绝对路径
*). 循环遍历脚本参数
1 2 3 4 5 6 7 8 | while (( "$#" )); do case $1 in -- with -tachyon) TACHYON_STR= "--with-tachyon" ;; esac shift done |
代码评注:
# 这是段遍历脚本参数的常见代码片段
# shell脚本中$#表示参数个数
# 由于$0是脚本名称本身占据, 因此脚本对参数的遍历从$1开始, 借助shift变量左移, 方便了对变长参数列表的遍历
# 基于事件的xml解析方式, 当采用pull方式去遍历的时候, 差不多也是类似的代码结构
# 当然需要注意, shift处理参数变量之后, 对后续脚本代码处理变量是有影响的(负作用), 因此最佳实践是集中处理脚本参数
*). 引入配置脚本
1 | . "$sbin/spark-config.sh" |
代码评注:
# shell脚本中'.' 等同于source, 把调用脚本作为调用方脚本的自身的一部分执行, source <shell_file>通常用于导入应用的配置参数
# source/exec/fork 外部脚本的区别, 详见这篇
*). 默认参数处理
1 2 3 | if [ "$SPARK_MASTER_PORT" = "" ]; then SPARK_MASTER_PORT=7077 fi |
代码评注:
# 对变量默认值的处理方式
# 注意对变量添加"", if [ $SPARK_MASTER_PORT = "" ] 会报错误: "[: =: unary operator expected"
# 类似的代码可采用-z $SPARK_MASTER_PORT的方式
1 2 3 | if [ -z $SPARK_MASTER_PORT ]; then SPARK_MASTER_PORT=7077 fi |
*) 对变量的高级处理
1 | this= "${BASH_SOURCE-$0}" |
代码评注:
# ${BASH_SOURCE-$0}, 属于特殊用法, 用于获取脚本名称, 那为何不用$(basename $0), 如脚本注释里谈到了, 遇到软接连, $(basename $0)就不行了
# 其次${BASH_SOURCE-$0}, 它属于${VAR_NAME:-DEFAULT_VALUE}这种变量语法的简写
# 比如, ${name:-"lilei"}, 如果name没有定义, 则默认返回为"lilei", 若name定义了, 则返回${name}值
*) 对软链接的处理
1 2 3 | # resolve links - $0 may be a softlink common_bin=$(cd -P -- "$(dirname -- " $this ")" && pwd -P) script= "$(basename -- " $this ")" |
代码评注:
# 无论是"cd -P", 还是"pwd -P", -P参数表明若遇到soft link, 则取soft link对应的真实目录/文件
*) set -a/a的使用
1 2 3 | set -a . "${use_conf_dir}/spark-env.sh" set +a |
代码评注:
# set -a, 把执行的变量自动export, set +a, 则把关闭该功能
# 可以简单理解为, 把脚本的本地变量, 都默认添加了export修饰
*). $!的使用和pid文件的使用
1 2 3 4 | nohup nice -n $SPARK_NICENESS "$SPARK_PREFIX" /bin/spark-class $command "$@" \ >> "$log" 2>&1 < /dev/null & newpid=$! echo $newpid > $pid |
代码评注:
# nohup 表示进程脱离session运行
# nice -n 用于调整进程nice值
# 2>&1 表示把标准错误(stderr, 2)关联到标准输出(stdout, 1), 可以简写为 &>
# $!表示上一个shell命令(后台运行)的pid
# echo $newpid > $pid (代表文件), 是把进程pid写入到进程的pid文件中去
# 很多服务(比如apache)会选择把自身的pid(进程id)写入到pid文件中去, 至于为何这么做? 各有各的应用场景, 下面的kill -0就应用到了
*). kill -0 的使用, 检测进程是否存在, 重入(误判)问题
1 2 3 4 5 6 | if [ -f $pid ]; then if kill -0 `cat $pid ` > /dev/null 2>&1; then echo $command running as process `cat $pid `. Stop it first. exit 1 fi fi |
代码评注:
# kill -0 <pid> 只是简单的向进程发送一个signal(不影响进程运行), 用来检测进程是否存在, 存在(echo $? => 0), 不存在(echo $? => 1)
# if [ -f $pid ] 判断pid文件是否存在, cat $pid, 则是获取pid值, 这与上面pid文件相吻合
# kill -0 `cat $pid` > /dev/null 2>&1 后面的'> /dev/null 2>&1'用于去掉不必要信息到控制台
疑问:
# 重入问题: 有点类似tcp的问题, socket占据的四元组(src: ip+port, dest: ip+port), 遗留的tcp包, 对后续重新复用port的socket造成的干扰
# 假设: pid写入到pid文件后, 然后进程退出, 然后有后续的新进程占据了这个pid, 那么脚本根据这个pid判断之前的进程是否存活就没意义了, 由此导致误判
# linux kernel对pid的分配采用了延时再分配的策略, pid被复用而导致重判, 这个需要注意
*). 并发+wait使用
1 2 3 4 5 6 7 8 9 | for slave in `cat "$HOSTLIST" |sed "s/#.*$//;/^$/d" `; do ssh $SPARK_SSH_OPTS $slave $ "${@// /\\ }" \ 2>&1 | sed "s/^/$slave: /" & if [ "$SPARK_SLAVE_SLEEP" != "" ]; then sleep $SPARK_SLAVE_SLEEP fi done wait |
代码评注:
# shell脚本没有多线程的概念, 且默认执行子shell是阻塞的, 因此只能通过后台运行多个子进程来模拟
# ssh $slave "<command> " & 是把ssh命令放在后台运行
# wait, 是指等待所有的后台进程结束, 才继续进行下去
# 这是很好的并发CountDownLatch的编程实践
*). sed命令使用
1 2 | sed "s/#.*$//;/^$/d" sed "s/^/$slave: /" |
代码评注:
# 使用流编辑器sed, 对文本内容进行替换和删除, 赞sed
posted on 2014-07-17 12:40 mumuxinfei 阅读(743) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构