Manifest-Version: 1.0
Created-By: 1.6.0_29 (Sun Microsystems Inc.)
Class-Path: spring-aop-3.2.0.jar spring-aspects-3.2.0.jar spring-beans
-3.2.0.jar spring-context-3.2.0.jar spring-context-support-3.2.0.jar
spring-core-3.2.0.jar spring-expression-3.2.0.jar spring-instrument-3
.2.0.jar spring-instrument-tomcat-3.2.0.jar spring-jdbc-3.2.0.jar spr
ing-jms-3.2.0.jar spring-orm-3.2.0.jar spring-oxm-3.2.0.jar spring-st
ruts-3.2.0.jar spring-test-3.2.0.jar spring-tx-3.2.0.jar spring-web-3
.2.0.jar spring-webmvc-3.2.0.jar spring-webmvc-portlet-3.2.0.jar
在清单中搜索jar很麻烦:
从jar中提取
META-INF/MANIFEST.MF
文件重新连接分割线以进行搜索人道化的操作
由于断行,因此必须执行步骤2。例如,在上面我找不到“ struts”,因为这个单词在中间被打断了。
我使用此shell脚本使此过程更容易,我称之为
jar-manifest-classpath.sh
: #!/bin/bash
if test -d "$TMPDIR"; then
:
elif test -d "$TMP"; then
TMPDIR=$TMP
elif test -d /var/tmp; then
TMPDIR=/var/tmp
else
TMPDIR=/tmp
fi
workdir=$TMPDIR/$(basename "spring-aop-3.2.0.jar
spring-aspects-3.2.0.jar
spring-beans-3.2.0.jar
spring-context-3.2.0.jar
spring-context-support-3.2.0.jar
spring-core-3.2.0.jar
spring-expression-3.2.0.jar
spring-instrument-3.2.0.jar
spring-instrument-tomcat-3.2.0.jar
spring-jdbc-3.2.0.jar
spring-jms-3.2.0.jar
spring-orm-3.2.0.jar
spring-oxm-3.2.0.jar
spring-struts-3.2.0.jar
spring-test-3.2.0.jar
spring-tx-3.2.0.jar
spring-web-3.2.0.jar
spring-webmvc-3.2.0.jar
spring-webmvc-portlet-3.2.0.jar
")-work-$$
cleanup() {
rm -fr "$workdir"
}
mkdir -p "$workdir"
trap 'cleanup' 1 2 3 15
for jar; do
if ! test -f $jar; then
echo warning: not a file: $jar
continue
fi
[[ $jar = /* ]] || jar=$PWD/$jar
(
cd "$workdir" || exit 1
jar xf "$jar"
sed -ne '/^Class-Path:/,$p' META-INF/MANIFEST.MF | sed -e 's/^Class-Path: //' -e 's/^ //' | tr -d '\n' | tr ' ' '\n'
)
done
cleanup
对于具有上述清单的jar文件,这将输出:
grep
这更令人高兴,并且可以完美地实现。
让我知道您是否有任何想法可以改进或简化!
(脚本是我收藏的一部分GitHub)
#1 楼
在完成需要清除的操作之前,我将创建清除陷阱,因此请更改命令顺序。我也希望信号名称而不是数字。如果将EXIT添加到信号列表中,则退出shell时将调用清除操作,因此您可以删除脚本的最后一行(cleanup
)trap 'cleanup' EXIT SIGHUP SIGINT SIGQUIT SIGTERM
mkdir -p "$workdir"
如果调用脚本时不带参数,或者使用
-?
显示使用情况消息。警告和错误消息应写入stderr(2)而不是stdout(1)。这是程序的Unix标准行为。否则,如果通过grep
命令通过管道传输输出,则不会看到错误消息。评论
\ $ \ begingroup \ $
优点,谢谢!希望您能更多地参加Code Review!
\ $ \ endgroup \ $
– janos
15年2月19日在12:23
#2 楼
您正在努力创建一个临时文件夹,但您应该使用正确的工具:mktemp
workdir=$(mktemp -d)
这是一种安全的创建方式
请注意,
mktemp
使用$TMPDIR
中的值(如果已设置)作为创建文件夹的位置,因此它将使用之前在代码中设置的任何值正在运行mktemp
评论
\ $ \ begingroup \ $
+1 mktemp是正确的工具,但是$ TMPDIR是默认的工具。所以应该使用mktemp -d
\ $ \ endgroup \ $
– Miracle173
2014年12月18日在18:33
\ $ \ begingroup \ $
如果没有-t mktemp,则不会使用$ TMPDIR吗?
\ $ \ endgroup \ $
– Etan Reisner
2014年12月19日,0:35
\ $ \ begingroup \ $
@EtanReisner-手册页上说:“如果未指定TEMPLATE,则使用tmp.XXXXXXXXXX,并暗示--tmpdir。”
\ $ \ endgroup \ $
–rolfl
2014-12-19在0:42
\ $ \ begingroup \ $
也许对于GNU / coreutils mktemp。不适用于BSD mktemp(至少根据我看到的文档)。
\ $ \ endgroup \ $
– Etan Reisner
2014-12-19在2:38
#3 楼
从提取和文本处理方面的困难可以证明,Bash可能不是工作的最佳工具。尽管jar
命令不支持将内容提取到标准输出,但是Java确实具有对清单文件解析的内置支持。考虑编写最小的外壳包装程序来调用Java程序: public static void main(String[] args) throws IOException {
Manifest mf = (args.length > 0) ? (new JarFile(args[0])).getManifest()
: (new JarInputStream(System.in)).getManifest();
String classPath = mf.getMainAttributes().getValue("Class-Path");
if (classPath != null) {
for (String dependency : classPath.split(" ")) {
System.out.println(dependency);
}
}
}
评论
\ $ \ begingroup \ $
这里引用了使用这种通用方法的相关示例。
\ $ \ endgroup \ $
–垃圾神
2014年12月20日在3:43
#4 楼
使用Jar文件时,请记住它们只是具有某些限制的zip文件。因此,无需遍历整个extract-to-temp文件夹,而您可以“简单路径”:
unzip -q -c $jar META-INF/MANIFEST.MF
这将从zip存档中仅提取MANIFEST.MF文件,并将其输出到标准输出。
因此,您可以完全跳过整个temp-dir过程。
#5 楼
尽管此脚本在给定的示例和一般的类路径jar(仅包含清单且不包含类的jar)中都可以正常工作,但对于包含类的jar来说效率很低。
罪魁祸首是步骤提取jar的文件:
[[ $jar = /* ]] || jar=$PWD/$jar
(
cd "$workdir" || exit 1
jar xf "$jar"
^^^^ not so good
这样的
jar xf
步骤将提取整个jar
文件,当清单文件时这样就足够了: jar xf "$jar" META-INF/MANIFEST.MF
还有另一个晦涩的错误:
尽管在示例中
Class-Path
字段是最后一个,不能保证那样。
例如,这是一个完全有效的清单,
,某些实现可能以这种方式生成清单:
Manifest-Version: 1.0
Class-Path: spring-aop-3.2.0.jar spring-aspects-3.2.0.jar spring-beans
-3.2.0.jar spring-context-3.2.0.jar spring-context-support-3.2.0.jar
spring-core-3.2.0.jar spring-expression-3.2.0.jar
Created-By: 1.6.0_29 (Sun Microsystems Inc.)
问题中的
sed
脚本无法很好地处理这种情况,Created-By
行的内容将最终出现在输出中。尽管这不会破坏您在类路径中命名的能力,
添加的行是垃圾,很明显不是预期的。
而不是乏味的倍数
grep
,临时目录:for TMPDIR in "$TMPDIR" "$TMP" /var/tmp /tmp; do
test -d "$TMPDIR" && break
done
(此技术在我其他相关问题的另一个答案中,由@frostschutz提出)
#6 楼
我找到这行:sed -ne '/^Class-Path:/,$p' META-INF/MANIFEST.MF | sed -e 's/^Class-Path: //' -e 's/^ //' | tr -d '\n' | tr ' ' '\n'
令人难以理解的阅读。当然,必须有一种更简单/更轻松的方式来做到这一点吗?我不懂bash,但无论如何它会跳出代码气味...
#7 楼
JAR提取很多麻烦来自处理临时文件。您必须找到一个临时目录(应该使用
mktemp(1)
来完成),然后即使在发生故障的情况下也可以清理混乱。您只需要处理
META-INF/MANIFEST.MF
的内容。理想情况下,您应该将文本提取到标准输出中,以便通过管道进行进一步处理。不幸的是,Java的jar
命令没有这种能力。幸运的是,JAR文件规范说该格式基于流行的ZIP文件格式。因此,可以使用不支持
unzip(1)
选项的-c
提取到标准输出。文本处理
您的代码在
Class-Path:
之后打印每个单词,直到文件末尾。如果在MANIFEST.MF
行之后的其他条目出现在Class-Path
中,它们的内容也将被打印!我建议编写一个简短的AWK脚本来展开连续行。然后找到
Class-Path
条目,将其每个单词分成一行,然后省略第一行。您可以尝试在一个AWK脚本中完成所有操作,但是我建议您将关注点分开。建议的解决方案
unzip -q -c "$jar" META-INF/MANIFEST.MF |
# Unfold continuation lines
# (This prepends an empty line, but that won't matter)
awk '/^[^ ]/ { print BUF; BUF=q4312078q }
END { print BUF }
/^ / { sub(" *", ""); BUF = BUF q4312078q }' |
# Get Class-Path entry
grep '^Class-Path: ' |
# Split into one line per word
tr ' ' '\n' |
# Omit the "Class-Path:" entry name itself
tail +2
评论
在Java中执行此操作会容易得多,因为标准库已经具有用于处理JAR清单的类。