Linux Shell脚本之远程自动化部署java maven项目
脚本功能: 检查运行环境(包括运行权限、网络、DNS解析等),自动从git上获取java maven项目工程源码,在机器A上build,build完成后,备份机器B上原有的配置文件(如果存在),将Class、Lib文件和备份的配置文件等上传到机器B,重新启动机器B上的服务以便变更生效。 脚本特点: 1.(与之前的自动部署脚本相比)全新优化了脚本代码,更friendly,结构更紧凑、逻辑更加严谨 2.Public header删除了无用或者不好用的有色彩显示函数,并修正了WORKDIR不是绝对路径可能导致的bug 3.修正了域名解析判断是否正常的一个bug,该bug可能导致遇到无法解析后不断尝试解析 4.全新的通用main函数,自带文件锁和自动接受处理INT、TERM、EXIT进程信号,整个脚本更加的模块化和标准化 5.针对项目方面,增加了对java项目配置文件的备份和还原功能,增加了对docker容器的支持 使用办法: 将脚本上传到Linux任意目录,至少修改三个变量(分别是项目源码的git地址、部署对象的IP、部署对象的目标目录): project_clone="ssh://git@xxx/xxx.git" deploy_target_host_ip="xxx.xxx.xxx.xxx" project_top_directory_to_target_host="/path/to/deploy/project" 如果此项目依赖于其他项目,则修改“project_clone_depends_1”变量,如果要想使部署后自动生效,则,可以将启动脚本放在项目git源码下的bin目录下,默认名称为“startup.sh” 如果此项目部署在目标主机的docker容器内,则修改“docker_container_name”变量,以便重启容器生效。 多个容器启动的依赖关系的处理已经在TODO计划中。 首次执行需要将配置文件准确的写入项目文件中,一次写入后期无须修改,如需修改,直接修改部署对象主机上的配置文件即可。 在任意位置使用下方命令运行即可,脚本一旦运行一次,自动添加可执行权限,无须手动添加。 bash/path/to/this.sh 可以从GitHub上获取最新脚本内容:https://github.com/DingGuodong/AutomaticDeployJavaMavenProject 脚本内容: #!/bin/bash #Name:doDeploy.sh #ExecutethisshellscripttodeployJavaprojectsbuiltbyMavenautomaticallyonremotehosts. #debugoption #_XTRACE_FUNCTIONS=$(set+o|grepxtrace) #set-oxtrace #defineuserfriendlymessages header=" Function:ExecutethisshellscripttodeployJavaprojectsbuiltbyMavenautomaticallyonremotehosts. License:Opensourcesoftware " #definevariables #Wheretogetsourcecode project_clone_depends_1="" project_clone="ssh://git@xxx/xxx.git" deploy_target_host_ip="xxx.xxx.xxx.xxx" project_top_directory_to_target_host="/path/to/deploy/project" docker_container_name="" #Settinghowmanydaysdoyouwantsaveoldreleases,defaultis10days save_old_releases_for_days=10 #enddefinevariables #pretreatment test-z${project_clone_depends_1}||project_clone_target_depends_1="`echo${project_clone_depends_1}|awk-F'[/.]+''{print$(NF-1)}'`" project_clone_target="`echo${project_clone}|awk-F'[/.]+''{print$(NF-1)}'`" project_clone_repository_name=${project_clone_target} #endpretreatment #Publicheader #============================================================================================================================= #resolvelinks-$0maybeasymboliclink #learnfromapache-tomcat-6.x.xx/bin/catalina.sh PRG="$0" while[-h"$PRG"];do ls=`ls-ld"$PRG"` link=`expr"$ls":'.*->\(.*\)$'` ifexpr"$link":'/.*'>/dev/null;then PRG="$link" else PRG=`dirname"$PRG"`/"$link" fi done #Getstandardenvironmentvariables PRGDIR=`dirname"$PRG"` #echocolorfunction,smarter,learnfromlnmp.orglnmpinstall.sh functionecho_r(){ #Colorred:Error,Failed [$#-ne1]&&return1 echo-e"\033[31m$1\033[0m" } functionecho_g(){ #Colorgreen:Success [$#-ne1]&&return1 echo-e"\033[32m$1\033[0m" } functionecho_y(){ #Coloryellow:Warning [$#-ne1]&&return1 echo-e"\033[33m$1\033[0m" } functionecho_b(){ #Colorblue:Debug,friendlyprompt [$#-ne1]&&return1 echo-e"\033[34m$1\033[0m" } #endechocolorfunction,smarter #WORKDIR="`realpath${WORKDIR}`" WORKDIR="`readlink-f${PRGDIR}`" #endpublicheader #============================================================================================================================= USER="`id-un`" LOGNAME="$USER" if[$UID-ne0];then echo"WARNING:Runningasanon-rootuser,\"$LOGNAME\".Functionalitymaybeunavailable.Onlyrootcanusesomecommandsoroptions" fi command_exists(){ #which"$@">/dev/null2>&1 command-v"$@">/dev/null2>&1 } check_command_can_be_execute(){ [$#-ne1]&&return1 command_exists$1 } check_network_connectivity(){ echo_b"checkingnetworkconnectivity..." network_address_to_check=8.8.4.4 stable_network_address_to_check=114.114.114.114 ping_count=2 ping-c${ping_count}${network_address_to_check}>/dev/null retval=$? if[${retval}-ne0];then ifping-c${ping_count}${stable_network_address_to_check}>/dev/null;then echo_g"Networkto$stable_network_address_to_checksucceed!" echo_y"Note:networkto$network_address_to_checkfailedonce!maybejustsomepackagesloss." elif!iproute|grepdefault>/dev/null;then echo_r"Networkisunreachable,gatewayisnotset." exit1 elif!ping-c2$(iproute|awk'/default/{print$3}')>/dev/null;then echo_r"Networkisunreachable,gatewayisunreachable." exit1 else echo_r"Networkisblocked!" exit1 fi elif[${retval}-eq0];then echo_g"Checknetworkconnectivitypassed!" fi } check_name_resolve(){ echo_b"checkingDNSnameresolve..." target_name_to_resolve="github.com" stable_target_name_to_resolve="www.aliyun.com" ping_count=1 if!ping-c${ping_count}${target_name_to_resolve}>/dev/null;then echo_y"Namelookupfailedfor$target_name_to_resolvewith$ping_counttimes" ifping-c${ping_count}${stable_target_name_to_resolve}>/dev/null;then echo_g"Namelookupsuccessfor$stable_target_name_to_resolvewith$ping_counttimes" fi eval_md5sum_of_nameserver_config="`md5sum/etc/resolv.conf|awk'{print$1}'`" iftest${eval_md5sum_of_nameserver_config}="674ea91675cdfac353bffbf49dc593c3";then echo_y"Nameserverconfigfileisvalidated,butnamelookupfailedfor$target_name_to_resolvewith$ping_counttimes" return0 fi [-f/etc/resolv.conf]&&cp/etc/resolv.conf/etc/resolv.conf_$(date+%Y%m%d%H%M%S)~ cat>/etc/resolv.conf<<eof nameserver114.114.114.114 nameserver8.8.4.4 eof check_name_resolve else echo_g"CheckDNSnameresolvepassed!" return0 fi } functioncheckOtherDependencies(){ echo_b"Checkingotherdependenciesfordeployprocedure..." echo_b"\tCheckingusercustomizedvariables..." #Refer: #if[-z${var+x}];then #echo"varisunset";elseecho"varissetto'$var'" #fi #if["$varx"="x"];then #echo"varisempty";elseecho"varissetto'$var'" #fi #if[-z$var];then #echo"varisempty";elseecho"varissetto'$var'" #fi if[[-z${project_clone}]];then echo_r"Error:project_cloneisundefined!" exit1 elif[[-z${deploy_target_host_ip}]];then echo_r"Error:deploy_target_host_ipisundefined!" exit1 elif[[-z${project_top_directory_to_target_host}]];then echo_r"Error:project_top_directory_to_target_hostisundefined!" exit1 fi echo_g"\tCheckingusercustomizedvariablespassed!" echo_b"\tCheckingdiskspaceavailable..." disk_space_available=`df${WORKDIR}|tail-n1|awk'{print$(NF-2)}'` if[[${disk_space_available}-lt2097152]];then echo_y"Warning:Diskspaceof$WORKDIRissmallerthan2GB" #exit1 else echo_g"\tCheckingdiskspaceavailablepassed!" fi echo_g"Allrequireddependenciescheckpassed!" } functionsetDirectoryStructureOnLocalHost(){ if[-f${WORKDIR}/.capistrano_ds_lock];then echo_g"Setdirectorystructurehasbeendone,skipping." return fi echo_b"Settingdirectorystructure..." #learnfromcapistrano #Refer:http://capistranorb.com/documentation/getting-started/structure/ #Refer:http://capistranorb.com/documentation/getting-started/structure/# #├──current->/var/www/my_app_name/releases/20150120114500/ #├──releases #│├──20150080072500 #│├──20150090083000 #│├──20150100093500 #│├──20150110104000 #│└──20150120114500 #├──repo #│└──<VCSrelateddata> #├──revisions.log #└──shared #└──<linked_filesandlinked_dirs> #currentisasymlinkpointingtothelatestrelease.Thissymlinkisupdatedattheendofasuccessfuldeployment.Ifthedeploymentfailsinanystepthecurrentsymlinkstillpointstotheoldrelease. #releasesholdsalldeploymentsinatimestampedfolder.Thesefoldersarethetargetofthecurrentsymlink. #repoholdstheversioncontrolsystemconfigured.Incaseofagitrepositorythecontentwillbearawgitrepository(e.g.objects,refs,etc.). #revisions.logisusedtologeverydeployorrollback.Eachentryistimestampedandtheexecutinguser(usernamefromlocalmachine)islisted.DependingonyourVCSdatalikebranchnamesorrevisionnumbersarelistedaswell. #sharedcontainsthelinked_filesandlinked_dirswhicharesymlinkedintoeachrelease.Thisdatapersistsacrossdeploymentsandreleases.Itshouldbeusedforthingslikedatabaseconfigurationfilesandstaticandpersistentuserstoragehandedoverfromonereleasetothenext. #Theapplicationiscompletelycontainedwithinthepathof:deploy_to.Ifyouplanondeployingmultipleapplicationstothesameserver,simplychooseadifferent:deploy_topath. #Checkdirectoriesfordeploy [!-d${WORKDIR}/release]&&mkdir${WORKDIR}/release [!-d${WORKDIR}/repository]&&mkdir${WORKDIR}/repository [!-d${WORKDIR}/share]&&mkdir${WORKDIR}/share #enddirectoriesstructure #Additionaldirectoriesstructureforfulldeployoperation #forbackupremotehostconfigfile [!-d${WORKDIR}/backup]&&mkdir${WORKDIR}/backup #setadirectoriesstructurelock touch${WORKDIR}/.capistrano_ds_lock echo_g"Setdirectorystructuresuccessfully!" } functioncleanOldReleases(){ save_days=${save_old_releases_for_days:-10} if[!-d${WORKDIR}/release];then echo_b"CanNOTfindreleasedirectory,skipping." return fi need_clean=$(find${WORKDIR}/release-mtime+${save_days}-execls'{}'\;) if[!-z${need_clean}];then echo_g"Expiredreleasesfoundandwillberemovedfromproject!" find${WORKDIR}/release-mtime+${save_days}-execrm-rf'{}'\; if[$?-eq0];then echo_g"Expiredreleaseshaveremovedfromproject!" else echo_r"CanNOTremoveexpiredreleases,pleasealtertoAdminusers." fi else echo_g"Allreleasesarenotexpired,skipping." fi } #git_project_clonerepositorybranch functiongit_project_clone(){ set-oerrexit [$#-ge1]&&project_clone_repository="$1" project_clone_repository_name="`echo${project_clone_repository}|awk-F'[/.]+''{print$(NF-1)}'`" project_clone_directory=${WORKDIR}/repository/${project_clone_repository_name} iftest-n$2;then branch="$2" else branch="develop" fi iftest!-d${project_clone_directory};then echo_b"gitclonefrom$project_clone_repository" gitclone${project_clone_repository}${project_clone_directory}>>${WORKDIR}/git_$(date+%Y%m%d)_$$.log2>&1 #TODO(GuodongDing)getbranchnamesorrevisionnumbersfromVCSdata cd${project_clone_directory} gitcheckout${branch}>>${WORKDIR}/git_$(date+%Y%m%d)_$$.log2>&1 cd.. echo_g"gitclonefrom$project_clone_repositorysuccessfully!" else echo_b"gitpullfrom$project_clone_repository" cd${project_clone_directory} gitpull>>${WORKDIR}/git_$(date+%Y%m%d)_$$.log2>&1 gitcheckout${branch}>>${WORKDIR}/git_$(date+%Y%m%d)_$$.log2>&1 #TODO(GuodongDing)getbranchnamesorrevisionnumbersfromVCSdata cd.. echo_g"gitpullfrom$project_clone_repositorysuccessfully!" fi set+oerrexit } functionmaven_build_project_deprecated(){ set-oerrexit echo_b"Domvnbuildjavaproject..." check_command_can_be_executemvn [$#-ge1]&&project_clone_repository="$1" project_clone_repository_name="`echo${project_clone_repository}|awk-F'[/.]+''{print$(NF-1)}'`" project_clone_directory=${WORKDIR}/repository/${project_clone_repository_name} cd${project_clone_directory} mvninstall>>${WORKDIR}/mvn_build_$(date+%Y%m%d)_$$.log2>&1 mvncleanpackage>>${WORKDIR}/mvn_build_$(date+%Y%m%d)_$$.log2>&1 cd.. echo_g"Domvnbuildjavaprojectfinishedwithexitcode0!" set+oerrexit } functionmaven_build_project(){ echo_b"Domvnbuildjavaprojectfor`echo$1|awk-F'[/.]+''{print$(NF-1)}'`..." check_command_can_be_executemvn [$#-ge1]&&project_clone_repository="$1" project_clone_repository_name="`echo${project_clone_repository}|awk-F'[/.]+''{print$(NF-1)}'`" project_clone_directory=${WORKDIR}/repository/${project_clone_repository_name} cd${project_clone_directory} mvninstall>>${WORKDIR}/mvn_build_$(date+%Y%m%d)_$$.log2>&1 retval=$? if[${retval}-ne0];then echo_r"mvninstallfailed!Moredetailsreferto${WORKDIR}/mvn_build_$(date+%Y%m%d)_$$.log" exit1 else echo_g"mvninstallfor${project_clone_repository_name}successfully!" fi mvncleanpackage>>${WORKDIR}/mvn_build_$(date+%Y%m%d)_$$.log2>&1 retval=$? if[${retval}-ne0];then echo_r"mvncleanpackagefor${project_clone_repository_name}failed!Moredetailsreferto${WORKDIR}/mvn_build_$(date+%Y%m%d)_$$.log" exit1 else echo_g"mvncleanpackagefor${project_clone_repository_name}successfully!" fi cd.. echo_g"Domvnbuildjavaprojectfinishedfor${project_clone_repository_name}withexitcode0!" } functioncheck_ssh_can_be_connect(){ [$#-ne1]&&return1 echo_b"Checkifcansshtoremotehost$1..." check_command_can_be_executessh||return1 ssh-i/etc/ssh/ssh_host_rsa_key-p22-oStrictHostKeyChecking=noroot@$1"uname-a>/dev/null2>&1" retval=$? if[${retval}-ne0];then echo_r"Checksshtoremotehost$1failed!" exit1 else echo_g"Checksshtoremotehost$1successfully!" fi } #ssh_execute_command_on_remote_hosthostnamecommand functionssh_execute_command_on_remote_host(){ [$#-ne2]&&return1 ssh-i/etc/ssh/ssh_host_rsa_key-p22-oStrictHostKeyChecking=noroot@$1"$2">>${WORKDIR}/ssh_command_$(date+%Y%m%d)_$$.log retval=$? if[${retval}-ne0];then echo_r"sshexecutecommandonremotehost$2failed!Moredetailsreferto${WORKDIR}/ssh_command_$(date+%Y%m%d)_$$.log" return1 else echo_g"sshexecutecommandonremotehost$2successfully!" return0 fi } functionrestart_docker_container(){ echo_b"Restartingdockercontainer..." [$#-ne1]&&return1 #TODO(GuodongDing)ifweneedrestartmorerelateddockercontainer localdocker_container_name="" test-n$1&&docker_container_name="$1" ssh_execute_command_on_remote_host"dockerrestart$docker_container_name" retval=$? if[${retval}-ne0];then echo_r"restartdockercontainerfor$docker_container_namefailed!" exit1 else echo_g"restartdockercontainerfor$docker_container_namesuccessfully!" return0 fi } #scp_local_files_to_remote_hostlocal_pathremote_hostnameremote_path functionscp_local_files_to_remote_host(){ [$#-ne3]&&return1 [!-d$1-a!-f$1]&&return1 check_ssh_can_be_connect$2 scp-i/etc/ssh/ssh_host_rsa_key-P22-oStrictHostKeyChecking=no-rp$1root@$2:$3>/dev/null2>&1 retval=$? if[${retval}-ne0];then echo_r"scplocalfilestoremotehostfailed!" exit1 else echo_g"scplocalfilestoremotehostsuccessfully!" fi } #scp_remote_files_to_local_hostremote_hostnameremote_pathlocal_path functionscp_remote_files_to_local_host(){ [$#-ne3]&&return1 check_ssh_can_be_connect$1 scp-i/etc/ssh/ssh_host_rsa_key-P22-oStrictHostKeyChecking=no-rproot@$1:$2$3>/dev/null2>&1 retval=$? if[${retval}-ne0];then echo_r"scpremotefilestolocalhostfailed!" exit1 else echo_g"scpremotefilestolocalhostsuccessfully!" fi } functionbackup_remote_host_config_files(){ echo_b"backupremotehostconfigfiles..." scp_remote_files_to_local_host${deploy_target_host_ip}${project_top_directory_to_target_host}/*${WORKDIR}/backup #getconfigfiles ["$(ls-A${WORKDIR}/backup)"]&&find${WORKDIR}/backup/.-typef!-name.-a!-name'*.xml*'-a!-name'*.properties*'-execrm-f--'{}'\; #removeemptydirectory find${WORKDIR}/backup/.-empty-typed-delete #TODO(GuodongDing)improvementshere echo_g"backupremotehostconfigfilesfinished." } functionrollback_remote_host_config_files(){ echo_b"rollbackremotehostconfigfiles..." #scp_local_files_to_remote_host${WORKDIR}/backup${deploy_target_host_ip}${project_top_directory_to_target_host} saved_IFS=$IFS IFS='' cd${WORKDIR}/current forfilein${WORKDIR}/backup/*;do scp_local_files_to_remote_host${file}${deploy_target_host_ip}${project_top_directory_to_target_host} done cd${WORKDIR} IFS=${saved_IFS} #TODO(GuodongDing)ifsaveremotehostconfigfiles #someops #TODO(GuodongDing)improvementshere echo_g"rollbackremotehostconfigfilesfinished." } functiondeploy(){ [-n"$header"]&&echo"$header" #checkadirectorieslock,Note:thisisredundant if[[!-f${WORKDIR}/.lock]];then setDirectoryStructureOnLocalHost fi cleanOldReleases #dodependencieschecking check_network_connectivity check_name_resolve checkOtherDependencies check_ssh_can_be_connect${deploy_target_host_ip} #docorejob #TODO(GuodongDing)ifweneedagit_project_clone"$project_clone_depends_1"hereusingautojudgmentstatement test-z${project_clone_depends_1}||git_project_clone"$project_clone_depends_1" git_project_clone"$project_clone" test-z${project_clone_depends_1}||maven_build_project"$project_clone_depends_1" maven_build_project"$project_clone" cd${WORKDIR} #links_target_directory_to_current #Makedirectorytoreleasedirectory iftest!-d${WORKDIR}/release-o!-d${WORKDIR}/share;then echo_r"capistranodirectorystructureisbroken,makesurethefile.capistrano_ds_lockisdeletedbeforeanewdeploy!" exit1 #test-f${WORKDIR}/.capistrano_ds_lock&&\rm-rf${WORKDIR}/.capistrano_ds_lock fi new_release_just_created="$WORKDIR/release/$(date+%Y%m%d%H%M%S)" [!-d${new_release_just_created}]&&mkdir${new_release_just_created} [-d${WORKDIR}/repository/${project_clone_repository_name}/target/${project_clone_repository_name}/]&&\ \cp-rf${WORKDIR}/repository/${project_clone_repository_name}/target/${project_clone_repository_name}/*${new_release_just_created} #Makesourcecodesymboliclinktocurrent ([-f${WORKDIR}/current]||[-d${WORKDIR}/current])&&rm-rf${WORKDIR}/current ln-s${new_release_just_created}${WORKDIR}/current #backupremotehostconfigfiles backup_remote_host_config_files #scp_local_files_to_remote_host${WORKDIR}/current/${deploy_target_host_ip}${project_top_directory_to_target_host} saved_IFS=$IFS IFS='' cd${WORKDIR}/current forfilein${WORKDIR}/current/*;do scp_local_files_to_remote_host${file}${deploy_target_host_ip}${project_top_directory_to_target_host} done cd${WORKDIR} IFS=${saved_IFS} #rollbackremotehostconfigfiles rollback_remote_host_config_files #Moveconfandlogsdirectivesfromreleasetoshare [-d${WORKDIR}/release/conf]&&mv${WORKDIR}/release/conf${WORKDIR}/share/conf [-d${WORKDIR}/release/logs]&&mv${WORKDIR}/release/logs${WORKDIR}/share/logs #Makeconfandlogssymboliclinktocurrent [-d${WORKDIR}/share/conf]&&ln-s${WORKDIR}/share/conf${WORKDIR}/current/conf [-d${WORKDIR}/share/logs]&&ln-s${WORKDIR}/share/logs${WORKDIR}/current/logs #Startserviceorvalidatestatus if[[-e${WORKDIR}/current/bin/startup.sh]];then ${WORKDIR}/current/bin/startup.shstart RETVAL=$? else #TODO(GuodongDing)externalhealthcheck test-z${docker_container_name}||restart_docker_container${docker_container_name} RETVAL=$? fi #ifstartedok,thencreateaworkableprogramtoafile if[[${RETVAL}-eq0]];then #Notecatwitheofmuststartatrow0,andwitheofendonly,suchasnoblankspaces,etc cat>${WORKDIR}/share/workable_program.log<<eof ${new_release_just_created} eof echo_g"Deploysuccessfully!" echo_g"currentworkableversionis$(cat${WORKDIR}/share/workable_program.log)" #ls--color=auto-l${WORKDIR}/current #ls--color=auto-l${WORKDIR}/current/ else echo_r"Error:Deployfailed!" ${WORKDIR}/`basename$0`rollback fi } #Rollbacktolastrightconfiguration functionrollback(){ [-n"$header"]&&echo"$header" echo_b"Rollbacktolastrightconfiguration..." #Thekeyisfindlastfileswhichcanwork WORKABLE_PROGRAM=`cat${WORKDIR}/share/workable_program.log` if[[-z${WORKABLE_PROGRAM}]];then echo_r"Error:CanNOTfindworkablereleaseversion!Pleasecheckifitisfirstdeployment!" exit1 fi #Stopserviceifwehave if[[-e${WORKDIR}/current/bin/startup.sh]];then ${WORKDIR}/current/bin/startup.shstop fi #Removefaileddeploy rm-rf${WORKDIR}/current #Remakesourcecodesymboliclinktocurrent ln-s${WORKABLE_PROGRAM}${WORKDIR}/current #Remakeconfandlogssymboliclinktocurrent [-d${WORKDIR}/share/conf]&&ln-s${WORKDIR}/share/conf${WORKDIR}/current [-d${WORKDIR}/share/logs]&&ln-s${WORKDIR}/share/logs${WORKDIR}/current #Startserviceorvalidatestatus if[[-e${WORKDIR}/current/bin/startup.sh]];then ${WORKDIR}/current/bin/startup.shstart RETVAL=$? else #TODO(GuodongDing)externalhealthcheck test-z${docker_container_name}||restart_docker_container${docker_container_name} RETVAL=$? fi #ifstartedok,thencreateaworkableprogramtoafile if[[${RETVAL}-eq0]];then echo_g"Rollbacksuccessfully!" echo_g"currentworkableversionis$WORKABLE_PROGRAM" #ls--color=auto-l${WORKDIR}/current fi } functiondestroy(){ [-n"$header"]&&echo"$header" #echoaWarningmessage echo_y"Warning:Thisactionwilldestroyallthisproject,andthisisunrecoverable!" answer="n" echo_y"Doyouwanttodestroythisproject?" read-p"(Defaultno,ifyouwantpleaseinput:y,ifnotpleasepresstheenterbutton):"answer case"$answer"in y|Y|Yes|YES|yes|yES|yEs|YeS|yeS) #deleteallfileexpectforthisscriptself #find:warning:Unixfilenamesusuallydon'tcontainslashes(thoughpathnamesdo).Thatmeansthat'-name`./deploy.sh''willprobablyevaluatetofalseallthetimeonthissystem.Youmightfindthe'-wholename'testmoreuseful,orperhaps'-samefile'.Alternatively,ifyouareusingGNUgrep,youcoulduse'find...-print0|grep-FzZ`./deploy.sh''. #echo$WORKDIR/ #find-L$WORKDIR-typef!-name"$(basename$0)"-execls--color=auto-al{}\; #find-L.-typef!-name"deploy.sh"-execls--color=auto-al{}\; #find-L.-typed-execls--color=auto-al{}\; #find-L./-maxdepth1!-name"deploy.sh"!-wholename"./" #ls|grep-v"filename"|xargsrm-rf find-L${WORKDIR}-maxdepth1!-name"$(basename$0)"!-wholename"$WORKDIR"-execrm-rf'{}'\; if[$?-eq0];then test-f${WORKDIR}/.capistrano_ds_lock&&\rm-rf${WORKDIR}/.capistrano_ds_lock echo_g"Destroythisprojectsuccessfully!Nowwillexitwithstatus0." exit0 else echo_r"Error:somethinggowrong!PleasecheckoraltertoAdminuser!" exit1 fi ;; n|N|No|NO|no|nO) echo_g"destroyactioniscancel" exit0 ;; *) echo_r"Areyoukiddingme?Youareabadkid!" exit1 ;; esac } functionmain(){ lock_filename="lock_$$_$RANDOM" #lock_filename_full_path="/var/lock/subsys/$lock_filename" lock_filename_full_path="/var/lock/$lock_filename" if(set-onoclobber;echo"$$">"$lock_filename_full_path")2>/dev/null;then trap'rm-f"$lock_filename_full_path";exit$?'INTTERMEXIT #Justatestforcallitself,commentit if[[$#-ne1]];then #$0deploy [!-x${WORKDIR}/`basename$0`]&&chmod+x${WORKDIR}/`basename$0` ${WORKDIR}/`basename$0`deploy exit0 fi case$1in deploy) deploy ;; rollback) rollback ;; destroy) destroy ;; help|*) echo"Usage:$0{deploy|rollback|destroy}with$0itself" exit1 ;; esac rm-f"$lock_filename_full_path" trap-INTTERMEXIT else echo"Failedtoacquirelock:$lock_filename_full_path" echo"heldby$(cat${lock_filename_full_path})" fi } main$@ #debugoption #${_XTRACE_FUNCTIONS} 运行效果: 可以尝试使用Chrome浏览器打开该页面,右键单击,选择“在新标签页中打开图片”查看清晰大图。 推荐Windows用户使用PyCharm(5.0.x)结合Bash Support插件编辑bash shell script文件。 注:由于该脚本的大幅度更新,因此最新的运行效果图可以自行运行观察。 --end--