shell基础部分学习
环境与插件
vscode编辑器 + 插件(shellman, bash debug ,shell format,shell check ,shell script IDE,shell script snippets)
参考书籍:
《Linux命令行与shell脚本编程大全(第3版)》
《Linux Shell核心编程指南》
vscode配置bash shell 调试 launch.json
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
// javascirpt调试
// 这个不用管
{
"name": "Launch Program",
"program": "${fileDirname}/${fileBasename}",
"request": "launch",
"skipFiles": [
"<node_internals>/**"
],
"type": "node"
},
// shell调试
{
"type": "bashdb",
"request": "launch",
"name": "Bash-Debug (simplest configuration)",
"program": "${fileDirname}/${fileBasename}"
}
]
}
1、变量、数组、分支、循环、函数
(1)、变量
定义: variable = value , 如REDIS_BIN=/usr/local/bin
取值:$variable, 如echo “$REDIS_BIN”
(2)、数组
定义、修改元素,取元素,取数组长度,取单个元素长度、
取数组所有值、数组拼接、删除数组、删除元素、遍历元素
定义关联数组、遍历关联数组值、遍历关联数组key等
#!/bin/bash
#服务列表名称,数组定义 array_name=(value1,value2,...,valueN)
serverList=(
"redis-server"
"mysql"
"nginx"
"xxl-job-admin"
"fdfs_trackerd"
"fdfs_storaged"
"mongod"
)
# 取数组下标为0的值 ${array_name[index]}
echo "${serverList[0]}"
# 修改特定元素的值 array_name[index]=value
serverList[0]="test"
# 取数组下标为0的值 ${array_name[index]}
echo "${serverList[0]}"
# 取数组长度 $#表示参数个数 假设echo "${serverList[#]}",不行!!!
echo "${#serverList[@]}";
echo "${#serverList[*]}"
# 获取单个元素的长度
echo "${#serverList[1]}"
# 取数组所有值,并以空格隔开,效果:redis-server mysql nginx xxl-job-admin fdfs_trackerd fdfs_storaged mongod
echo "${serverList[@]}"
echo "${serverList[*]}"
# 数组拼接,就是形成(value1 value2 ... valueN)
# 那么按照上边的说法array_new=(${array1[@]} ${array2[@]})
# 玩积木---层层叠加
# 删除数组元素 unset array_name[index] unset array_name
unset "serverList[0]"
if [[ "${serverList[0]}" == "" ]]; then
echo "删除成功"
fi
echo "${serverList[0]}"
unset serverList
if [[ "${serverList[*]}" == "" ]]; then
echo "删除成功"
fi
# 循环遍历数组
for serverItem in "${serverList[@]}";
do
echo "$serverItem"
done
# 形如map的关联数组
declare -A array_name
array_name["lch"]=999
array_name["lx"]=0
# 遍历关联数组值
for arrayItem in "${array_name[@]}";
do
echo "$arrayItem"
done
# 遍历关联数组键,在${array_name[@]}基础之上加个 ! 形成 ${!array_name[@]}
for arrayKey in "${!array_name[@]}";
do
echo "$arrayKey"
done
(3)、分支
if分支定义:
if condition; then
# if body
commands
elif condition; then
# else if body
commands
else
# else body
commands
fi
condition: 条件
commands:0个或多个命令
根据条件执行命令。
如果condition为零,则执行`then commands'列表。
否则按顺序执行每个 `elif condition'列表,并且如果它的退出状态为零,
则执行对应的 `then commands' 列表并且 if 命令终止。
否则如果存在的情况下,执行 `else commands'列表。
整个结构的退出状态是最后一个执行的命令的状态,或者如果没有条件测试为真的话,为零。
退出状态:
返回最后一个执行的命令的状态。
注:
1、一般的命令其成功返回值为0,这里的if分支,其实是condition为0,则执行then语句。
2、condition可以是命令,也可以是数值判断
3、condition为零,具体含义为执行命令/判断之后,$?的值为0
比如:
(( 2 == 3))
echo "$?" # 2 3不相等,$?值为 1
(( 2 == 2))
echo "$?" # 2 3相等,$?值为 0
# 那么,下边就会打印相等
if (( 2 == 2));
then
echo "相等"
fi
case 分支:
case 词 in [模式 [| 模式]...) 命令 ;;]... esac
基于模式匹配来执行命令。
基于 PATTERN 模式匹配的词 WORD,有选择的执行 COMMANDS 命令。
`|' 用于分隔多个模式。
退出状态:
返回最后一个执行的命令的状态。
case "${item}" in
1)
echo "item = 1"
;;
2|3)
echo "item = 2 or item = 3"
;;
*)
echo "default (none of above)"
;;
esac
(4)、循环
for循环
for 名称 [in 词语 ... ] ; do 命令; done
为列表中的每个成员执行命令。
“for”循环为列表中的每个成员执行一系列的命令。如果没有
“in <词语> ...;”则假定使用“in "$@"”。对于 <词语> 中的每
个元素,<名称> 变量被设定为该元素后执行 <命令>。
退出状态:
返回最后执行的命令的状态。
比如:
#!/bin/bash
serverList=("redis-server" "mysql" "nginx" "xxl-job-admin" "fdfs_trackerd" "fdfs_storaged" "mongod")
#服务启动核心逻辑
for serverItem in "${serverList[@]}";
do
echo "$serverItem"
done
while循环:
while condition; do commands done
只要测试成功即执行命令。
只要在 condition 为0,则展开并执行 commands 命令。
退出状态:
返回最后一个执行的命令的状态。
比如:
# 打印0-9
index=0
while (( index < 10 )); do
# body
echo "$index"
(( index++ ))
done
注:
1、这里的condition跟if分支condition一样
(5)、函数
抽离重复代码,将代码封装到函数以便其他模块调用。
1、创建函数
function name{
commands
}
2、使用函数
function name 参数
如:
# $@ $* : 所有参数
# $0 1 2 ... : 参数0 1 ... $0为执行程序 $1 .. 为参数
# $? 程序返回值
# $# 参数个数
# $variable 取变量值
function printAge{
echo "age: $1"
return 0
}
printAge 20
3、函数返回跟默认退出状态码
函数中变量:
局部变量与全局变量冲突,默认为局部变量。
函数返回值(最好为0)
return value
取函数返回值
$?
若没有return,默认为最后一条命令的返回值
若函数为空,默认返回 0
正确使用函数输出
#!/bin/bash
# using the echo to return a value
function dbl {
read -p "Enter a value: " value
echo $[ $value * 2 ]
}
result=$(dbl)
echo "The new value is $result"
通过这种技术,你还可以返回浮点值和字符串值。这使它成为一种获取函数返回值的强
大方法。
2、命令替换与匹配
#!/bin/bash
# left right
# 最小匹配 # %
# 最大匹配 ## %%
# 例子:假设 path=/etc/sysconfig/network
# ${path#*/} //去掉path变量左边 第一次出现 */ 子串 *代表匹配任意0或0个以上字符
# ${path#tc} //像这样是匹配不成功的,第一个字符‘t’ 与 左边第一个字符‘/’没匹配上,直接就结束了
# ${path##*/} //最大匹配
# ${path%/*} //去掉path变量右边匹配的子串
# ${字符串/变量:开始位置:长度} ${path:0:5}
# 字符替换 ${字符串/变量//要查找的值/替换值} ${path//lc/ym}
# $((算术表达式))
# 命令替换:可在shell语句里边执行命令 ``或$()
CURRENT_SCRIPT_DIR=$(cd "$(dirname "$0")" || exit; pwd;)
echo "$CURRENT_SCRIPT_DIR"
paramNumber=$#
if (( paramNumber != 1 )); then
echo "没有输入主目录"
return 1;
fi
HOME=$1
# HOME=/home/lch/learn/shell
COMMON_CONFIG_PATH=$HOME"/config/config.ini"
LOG_CONFIG_PATH=$HOME"/config/log.conf"
echo "$COMMON_CONFIG_PATH"
echo "$LOG_CONFIG_PATH"
# sed 's/\/sbin\/nologin/\/bin\/sh/' /tmp/passwd 使用转义替换
# sed 's#/sbin/nologin#/bin/sh#' /tmp/passwd 井号作为替换符
# sed 's,/sbin/nologin,/bin/sh,' /tmp/passwd 逗号作为替换符
sed -i "s#\${[a-zA-Z_]\+}#$HOME#" "$COMMON_CONFIG_PATH"
sed -i "s#\${[a-zA-Z_]\+}#$HOME#" "$LOG_CONFIG_PATH"
3、$取值
#!/bin/bash
export COMMAND_HOME=/home/lch/learn/shell
bash ./config.sh $COMMAND_HOME
result=$?
if (( result != 0 )); then
return 1;
fi
# export COMMAND_HOME=/home/lch/os_code/command/build/command
# $@ $* : 所有参数
# $0 1 2 ... : 参数0 1 ... $0为执行程序 $1 .. 为参数
# $? 程序返回值
# $# 参数个数
# $variable 取变量值
paramNumber=$#
#./client -e "ls -l"
if (( paramNumber != 2 )); then
echo "参数数量错误"
return 1;
fi
echo "./client $1 \"$2\""
3、服务启动停止
#!/bin/bash
#开启管理员
#sudo -i
#开启ssh连接
#service ssh start
if [ -f /etc/profile ]
then
source /etc/profile
result=$?
# -lt -le -eq -ge -gt -ne
if [[ $result != 0 ]]
then
exit 1
fi
else
exit 1
fi
#启动参数,可修改
#redis
REDIS_BIN=/usr/local/bin
REDIS_CONF=/usr/local/redis/redis.conf
#nginx
NGINX_BIN=/opt/apps/nginx/sbin
NGINX_CONF=/opt/apps/nginx/conf/nginx.conf
#mysql
MYSQL_START_HOME=/usr/local/mysql/support-files
#xxl-job-admin
XXLJOBBIN=/usr/local
#code_id
CODEIDBIN=/usr/local
#code_websocket
CODEWEBSOCKETTESTBIN=/usr/local
#fdfs_tracker
TRACKER_CONF=/etc/fdfs/tracker.conf
#fdfs_storage
STORAGE_CONF=/etc/fdfs/storage.conf
#mongod
MONGODB_BIN=/usr/local/mongodb/bin
MONGODB_CONF=/usr/local/mongodb/mongodb.conf
#函数定义
function startServer() {
serverName=$1
result=0
echo "$serverName"
case "${serverName}" in
redis-server)
startRedis
;;
mysql)
startMysql
;;
xxl-job-admin)
startXxlJobAdmin
;;
nginx)
startNginx
;;
fdfs_trackerd)
startFdfsTrackerd
;;
fdfs_storaged)
startFdfsStoraged
;;
mongod)
startMongod
;;
code_id)
startCodeId
;;
code_websocket_test)
startCodeWebsocket
;;
*)
echo "default (none of above)"
return 1
;;
esac
}
#redis 启动逻辑
function startRedis() {
cd $REDIS_BIN || exit
./redis-server $REDIS_CONF
}
#mysql 启动逻辑
function startMysql() {
cd $MYSQL_START_HOME || exit
./mysql.server start
}
#xxl-job-admin 启动逻辑
function startXxlJobAdmin() {
cd $XXLJOBBIN || exit
./xxlJob.sh
}
#nginx 启动逻辑
function startNginx() {
cd $NGINX_BIN || exit
./nginx -c $NGINX_CONF
}
#fdfs_trackerd 启动逻辑
function startFdfsTrackerd() {
fdfs_trackerd $TRACKER_CONF
sleep 2
}
#fdfs_storaged 启动逻辑
function startFdfsStoraged() {
# netstat -tulnp
fdfs_storaged $STORAGE_CONF
sleep 2
}
#mongod 启动逻辑
function startMongod() {
cd $MONGODB_BIN || exit
nohup mongod --fork --config $MONGODB_CONF > /dev/null 2>&1
}
#CodeId 启动逻辑 code_id
function startCodeId() {
cd $CODEIDBIN || exit
./codeId.sh
}
#CodeWebsocket 启动逻辑
function startCodeWebsocket() {
#code_websocket_test
cd $CODEWEBSOCKETTESTBIN || exit
./code_websocket_test.sh
}
#服务列表名称
serverList=(
"redis-server"
"mysql"
"nginx"
"xxl-job-admin"
"fdfs_trackerd"
"fdfs_storaged"
"mongod"
)
#服务启动核心逻辑
for serverItem in "${serverList[@]}";
do
sleep 1
#$(ps -ef | grep "mysql" | grep -c -v grep )
runFlag=$(pgrep "${serverItem}" | grep -c -v grep )
if [ "$runFlag" -lt 1 ];
then
echo "$serverItem 服务未启动, 正在启动该服务......"
else
echo "$serverItem 服务已启动, 正在停止该服务......"
#$( ps -ef | grep "redis-server" | grep -v grep |awk '{print $2}')
oldServerIds=$( pgrep "${serverItem}" )
for oldServerPid in $oldServerIds;
do
if [ -n "$oldServerPid" ];
then
echo "kill $serverItem server id: $oldServerPid"
kill -9 "$oldServerPid"
fi
done
echo "$serverItem 服务已停止, 正在启动该服务......"
fi
sleep 1
startServer "$serverItem"
#判断服务是否启动成功
runFlag=$(pgrep "${serverItem}" | grep -c -v grep )
if [ "$runFlag" -lt 1 ];
then
echo "$serverItem 服务启动失败"
else
echo "$serverItem 服务启动成功"
fi
done
exit 0