简介Java程序的Shell脚本包装
在许多Java工程中,经常会看到带有程序自定义参数调用Java命令的包装shell脚本。例如,
$ANT_HOME/bin/ant,$GROOVY_HOME/bin/groovy
,甚至在我们的TimeMachineScheduler程序中也能见到
$TIMEMACHINE_HOME/bin/scheduler.sh
编写这些包装脚本很无聊而且容易出错。大多数的问题来自为程序设置正确的classpath。如果你正在为一个公司开发内部项目的话,那么你有可能远离纠结的路径以及环境变量问题。但是对于开源项目,人们需要使包装更加灵活和通用。大多数甚至提供了.bat版本。WindowsDOS确实是个野蛮且被限制的终端而不能很好的满足你项目脚本需求。因此,我常鼓励别人尽量还是多使用Cygwi。至少它具备一个真实的bashshell。其他常见的问题就是这些包装很快就会失去控制而且在你的项目各处都会出现很多冗余脚本。
run-java包装脚本介绍
如果你看到$TIMEMACHINE_HOME/bin/scheduler.sh的代码,你会看到它其实是在同目录下循环调用run-java脚本。
DIR=$(dirname$0) SCHEDULER_HOME=$DIR/.. $DIR/run-java-Dscheduler.home="$SCHEDULER_HOME"timemachine.scheduler.tool.SchedulerServer"$@"
正如你看到的,我们的run-java可以使用-D选项,不仅这样,它同样也能使用-cp选项!而且,你还能在mainclass后面指定这些选项!这样能够使得run-java被其他的脚本包装,并且仍旧能够添加额外的系统属性以及classpath。
例如,TimeMachine附带了Groovy库,所以你可以简单的像这样调用
groovy:$TIMEMACHINE_HOME/bin/run-javagroovy.ui.GroovyMaintest.groovy,而不用再次下载整个分支。
你可以很方便地在任何目录下使用,它确认自己的目录然后可以自动加载lib目录下的任何jar包。现在如果你想要附加更多的jar包来运行Groovy的话,可以如下使用-cp选项:
$TIMEMACHINE_HOME/bin/run-java-cp"$HOME/apps/my-app/lib/*"groovy.ui.GroovyMaintest.groovy通常如果你设置javaclasspath不够小心时会经常导致错误,但是使用run-java可以预先运行一次:
RUN_JAVA_DRY=1$TIMEMACHINE_HOME/bin/run-java-cp"$HOME/apps/my-app/lib/*"groovy.ui.GroovyMaintest.groovy
你只需在命令提示行中运行上面一整行代码即可。它将输出完整的附带所有选项和参数的java命令。
run-script还包含很多其它的选项,你可以通过查看其注释了解。当前的脚本能够在任何的Linuxbash和WindowsCygwin中运行。
在开发中通过Maven使用run-java
根据上面提到的示例,假设项目发布结构如下:
$TIMEMACHINE_HOME +-bin/run-java +-lib/*.jar
但是在开发过程中目录会是怎样呢?一个常见的用例便是:你希望能够运行target/classes下最新编译的代码而不是将整个项目打包或者发布。你同样可以在此种情况下使用run-java。首先,简单的将bin/run-java添加进你的项目,然后运行
mvncompiledependency:copy-dependencies
将会在target/dependency下生成所有的jar文件。就只需要做这些。run-java将自动的检测这些目录,并为你的mainclass创建正确的classpath。
如果你使用Eclipse来开发,那么你的target/classes目录将总是在更新的,run-java便能成为你项目开发中的瑰宝。
获取run-java包装脚本
#!/usr/bin/envbash # #Copyright2012ZemianDeng # #LicensedundertheApacheLicense,Version2.0(the"License"); #youmaynotusethisfileexceptincompliancewiththeLicense. #YoumayobtainacopyoftheLicenseat # #http://www.apache.org/licenses/LICENSE-2.0 # #Unlessrequiredbyapplicablelaworagreedtoinwriting,software #distributedundertheLicenseisdistributedonan"ASIS"BASIS, #WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. #SeetheLicenseforthespecificlanguagegoverningpermissionsand #limitationsundertheLicense. #AwrapperscriptthatrunanyJava6applicationinunix/cygwinenv. # #Thisscriptisassumedtobelocatedinanapplication's"bin"directory.Itwill #autoresolveanysymboliclinkandalwaysruninrelativetothisapplication #directory(whichisoneparentupfromthescript.)Therefore,thisscriptcanbe #runanywhereinthefilesystemanditwillstillreferencethisapplication #directory. # #ThisscriptwillbydefaultautosetupaJavaclasspaththatpicksupany"config" #and"lib"directoriesundertheapplicationdirectory.Italsowillalsoadda #anytypicalMavenprojectoutputdirectoriessuchas"target/test-classes", #"target/classes",and"target/dependency"intoclasspath.Thiscanbedisableby #settingRUN_JAVA_NO_PARSE=1. # #Ifthe"Defaultparameters"sectionbellowdoesn'tmatchtouser'senv,thenuser #mayoverridethesevariablesintheirterminalsessionorpresettheminshell's #profilestartupscript.Thevaluesofallpathshouldbeincygwin/unixpath, #andthisscriptwillautoconvertthemintoWindowspathwhereisneeded. # #UsermaycustomizetheJavaclasspathbysettingRUN_JAVA_CP,whichwillprefixtoexisting #classpath,orusethe"-cp"option,whichwillpostfixtoexistingclasspath. # #Usage: #run-java[java_opts]<java_main_class>[-cp/more/classpath][-Dsysprop=value] # #Example: #run-javaexample.Hello #run-javaexample.Hello-Dname=World #run-javaorg.junit.runner.JUnitCoreexample.HelloTest-cp"C:\apps\lib\junit4.8.2\*" # #Createdby:ZemianDeng03/09/2012 #Thisrunscriptdir(resolvetoabsolutepath) SCRIPT_DIR=$(cd$(dirname$0)&&pwd)#Thisdiriswherethisscriptlive. APP_DIR=$(cd$SCRIPT_DIR/..&&pwd)#Assumetheapplicationdirisonelevelupfromscriptdir. #Defaultparameters JAVA_HOME=${JAVA_HOME:=/apps/jdk}#ThisisthehomedirectoryofJavadevelopmentkit. RUN_JAVA_CP=${RUN_JAVA_CP:=$CLASSPATH}#Aclasspathprefixbefore-classpathoption,defaultto$CLASSPATH RUN_JAVA_OPTS=${RUN_JAVA_OPTS:=}#Javaoptions(-Xmx512m-XX:MaxPermSize=128metc) RUN_JAVA_DEBUG=${RUN_JAVA_DEBUG:=}#Ifnotempty,printthefulljavacommandlinebeforeexecutingit. RUN_JAVA_NO_PARSE=${RUN_JAVA_NO_PARSE:=}#Ifnotempty,skiptheautoparsingof-Dand-cpoptionsfromscriptarguments. RUN_JAVA_NO_AUTOCP=${RUN_JAVA_NO_AUTOCP:=}#Ifnotempty,donotautosetupJavaclasspath RUN_JAVA_DRY=${RUN_JAVA_DRY:=}#Ifnotempty,donotexecJavacommand,butjustprint #OSspecificsupport.$var_must_besettoeithertrueorfalse. CYGWIN=false; case"`uname`"in CYGWIN*)CYGWIN=true;; esac #Definewhereisthejavaexecutableis JAVA_CMD=java if[-d"$JAVA_HOME"];then JAVA_CMD="$JAVA_HOME/bin/java" fi #Autosetupapplciation'sJavaClasspath(onlyiftheyexists) if[-z"$RUN_JAVA_NO_AUTOCP"];then if$CYGWIN;then #ProvideWindowsdirectoryconversion JAVA_HOME_WIN=$(cygpath-aw"$JAVA_HOME") APP_DIR_WIN=$(cygpath-aw"$APP_DIR") if[-d"$APP_DIR_WIN\config"];thenRUN_JAVA_CP="$RUN_JAVA_CP;$APP_DIR_WIN\config";fi if[-d"$APP_DIR_WIN\target\test-classes"];thenRUN_JAVA_CP="$RUN_JAVA_CP;$APP_DIR_WIN\target\test-classes";fi if[-d"$APP_DIR_WIN\target\classes"];thenRUN_JAVA_CP="$RUN_JAVA_CP;$APP_DIR_WIN\target\classes";fi if[-d"$APP_DIR_WIN\target\dependency"];thenRUN_JAVA_CP="$RUN_JAVA_CP;$APP_DIR_WIN\target\dependency\*";fi if[-d"$APP_DIR_WIN\lib"];thenRUN_JAVA_CP="$RUN_JAVA_CP;$APP_DIR_WIN\lib\*";fi else if[-d"$APP_DIR/config"];thenRUN_JAVA_CP="$RUN_JAVA_CP:$APP_DIR/config";fi if[-d"$APP_DIR/target/test-classes"];thenRUN_JAVA_CP="$RUN_JAVA_CP:$APP_DIR/target/test-classes";fi if[-d"$APP_DIR/target/classes"];thenRUN_JAVA_CP="$RUN_JAVA_CP:$APP_DIR/target/classes";fi if[-d"$APP_DIR/target/dependency"];thenRUN_JAVA_CP="$RUN_JAVA_CP:$APP_DIR/target/dependency/*";fi if[-d"$APP_DIR/lib"];thenRUN_JAVA_CP="$RUN_JAVA_CP:$APP_DIR/lib/*";fi fi fi #Parseaddition"-cp"and"-D"aftertheJavamainclassfromscriptarguments #ThisisdoneforconvenientsakesousersdonothavetoexportRUN_JAVA_CPandRUN_JAVA_OPTS #saparately,butnowtheycanpassintoendofthisrun-javascriptinstead. #ThiscanbedisablebysettingRUN_JAVA_NO_PARSE=1. if[-z"$RUN_JAVA_NO_PARSE"];then #Preparevariablesforparsing FOUND_CP= declare-aNEW_ARGS IDX=0 #Parseallargumentsandlookfor"-cp"and"-D" forARGin"$@";do if[[-n$FOUND_CP]];then if["$OS"="Windows_NT"];then #Can'tusecygpathhere,becausecygpathwillautoexpand"*",whichwedonot #want.UserwilljusthavetouseOSpathwhenspecifying"-cp"option. #ARG=$(cygpath-w-a$ARG) RUN_JAVA_CP="$RUN_JAVA_CP;$ARG" else RUN_JAVA_CP="$RUN_JAVA_CP:$ARG" fi FOUND_CP= else case$ARGin '-cp') FOUND_CP=1 ;; '-D'*) RUN_JAVA_OPTS="$RUN_JAVA_OPTS$ARG" ;; *) NEW_ARGS[$IDX]="$ARG" letIDX=$IDX+1 ;; esac fi done #DisplayfullJavacommand. if[-n"$RUN_JAVA_DEBUG"]||[-n"$RUN_JAVA_DRY"];then echo"$JAVA_CMD"$RUN_JAVA_OPTS-cp"$RUN_JAVA_CP""${NEW_ARGS[@]}" fi #RunJavaMainclassusingparsedvariables if[-z"$RUN_JAVA_DRY"];then "$JAVA_CMD"$RUN_JAVA_OPTS-cp"$RUN_JAVA_CP""${NEW_ARGS[@]}" fi else #DisplayfullJavacommand. if[-n"$RUN_JAVA_DEBUG"]||[-n"$RUN_JAVA_DRY"];then echo"$JAVA_CMD"$RUN_JAVA_OPTS-cp"$RUN_JAVA_CP""$@" fi #RunJavaMainclass if[-z"$RUN_JAVA_DRY"];then "$JAVA_CMD"$RUN_JAVA_OPTS-cp"$RUN_JAVA_CP""$@" fi fi