Java archives (*.jar
files) that have set the main class can be started using java -jar program.jar
command.
Let us find all JARs under a certain directory that are runnable.
#!/bin/bash
findFiles() {
find ~/.m2/ -iname '*.jar' -printf '%p\0';
}
fetchAttributes() {
relpipe-in-filesystem \
--parallel 4 \
--file path \
--streamlet jar_info;
}
filterRunable() {
relpipe-tr-awk --relation '.*' --where 'main_class';
}
shortenPath() {
relpipe-tr-sed --relation '.*' --attribute 'path' --value "^$HOME/?" --replacement "~/";
}
findFiles | fetchAttributes | filterRunable | shortenPath | relpipe-out-tabular
The script above will print output like this one:
filesystem:
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────┬───────────────────╮
│ path (string) │ main_class (string) │ entries (integer) │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────┼───────────────────┤
│ ~/.m2/repository/com/example/demo1/0.0.1-SNAPSHOT/demo1-0.0.1-SNAPSHOT.jar │ org.springframework.boot.loader.JarLauncher │ 116 │
│ ~/.m2/repository/net/java/dev/jna/jna/5.2.0/jna-5.2.0.jar │ com.sun.jna.Native │ 165 │
│ ~/.m2/repository/org/sonatype/sisu/sisu-inject-bean/1.4.2/sisu-inject-bean-1.4.2.jar │ org.sonatype.guice.bean.containers.Main │ 165 │
│ ~/.m2/repository/org/apache/maven/surefire/surefire-grouper/2.12.4/surefire-grouper-2.12.4.jar │ org.apache.maven.surefire.group.parse.GroupMatcherParser │ 34 │
│ ~/.m2/repository/org/apache/commons/commons-compress/1.11/commons-compress-1.11.jar │ org.apache.commons.compress.archivers.Lister │ 267 │
│ ~/.m2/repository/org/apache/commons/commons-compress/1.14/commons-compress-1.14.jar │ org.apache.commons.compress.archivers.Lister │ 328 │
│ ~/.m2/repository/org/apache/commons/commons-compress/1.18/commons-compress-1.18.jar │ org.apache.commons.compress.archivers.Lister │ 377 │
│ ~/.m2/repository/org/apache/commons/commons-compress/1.16.1/commons-compress-1.16.1.jar │ org.apache.commons.compress.archivers.Lister │ 350 │
│ ~/.m2/repository/org/apache/ant/ant-launcher/1.7.0/ant-launcher-1.7.0.jar │ org.apache.tools.ant.launch.Launcher │ 12 │
│ ~/.m2/repository/org/apache/ant/ant/1.7.0/ant-1.7.0.jar │ org.apache.tools.ant.Main │ 801 │
│ ~/.m2/repository/org/eclipse/sisu/org.eclipse.sisu.inject/0.0.0.M5/org.eclipse.sisu.inject-0.0.0.M5.jar │ org.eclipse.sisu.launch.Main │ 236 │
│ ~/.m2/repository/org/projectlombok/lombok/1.16.22/lombok-1.16.22.jar │ lombok.launch.Main │ 932 │
│ ~/.m2/repository/org/beanshell/bsh/2.0b4/bsh-2.0b4.jar │ bsh.Console │ 238 │
│ ~/.m2/repository/cz/frantovo/PigLatin/1.0-SNAPSHOT/PigLatin-1.0-SNAPSHOT.jar │ cz.frantovo.piglatin.CLIStarter │ 14 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────┴───────────────────╯
Record count: 14
This pipeline consists of five steps:
findFiles
– prepares the list of files separated by \0
byte;
if we omit the -iname '*.jar'
, the result will be the same,
just more files will be examinated
fetchAttributes
– does the heavy work – tries to open each given file as a JAR (same as ZIP format)
and looks for the Main-Class
field in the META-INF/MANIFEST.MF
file (if any);
because the jar_info
streamlet itself is written in Java, it simply uses existing Java functionality for main class lookup instead of reimplementing it in custom code;
thanks to --parallel N
option, utilizes N cores of our CPU;
we can experiment with the N value and look how the total time decreases
filterRunable
– uses AWK to skip the records (files) that does not have a main class;
in this step we could use also relpipe-tr-sql
or relpipe-tr-scheme
if we prefer SQL or Scheme to AWK
shortenPath
– replaces part of the absolute path with the ~
shortcut
(just to make it shorter and hide our username)
relpipe-out-tabular
– formats the results as a table in the terminal (we could use e.g. relpipe-out-gui
to call a GUI viewer or format the results as XML, CSV or other format)
We can omit the -iname '*.jar'
and run this pipeline on another directory
in order to find all valid JAR and ZIP files regardless their extension.
We will get also the number of entries (files and directories) in each archive.
In future versions, this streamlet might be extended to optionally provide files from the archive or their list e.g. in form of XML.
Relational pipes, open standard and free software © 2018-2022 GlobalCode