我有一个用于自动部署文件的java应用程序。 我想创建一个shell脚本,以便从svn轻松部署。
目前,我的脚本检出文件,获取文件名,询问一些部署信息,然后将文件名传递给Jar。
通常,我可以在-f选项之后传递多个文件,并且Jar收集每个文件,如果文件存在,则将它们添加到数组中进行处理。如果文件名包含空格,我会在文件名周围添加引号。
这是通常与我的Jar一起使用的输入:
java -jar deploy.jar -f 'Cancellation V2.xsl' 'Final Price.xsl' -e test
我的shell脚本从SVN url的末尾获取文件名,在名称周围添加引号并传递给Jar。我收到这个错误:
'Final Price.xsl' 'Cancellation V2.xsl' not found!
Exception in thread "main" com.DeployException:
at com.ConsoleUtils.parseCommandLineArgs(ConsoleUtils.java:42)
at com.Main.main(Main.java:23)
当Java应用程序在将每个文件名添加到数组之前使用if(file.exists)之前,会抛出此异常。此日志显示两个文件名都作为单个字符串传递,即使shell脚本已正确格式化它们。
这是我的完整Shell脚本:
#!/bin/bash
now=`date +%Y%m%d%H%M`
mkdir 'deployment-'$now
cd 'deployment-'$now
echo 'Please type tag name to deploy from:'
read tag
#svn export svn://------[removed]------
#This checks out a list of SVN urls to deploy.
j=1
for i in `cat deploy-filelist`
do
filename=$i
$j = $j++
svn export $filename
echo $filename | rev | cut -d/ -f1 | rev >> files.txt
done
echo 'loaded' $j 'files'
#svn export svn://------[removed]------
#This checks out the Jar used to deploy the files
files=''
for f in `cat files.txt`
do
filename="${f/\%20/ }"
files+="'$filename' "
done
echo $files >> files.txt
echo "Please type environment to deploy to [devl | test | etc]:"
read env
java -jar deploy.jar -f "$files" -e $env
有人能就处理文件名给我一些建议吗?当相同的格式通常正常时,为什么变量读为一个String?
其他意见得到了回应。
感谢您阅读
答案 0 :(得分:2)
代码的最后一行显示“$ files”,这意味着对字符串执行以下操作:
"'filename1' 'filename2'"
因此,它由JVM读取为一个字符串,并作为单个参数传递。
要解决此问题,请删除$ file周围的引号,如Mubin所暗示的那样。
进一步提示,尝试在Shell的不同阶段回显您的输出,以便您可以看到整个程序中变量发生了什么。在调试Shell时大量帮助。
答案 1 :(得分:2)
"经典"真的不是一个好方法。 Bourne shell引用内部引号的东西并稍后插入它。由于您在shebang线上使用Bash,简单而直接的解决方法是将文件名列表放在数组中。
IFS=$'\n' urls=($(cat deploy-filelist))
for u in "${urls[@]}"; do
svn export "$u"
done
echo "Exported ${#urls[@]} files"
IFS=$'\n' basenames=($(sed 's%.*/%%;s/%20/ /g' deploy-filelist))
请注意这会如何消除您编写和重写files.txt
文件时遇到的一些相当折磨的shell脚本,我认为您不需要其他任何内容(将其作为副产品创建)如果因其他原因需要它,显然是微不足道的。)
(我将数组重命名为urls
和basenames
以保持其自我记录和唯一性。第一个显然包含SVN repo URL,而第二个只包含非目录部分,任意{ {1}}归一化回空间。)
现在您可以插入数组,并保留引号,例如
%20
(这对我来说看起来仍然有点滑稽。java -jar deploy.jar -f "${basenames[@]}" -e $env
选项是布尔值,你提供两个文件名,或者是-f
一个需要文件名参数的选项,你应该有两个他们?)
答案 2 :(得分:1)
问题从这里开始:
`for f in `cat files.txt`
你可能会认为每行会给你一个f
,但不会,每个单词会给你一个f。因此,如果您的文件有空格,它将拆分它们。
您可以将其替换为:
cat files.txt | while read -r
do
f="$REPLY"
# rest of your code here
done
然后仍然在jar调用行中删除双引号。
read命令将从stdin中读取每一行,我们在这里输出文件列表,每行一个,并将其存储在$REPLY
变量中。它不会像for
那样分割单词。
答案 3 :(得分:1)
快速重写,结合了大部分给定的建议,添加了一些我自己的建议:
#!/bin/bash
dir=deployment-$(date +%Y%m%d%H%M)
mkdir "$dir" || { echo "could not mkdir $dir"; exit 1; }
cd "$dir"
read -p 'Please type tag name to deploy from: ' tag
#svn export svn://------[removed]------
#This checks out a list of SVN urls to deploy.
j=1
files=()
while read -r filename; do
if [[ -f "$filename" ]]; then
((j++))
svn export "$filename"
files+=( "$(basename "$filename")" )
else
echo "warning: no such file: $filename"
fi
done < deploy-filelist
echo "loaded $j files"
echo ${files[@]}" >> files.txt
#svn export svn://------[removed]------
#This checks out the Jar used to deploy the files
while :; do
read -p "Please type environment to deploy to [devl | test | etc]: " env
case $env in
devl|test|etc) break ;;
esac
done
java -jar deploy.jar -f "${files[@]}" -e $env