美文网首页
shell脚本的使用及整理

shell脚本的使用及整理

作者: 拿根针尖对麦芒 | 来源:发表于2020-04-22 16:14 被阅读0次

    Overview

    这篇文章应该算对自己之前使用Jenkins、Shell脚本打包项目的总结。

    毕竟之前很多东西要么存到项目中,要么存在笔记中,并没有进行整理。

    关于shell

    关于shell脚本的介绍,可以查看百度百科中的介绍。

    任何代码最终需要被‘翻译’成二进制的形式才能在计算机中执行。
    
    有的编程语言,如C/C++、Pascal、Go语言、汇编等,必须在程序运行之前将所有代码都编程成二进制形式(可执行文件),用户拿到最终生成的可执行文件,看不到源码。
    
    这个过程叫做编译(Compile),这样的编程语言叫做[编译型语言],完成编译过程的软件叫做编译器[Compiler]。
    
    而有的编程语言,如Shell、JavaScript、Python、PHP等,需要一边执行一边翻译,不会生成任何可执行文件,用户拿到源代码才能运行程序。程序运行后会即时翻译,翻译完一部分执行一部分,不用等到所有代码都翻译完。
    
    这个过程叫做解释,这样的编程语言叫做[解释型语言]或者[脚本语言](Script),完成解释过程的软件叫做解释器。
    
    编译型语言的优点是执行速度快、对硬件要求低、保密性好,适合开发操作系统、大型应用程序、数据库等。
    
    脚本语言的优点是使用灵活、部署容易、跨平台性好、非常适合Web开发以及小工具的制作。
    
    Shell 就是一种脚本语言,我们编写完源码后不用编译,直接运行源码即可。
    
    

    shell历史

    由于历史原因,UNIX系统上有很多种Shell:
    
    1.sh(Bourne Shell):由Steve Bourne开发,各种UNIX系统都配有sh。
    
    2.csh(C Shell):由Bill Joy开发,随BSD UNIX发布,它的流程控制语句很像C语言,支持很多Bourne Shell所不支持的功能:作业控制,命令历史,命令行编辑。
    
    3.ksh(Korn Shell):由David Korn开发,向后兼容sh的功能,并且添加了csh引入的新功能,是目前很多UNIX系统标准配置的Shell,在这些系统上/bin/sh往往是指向/bin/ksh的符号链接。
    
    4.tcsh(TENEX C Shell):是csh的增强版本,引入了命令补全等功能,在FreeBSD、Mac OS X等系统上替代了csh。
    
    5.bash(Bourne Again Shell):由GNU开发的Shell,主要目标是与POSIX标准保持一致,同时兼顾对sh的兼容,bash从csh和ksh借鉴了很多功能,是各种Linux发行版标准配置的Shell,在Linux系统上/bin/sh往往是指向/bin/bash的符号链接。虽然如此,bash和sh还是有很多不同的,一方面,bash扩展了一些命令和参数,另一方面,bash并不完全和sh兼容,有些行为并不一致,所以bash需要模拟sh的行为:当我们通过sh这个程序名启动bash时,bash可以假装自己是sh,不认扩展的命令,并且行为与sh保持一致。
    

    术语名词

    - IDC--(Internet Data Center)互联网数据中心,主要服务包括整机租用、服务器托管、机柜租用、机房租用、专线接入和网络管理服务等。广义上的IDC业务,实际上就是数据中心所提供的一切服务。客户租用数据中心的服务器和带宽,并利用数据中心的技术力量,来实现自己对软、硬件的要求,搭建自己的互联网平台,享用数据中心所提供的一系列服务。
    
    - ISP--(Internet Service Provider)互联网服务提供商,即向广大用户综合提供互联网接入业务、信息业务、和增值业务的电信运营商。
    
    - ICP--(Internet Content Provider)互联网内容提供商,向广大用户综合提供互联网信息业务和增值业务的电信运营商。 根据中华人民共和国国务院令第292号《互联网信息服务管理办法》规定,国家对提供互联网信息服务的ICP实行许可证制度。从而,ICP证成为网站经营的许可证,经营性网站必须办理ICP证,否则就属于非法经营。因此,办理ICP证是企业网站合法经营的需要.
    
    - CDN--(Content Delivery Network)内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。 CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。
    
    - LVS--(Linux Virtual Server)的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统。LVS集群采用IP负载均衡技术和基于内容请求分发技术。调度器具有很好的吞吐率,将请求均衡地转移到不同的服务器上执行,且调度器自动屏蔽掉服务器的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。整个服务器集群的结构对客户是透明的,而且无需修改客户端和服务器端的程序。为此,在设计时需要考虑系统的透明性、可伸缩性、高可用性和易管理性。
    
    - CGI--(Common Gateway Interface)通用网关接口。CGI规范允许Web服务器执行外部程序,并将它们的输出发送给Web浏览器,CGI将Web的一组简单的静态超媒体文档变成一个完整的新的交互式媒体。
    
    - GSLB--(Global Server Load Balance,全局负载均衡)作为 CDN 系统架构中最核心的部分,负责流量调度.基于DNS的GSLB 绝大部分使用负载均衡技术的应用都通过域名来访问目的主机,在用户发出任何应用连接请求时,首先必须通过DNS请求获得服务器的IP地址,基于DNS的GSLB正是在返回DNS解析结果的过程中进行智能决策,给用户返回一个最佳的服务IP。用户应用流程与没有GSLB时未发生任何变化。这也是市场上主流的GSLB技术。
    
    - BOSS--(Business & Operation Support System,BOSS)是业务运营支撑系统。通常所说的BOSS分为四个部分:计费及结算系统、营业与账务系统、客户服务系统和决策支持系统。BOSS从业务层面来看就是一个框架,来承载业务系统、CRM系统、计费系统。实现统一框架中的纵向、横向管理。该系统最早由电信部门的计费系统发展演变而来,基本功能包括客户资料管理、产品管理、用户订购管理、计费、出帐、结算等,负责登记客户资料、管理用户订购服务的提供、实时的根据不同产品、套餐的资费标准计算业务(手机、固定电话用户通话时、点播收视、宽带流量与时间等)的消费金额,准实时及定期计算用户帐单,实时或定期结算用户各种消费费用。
    

    常用shell命令

    获取SDK或IPA的版本号

    使用shell脚本获取SDK或IPA的版本号,跟我们在代码中获取IPA的版本号一样,都是解析Info.plist中的CFBundleShortVersionString

    所以在这里就可以使用Mac自带的专门解析plist的小工具PlistBuddy来获取项目的版本号。

    使用方式如下:

    #获取Inof.plist中的版本号
    buildVersion=$(/usr/libexec/PlistBuddy -c "print:CFBundleShortVersionString" ${projectPath}../tpocr/tpocr/Info.plist)
    

    同理我们可以将打包IPA所需的schemeconfiguration……等信息存放到config.plist中,使用PlistBuddy直接读取,我们需要修改相应参数的时候可以直接修改config.plist,避免直接修改脚本。

    #工程项目路径
    projectPath="$(pwd)"
    #build配置文件
    buildConfigFile="${projectPath}/Packaging/config.plist"
    #打包的target名称
    buildSchemeName=$(/usr/libexec/PlistBuddy -c "print:build:scheme" ${buildConfigFile})
    #build configuration
    buildConfiguration=$(/usr/libexec/PlistBuddy -c "print:build:configuration" ${buildConfigFile})
    #export options plist
    exportOptionsPlist="${projectPath}/Packaging/$(/usr/libexec/PlistBuddy -c "print:build:exportPlist" ${buildConfigFile}).plist"
    #用于签名的企业开发者名称
    enterpriseDistributionSignName=$(/usr/libexec/PlistBuddy -c "print:resign:enterpriseName" ${buildConfigFile})
    #企业打包provision证书
    enterpriseProvisionFile="${projectPath}/Packaging/$(/usr/libexec/PlistBuddy -c "print:resign:enterpriseProvision" ${buildConfigFile}).mobileprovision"
    #Jenkins job
    jenkinsJob=$(/usr/libexec/PlistBuddy -c "print:deliver:jenkinsJob" ${buildConfigFile})
    #fir api token
    firAPIToken=$(/usr/libexec/PlistBuddy -c "print:deliver:fir" ${buildConfigFile})
    

    使用Xcodebuild指令对项目进行打包

    xcodebuild是命令行工具包(Command Line Tools)中的一项,是一个轻量型的、可以与Xcode分开的、在Mac上单独下载的命令行工具包。

    /usr/bin目录下,我们可以在终端使用open /usr/bin查找该工具。而在usr/bin目录下还有很多命令工具,比如:git、make、xcrun等。

    在xcode9后build不指定任何签名信息,具体编译命令如下所示:

    #编译IPA
    xcodebuild -workspace ${workspace} -scheme "$buildSchemeName" -configuration "$buildConfiguration" -sdk iphoneos clean archive -archivePath ${archiveFilePath} | xcpretty
    
    #导出ipa文件
    xcodebuild -allowProvisioningUpdates -exportArchive -archivePath ${archiveFilePath} -exportPath ${ipaDir} -exportOptionsPlist ${exportOptionsPlist} | xcpretty
    
    

    导出的文件中除了导出ipa包以外,还有dSYM文件。

    dSYM是符号表文件,每次打包我们需要保存好,如果后面项目出现问题,我们可以使用dSYM分析其崩溃信息。

    使用xcodebuild指令打包Framework

    我们手动打包Framework的时候,是先将每个指令集进行打包,然后使用lipo -create将其合并,使用命令打包也是这个逻辑。

    ### Build simulator platform. (i386, x86_64)
    echo "========== Build Simulator Platform =========="
    echo "===== Build Simulator Platform: i386 ====="
    xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_SIMULATOR_BUILD_DIR}/i386" SYMROOT="${SYMROOT}" ARCHS='i386' VALID_ARCHS='i386' -UseModernBuildSystem=NO $ACTION
    
    echo "===== Build Simulator Platform: x86_64 ====="
    xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_SIMULATOR_BUILD_DIR}/x86_64" SYMROOT="${SYMROOT}" ARCHS='x86_64' VALID_ARCHS='x86_64' -UseModernBuildSystem=NO $ACTION
    
    ### Build device platform. (armv7, arm64)
    echo "========== Build Device Platform =========="
    echo "===== Build Device Platform: armv7 ====="
    xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}"  CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/armv7" SYMROOT="${SYMROOT}" ARCHS='armv7' VALID_ARCHS='armv7' -UseModernBuildSystem=NO $ACTION
    
    echo "===== Build Device Platform: arm64 ====="
    xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/arm64" SYMROOT="${SYMROOT}" ARCHS='arm64'  VALID_ARCHS='arm64' -UseModernBuildSystem=NO $ACTION
    
    ### Build device platform. (arm64, armv7)
    echo "========== Build Universal Platform =========="
    ## Copy the framework structure to the universal folder (clean it first).
    rm -rf "${UNIVERSAL_OUTPUTFOLDER}"
    mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
    ## Copy the last product files of xcodebuild command.
    cp -R "${IPHONE_DEVICE_BUILD_DIR}/arm64/${PRODUCT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/${PRODUCT_NAME}.framework"
    echo "Smash them together to combine all architectures."
    lipo -create  "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/i386/${PRODUCT_NAME}.framework/${PRODUCT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/x86_64/${PRODUCT_NAME}.framework/${PRODUCT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/armv7/${PRODUCT_NAME}.framework/${PRODUCT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/arm64/${PRODUCT_NAME}.framework/${PRODUCT_NAME}" -output "${UNIVERSAL_OUTPUTFOLDER}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}"
    echo "Copy Framework Code Signature"
    cp -R "${IPHONE_SIMULATOR_BUILD_DIR}/x86_64/${PRODUCT_NAME}.framework/_CodeSignature" "${UNIVERSAL_OUTPUTFOLDER}/${PRODUCT_NAME}.framework/"
    

    关于xcpretty

    xcpretty拎出来单独说,是因为刚复工回来,更新了电脑的ruby然后导致了打包脚本不能使用,才注意到它。

    xcpretty是一个用ruby实现的小工具,可以使用gem install xcpretty,作用是美化xcodebuild冗长的日志输入。

    如需使用xcpretty对日志进行美化只需要在xcodebuild命令后面加入| xcpretty即可。

    类似于下方:

    xcodebuild -workspace ${workspace} -scheme "$buildSchemeName" -configuration "$buildConfiguration" | xcpretty
    

    for循环

    shell脚本的for循环结构和我们的OCfor in遍历有些类似。
    例如下方脚本:

    #获取所有jar包并上传
    for workitem in `ls ${updatePath}/`; do
    workspaceDir=${updatePath}"/"$workitem
    echo "\n---jar包目录:\n${workspaceDir}---\n"
    java -jar /Users/apple/Desktop/build/UpdateService/jenkins-file-service.jar ${workspaceDir} 1 /home/verifaceops/jenkins-file/taiping_src/
    done
    
    

    该脚本其主要功能是遍历${updatePath}目录下的的文件,然后将所有文件上传到公司内网的文件服务器中。

    这很类似于我们了解的for in遍历了。

    for循环的一般格式:

    for var in item1 item2 ... itemN
    do
        command1
        command2
        ...
        commandN
    done
    

    while循环

    shll中的while循环也跟OCC的用法和写法类似,比如下方一个验证密码是否正确(当然在现实中并不会用到)。

    #脚本内容:
    echo "请输入打包密码:"
    read TRY
    while [ "$TRY" != "tplife" ]; do
      echo "抱歉,密码错误!\n请重新输入:"
      read TRY
    done
    
    #终端输出:
    cntp@TPL-0000-161520deMacBook-Pro Python_Project % sh build.sh
    请输入打包密码:
    123
    抱歉,密码错误!
    请重新输入:
    tplife
    cntp@TPL-0000-161520deMacBook-Pro Python_Project %
    

    if判断语句

    shellif语法跟OC类似,但感觉所有语言并没有OC写着优雅。

    if语句语法格式:

    if condition
    then
        command1 
        command2
        ...
        commandN 
    fi
    

    if else 语法格式:

    if condition
    then
        command1 
        command2
        ...
        commandN
    else
        command
    fi
    

    if else-if else 语法格式:

    if condition1
    then
        command1
    elif condition2 
    then 
        command2
    else
        commandN
    fi
    
    -eq           //等于
    -ne           //不等于
    -gt            //大于 (greater )
    -lt            //小于  (less)
    -ge            //大于等于
    -le            //小于等于
    
    命令的逻辑关系:
    在linux 中 命令执行状态:0 为真,其他为假
    逻辑与: &&
    第一个条件为假时,第二条件不用再判断,最终结果已经有;
    第一个条件为真时,第二条件必须得判断;
    逻辑或: ||
    逻辑非: !
    

    例如:

    #xcode9后build不指定任何签名信息
    xcodebuild -workspace ${workspace} -scheme "$buildSchemeName" -configuration "$buildConfiguration" -sdk iphoneos clean archive -archivePath ${archiveFilePath} | xcpretty
    
    EXCODE=$?
    if [ "$EXCODE" == "0" ]; then
    echo "Build, Archive ----> O.K"
    else
    echo "***********************编译失败********************************"
    exit 1
    fi
    echo "***********************结束build archive app文件***********************"
    
    
    #将打包的apk和IPA包上传到蒲公英
    #!/bin/sh
    
    #  update.sh
    #  
    #
    #  Created by CNTP on 2020/04/14.
    #
    
    export LANG="en_US.UTF-8"
    
    updateAndroidPath="/Users/apple/Desktop/build/OCR/Android"
    updateiOSPath="/Users/apple/Desktop/build/OCR/iOS"
    userKey="**"
    apiKey="**"
    
    # ----- 上传Android apk -----
    echo "\n\n----- 上传Android apk -----\n\n"
    
    for workitem in `ls ${updateAndroidPath}`; do
    filePath=${updateAndroidPath}"/"$workitem
    if [[ "${filePath##*.}"x = "apk"x ]]; then
    echo "----文件----${filePath}---"
    curl -F "file=@$filePath" \
    -F "uKey=${userKey}" \
    -F "_api_key=${apiKey}" \
    https://www.pgyer.com/apiv1/app/upload
    fi
    done
    
    #----- 上传iOS IPA -----
    echo "\n\n----- 上传iOS IPA -----\n\n"
    cd ${updateiOSPath}
    unzip ipa-1.0.zip
    for workitem in `ls ${updateiOSPath}/ipa-1.0/1.0`; do
    filePath=${updateiOSPath}"/ipa-1.0/1.0/"$workitem
    if [[ "${filePath##*.}"x = "ipa"x ]]; then
    echo "----文件----${filePath}---"
    curl -F "file=@$filePath" \
    -F "uKey=${userKey}" \
    -F "_api_key=${apiKey}" \
    https://www.pgyer.com/apiv1/app/upload
    rm -rf ${updateiOSPath}/ipa-1.0
    fi
    done
    
    

    Android打包

    Android打包是调用工程根目录下的gradlew来完成打包。

    cd到相应工程目录,然后执行gradlew clean清理缓存,然后再执行gradlew assembleRelease进行打包。

    具体命令如下:

    gradlew clean
    gradlew assembleRelease
    

    gradlew clean是清理工程目录下的build文件夹。
    gradlew assembleRelease是编译并打Release包,同理gradlew assembleDebug是编译是打Debug包。

    我们也可以使用gradlew build命令将debugrelease环境的包都打出来。

    服务端打包

    服务端那边是使用Maven进行自动化打包。

    全程也只有下方一个指令。

    mvn clean install -DskipTests
    
    

    首先使用Maven需要在Mac系统上安装和配置Maven环境。

    具体配置可以参考Mac系统下载、安装和配置Maven环境这篇文章。

    因还有其他逻辑,所以将所有逻辑存放在build.sh文件中。但在调用时发现,会报配置出错。

    所以需要在Jenkins配置的Execute shell中添加#!/bin/bash -ilex

    其中build.sh为编译jar包的脚本。

    #!/bin/bash -ilex
    cd /Users/apple/Desktop/build/taiping_src
    sh build.sh
    

    参考资料:

    相关文章

      网友评论

          本文标题:shell脚本的使用及整理

          本文链接:https://www.haomeiwen.com/subject/lhtwihtx.html