第1章:Shell入门

[TOC]

Shell入门

基础知识

  • shell数组

    MentionedList=(“zhangsan” “lisi”)

一、主要用法

1、获取值

1
2


2、修改值

2.1、修改文件名

1、传参并修改app_info.json文件内容

如修改以下app_info.json文件内容

1
2
3
4
5
{
"version": 0,
"time": "unknow time",
"brance": "unknow brance",
}

修改命令如下:

1
#sed -i '' 's/unknow brance/dev_1.0.0_fix/g' app_info.json

如果要使用传入的参数,则命令如下:

1
2
3
4
5
# 使用传入的参数方法1:把单引号改为双引号(因为单引号会阻止转义)
sed -i '' "s/unknow brance/$1/g" app_info.json

# 使用传入的参数方法2:变量${}外的字符串全都用单引号''圈住
sed -i '' 's/unknow brance/'$1'/g' app_info.json

update_app_info.sh脚本的核心命令:

1
2
3
4
5
6
7
8
# update_app_info.sh
# app信息中加入打包的分支名称,便于知晓当前包的来源分支
BranceName=$1
echo "BranceName=$BranceName"
sed -i '' "s/unknow brance/$BranceName/g" app_info.json

# 将含信息的文件拷贝到app中,以便在app可以获取这些信息
cp app_info.json ../wish/asset/data

update_app_info.sh脚本的完整命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
FullBranceName=$1
ShortBranceName=${FullBranceName##*/}
echo "ShortBranceName=$ShortBranceName"
sed -i '' 's/unknow brance/'${ShortBranceName}'/g' app_info.json

#cp app_info.json ../flutter_updateversion_kit/assets/data
app_info_dir_home="../flutter_updateversion_kit/assets"
if [ ! -d "$app_info_dir_home" ];then
mkdir $app_info_dir_home
#echo "assets文件夹创建成功"
#else
#echo "assets文件夹已经存在"
fi


app_info_dir="$app_info_dir_home/data"
if [ ! -d "$app_info_dir" ];then
mkdir $app_info_dir
#echo "assets/data文件夹创建成功"
#else
#echo "assets/data文件夹已经存在"
fi

fileName="app_info.json"
app_info_file_path=$app_info_dir/$fileNam
cp $fileName $app_info_file_path

jenkins中,直接取定义分支名时候的名称作为变量:

1
2
cd $WORKSPACE/bulidScript
sh update_app_info.sh $BRANCH

2、读取Json及其属性值

假设json文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"version": "1.0.0",
"package_create_time": "package unknow time",
"package_from_brance": "package unknow brance",
"package_default_env": "package unknow env",
"brances_record_time": "03.18",
"feature_brances": [
{
"name": "dev_1.0.0_fix",
"des": "线上版问题修复或优化"
}
]
}

前提:

必须安装jq,安装方法如下:

mac上安装brew后,执行brew install jq安装jq

2.1、如果是json文件

则读取 version、package_create_time、feature_brances.name 的值分别如下:

1
2
3
4
5
6
7
8
9
10
#! /bin/bash
JQ_EXEC=`which jq`
FILE_PATH=app_info.json

packageVersion=$(cat $FILE_PATH | ${JQ_EXEC} .version | sed 's/\"//g')
packageCreateTime=$(cat $FILE_PATH | ${JQ_EXEC} .package_create_time | sed 's/\"//g')
echo "packageVersion=$packageVersion,packageCreateTime=$packageCreateTime"

featureBranceName=$(cat $FILE_PATH | ${JQ_EXEC} .feature_brances.name | sed 's/\"//g')
echo "featureBranceName=$featureBranceName"

2.2、如果是json数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
responseResult=$(\
curl $ROBOTURL \
-H 'Content-Type: application/json' \
-d "
{
\"msgtype\": \"text\",
\"text\": {
\"content\": \"${LastNotificationText}\"
\"mentioned_list\":$MentionedList
}
}"
)

responseResultCode=$(**echo** ${responseResult} | jq '.errcode') # mac上安装brew后,执行brew install jq安装jq

Shell脚本8种字符串截取方法总结

一、shell 中的与、表达式

  • 逻辑与的表达:
1
2
3
4
1)、if [ $xxx=a -a $xx=b ]

注:-a表示and的意思
2)、if [ $xxx=a ] && [ $xx=b ]
  • 逻辑或的表达:
1
2
3
4
1)、if [ $xxx=a -o $xx=b ]

注:-o表示or的意思
2)、if [ $xxx=a ] || [ $xx=b ]

一、Shell的特殊变量

变量 含义
$0 当前脚本的文件名
$# 传递给脚本或函数的参数个数。
$n 传递给脚本或函数的第n个参数
$* 传递给脚本或函数的所有参数。
$@ 传递给脚本或函数的所有参数。被双引号(“ “)包含时,与 $* 稍有不同,下面将会讲到。
$? 上个命令的退出状态,或函数的返回值。
$$ 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。

1、 \$* 和 \$@ 的区别

\$* 和 \$@ 都表示传递给函数或脚本的所有参数

情况①:不被双引号(“ “)包含时,都以”\$1” “\$2” … “\$n” 的形式输出所有参数。

情况②:但是当它们被双引号(“ “)包含时,”\$*” 会将所有的参数作为一个整体,以”\$1 \$2 … \$n”的形式输出所有参数;”\$@” 会将各个参数分开,以”\$1” “\$2” … “\$n” 的形式输出所有参数。

2、shell中的重定向 1>&2

二、echo

1、超级终端的字体背景和颜色显示

格式: echo -e “\033[字背景颜色;字体颜色m字符串\033[0m”

例如:
echo -e “\033[41;36m something here \033[0m”

其中41的位置代表底色, 36的位置是代表字的颜色

那些ascii code 是对颜色调用的始末.
\033[ ; m …… \033[0m

参考:

https://blog.csdn.net/panpan639944806/article/details/23930553

https://blog.csdn.net/u014470361/article/details/81512330

颜色 字色值 背景色值
黑色 30 40
紅色 31 41
綠色 32 42
黃色 33 43
藍色 34 44
紫紅色 35 45
青藍色 36 46
白色 37 47

三、判断语句

1、对字符串的判断

判断参数个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if [[ -z $2 ]]
then
echo 'Error:参数数量过少,请重新输入'
exit_script
fi
if [[ -n $3 ]]
then
echo 'Error:参数数量过多,请重新输入'
exit_script
fi
if [ -z ${xcarchive_file_path} ] \
|| [ -z ${APPENVIRONMENT} ];
then
echo 'Error:上述2个参数都不能为空'
echo '请重新执行:sh base_exportArchive.sh ${xcarchive_file_path} ${APPENVIRONMENT}'
exit_script
fi

1.1、截取字符串

注意赋值等号两边不能有空格

注意赋值等号两边不能有空格

注意赋值等号两边不能有空格

1.1.1、根据路径取目录和文件名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
file_path=d/dir1/dir2/file.png
file_dir=${file_path%/*}
file_name=${file_path##*/}
echo "file_dir = ${file_dir}"
echo "file_name = ${file_name}"
exit


APPPROJECT_NAME=${workspace_file_path##*/}


xcarchive_file_path=./aaa/bbb/ccc/App1Enterprise.xcarchive
xcarchive_file_dir=${xcarchive_file_path%/*} # 文件目录
xcarchive_file_name_withSuffix=${xcarchive_file_path##*/} # 文件主名+文件后缀名
xcarchive_file_name=${xcarchive_file_name_withSuffix%.*} # 文件主名
xcarchive_file_suffix=${xcarchive_file_name_withSuffix##*.}# 文件后缀名
echo "xcarchive_file_dir: ${xcarchive_file_dir}"
echo "xcarchive_file_name: ${xcarchive_file_name}"
echo "xcarchive_file_suffix: ${xcarchive_file_suffix}"
1.1.2、根据1.0.0+100去版本号和编译号
1
2
3
4
5
6
version=1.0.0+100
app_bundle_version=${version%+*} # 取+号左侧,结果1.0.0()
app_build_version=${version#*+} # 取+号右侧,结果100(注意赋值等号两边不能有空格)
echo "app_bundle_version = ${app_bundle_version}"
echo "app_build_version = ${app_build_version}"
exit

1、环境判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
APPENVIRONMENT=$2           # app的打包环境
echo "app的打包环境APPENVIRONMENT: ${APPENVIRONMENT}"


CUR_DIR=$PWD #$PWD代表获取当前路径,当cd后,$PWD也会跟着更新到新的cd路径。这个和在终端操作是一样的道理的
if [ $APPENVIRONMENT == "Product" ]; # 生产环境
then
ExportOptionsPlist_PATH=$CUR_DIR/ExportOptions_inhouse.plist
elif [ $APPENVIRONMENT == "PreProduct" ]; # 预生产环境
then
ExportOptionsPlist_PATH=$CUR_DIR/ExportOptions_adhoc.plist
elif [ $APPENVIRONMENT == "Develop1" -o $APPENVIRONMENT == "Develop2" -o $APPENVIRONMENT == "Develop3" ];
then # 测试环境
ExportOptionsPlist_PATH=$CUR_DIR/ExportOptions_dev.plist
else # 不是允许/支持的环境
echo "-------- Error:所执行的脚本命令中,输入的${APPENVIRONMENT}不是允许/支持的环境,故不再继续执行 --------"
exit
fi

1、判断字符串是否以某个字符串结尾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 判断字符串是否以某个字符串结尾
endWithSuffix() {
fullString=$1
suffixString=$2
echo "判断 ${fullString} 是否以 ${suffixString} 结尾"
file_name_withSuffix=${fullString##*/} # 文件主名+文件后缀名
#file_name=${file_name_withSuffix%.*} # 文件主名
file_suffix=${file_name_withSuffix##*.} # 文件后缀名
#echo "file_name: ${file_name}"
echo "file_suffix: ${file_suffix}"
echo "suffixString: ${suffixString}"
if [ ${file_suffix} == ${suffixString} ]; then
echo 'suffix correct'
return 1
else
echo 'suffix error'
return 0
fi
}
# 使用示例:
# endWithSuffix "./aaa/bbb/ccc/App1Enterprise.xcarchive" 'xcarchive'
endWithSuffix "./aaa/bbb/ccc/App1Enterprise.ipa" 'xcarchive'
if [ $? == 0 ]; then
echo '执行失败:文件后缀错误,请重新输入'
exit
fi
exit

1.1、判断是不是以http开头

if [[$1 =~^http.* ]]; then
commond
else
commond
fi

1、shell中[[]]和[]的主要区别

  • 实际上是bash 中 test 命令的简写。即所有的 [ expr ] 等于 test expr
  • [[ expr ]] 是bash中真正的条件判断语句

2、对文件名filename的判断

1、获取当前文件路径

1
2
curPath=$(cd "$(dirname "$0")"; pwd) # 获取当前文件路径
echo "curPath=${curPath}"
表达式 含义
-e filename 如果 filename存在,则为真
-d filename 如果 filename为目录,则为真
-f filename 如果 filename为常规文件,则为真
-L filename 如果 filename为符号链接,则为真
-h filename 如果文件是软链接,则为真

使用举例:

1
2
3
4
5
6
7
8
9
10
11
AssertExists() {
if [[ ! -e "$1" ]]; then
if [[ -h "$1" ]]; then
EchoError "The path $1 is a symlink to a path that does not exist"
else
EchoError "The path $1 does not exist"
fi
exit -1
fi
return 0
}

其他表达式:

表达式 含义
-r filename 如果 filename可读,则为真
-w filename 如果 filename可写,则为真
-x filename 如果 filename可执行,则为真
-s filename 如果文件长度不为0,则为真
filename1 -nt filename2 如果 filename1比 filename2新,则为真。
filename1 -ot filename2 如果 filename1比 filename2旧,则为真。

3、对字符串str的判断

表达式 含义
if [ str1 = str2 ] 当两个串有相同内容、长度时为真
if [ str1 != str2 ] 当串str1和str2不等时为真
if [ -n str1 ] 当串的长度大于0时为真(串非空)
if [ -z str1 ] 当串的长度为0时为真(空串)
if [ str1 ] 当串str1为非空时为真

shell 中利用 -n 来判定字符串非空。

使用示例:

1
2
3
4
5
6
7
8
9
RunCommand() {
if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then
echo "♦ $*"
fi
"$@"
return $?
}

# 如果$VERBOSE_SCRIPT_LOGGING值不为空,则通过"$*"打印传递给脚本或函数的所有参数(将所有的参数作为一个整体输出),如果为空,则通过"$@"传递给脚本或函数的所有参数(将所有的参数依次分开输出),并通过$?返回上个命令的退出状态,或函数的返回值。

注意

  • 使用[]和[[]]的时候不要吝啬空格,每一项两边都要有空格,[[ 1 == 2 ]]的结果为“假”,但[[ 1==2 ]]的结果为“真”!

判断

1、数组里是否包含某个元素

1
2
3
4
5
6
7
8
noBranchNames=("HEAD" "->")
if [[ "${noBranchNames[*]}" =~ ${devBranchName} ]]; then
#echo "devBranceMapString=${devBranceMapString}"
packageMergerBranchString+="${devBranceName}:${devBranceDes}#"
else
echo ${PackageErrorCode}:${PackageErrorMessage}
exit_script
fi

文件包含/公用代码

  • 权威知识请查看:

    Shell 文件包含

    testReturn1.sh

    1
    2
    > OUTPUT_ARCHIVE_PATH="/User/xxx/output/abc"
    >

    d

    1
    2
    3
    4
    5
    6
    > #!/bin/bash
    > . ./testReturn1.sh
    > # source ./testReturn1.sh # 此种方式也可以
    > # sh ./testReturn1.sh # 此种方式无法拿到另一个shell中的变量
    > echo "OUTPUT_ARCHIVE_PATH=${OUTPUT_ARCHIVE_PATH}"
    >

文件操作

1、mkdir -p中的 p有什么用

-p, –parents 需要时创建上层目录,如目录早已存在则不当作错误

命令格式:mkdir [-p] DirName

说明:建立一个子目录。

参数:-p 确保目录名称存在,如果目录不存在的就新创建一个。

1
2


获取当前文件所在的目录

1
2
3
4
5
# 注意:${0} 指的是执行这个方法的那个文件的所在目录。
# 所以当外部不是执行脚本,而只是引用脚本,即使用如 source ./xx.sh 的时候,所以无法获取我们真正想要的当前文件的所在目录
curdir=$(cd $(dirname $0); pwd)
echo "脚本$0当前文件所在的目录:${curdir}"
exit