Correct way to check Java version from BASH script - java

How can I check whether Java is available (in the PATH or via JAVA_HOME) from a bash script and make sure the version is at least 1.5?

Perhaps something like:
if type -p java; then
echo found java executable in PATH
_java=java
elif [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
echo found java executable in JAVA_HOME
_java="$JAVA_HOME/bin/java"
else
echo "no java"
fi
if [[ "$_java" ]]; then
version=$("$_java" -version 2>&1 | awk -F '"' '/version/ {print $2}')
echo version "$version"
if [[ "$version" > "1.5" ]]; then
echo version is more than 1.5
else
echo version is less than 1.5
fi
fi

You can obtain java version via:
JAVA_VER=$(java -version 2>&1 | sed -n ';s/.* version "\(.*\)\.\(.*\)\..*".*/\1\2/p;')
it will give you 16 for java like 1.6.0_13, 15 for version like 1.5.0_17 and 110 for openjdk 11.0.6 2020-01-14 LTS.
So you can easily compare it in shell:
[ "$JAVA_VER" -ge 15 ] && echo "ok, java is 1.5 or newer" || echo "it's too old..."
UPDATE:
This code should work fine with openjdk and JAVA_TOOL_OPTIONS as mentioned in comments.

The answers above work correctly only for specific Java versions (usually for the ones before Java 9 or for the ones after Java 8.
I wrote a simple one liner that will return an integer for Java versions 6 through 11 (and possibly all future versions, until they change it again!).
It basically drops the "1." at the beginning of the version number, if it exists, and then considers only the first number before the next ".".
java -version 2>&1 | head -1 | cut -d'"' -f2 | sed '/^1\./s///' | cut -d'.' -f1

You can issue java -version and read & parse the output
java -version 2>&1 >/dev/null | grep 'java version' | awk '{print $3}'

I wrote a bash function that should work for JDK 9 and JDK 10.
#!/bin/bash
# returns the JDK version.
# 8 for 1.8.0_nn, 9 for 9-ea etc, and "no_java" for undetected
jdk_version() {
local result
local java_cmd
if [[ -n $(type -p java) ]]
then
java_cmd=java
elif [[ (-n "$JAVA_HOME") && (-x "$JAVA_HOME/bin/java") ]]
then
java_cmd="$JAVA_HOME/bin/java"
fi
local IFS=$'\n'
# remove \r for Cygwin
local lines=$("$java_cmd" -Xms32M -Xmx32M -version 2>&1 | tr '\r' '\n')
if [[ -z $java_cmd ]]
then
result=no_java
else
for line in $lines; do
if [[ (-z $result) && ($line = *"version \""*) ]]
then
local ver=$(echo $line | sed -e 's/.*version "\(.*\)"\(.*\)/\1/; 1q')
# on macOS, sed doesn't support '?'
if [[ $ver = "1."* ]]
then
result=$(echo $ver | sed -e 's/1\.\([0-9]*\)\(.*\)/\1/; 1q')
else
result=$(echo $ver | sed -e 's/\([0-9]*\)\(.*\)/\1/; 1q')
fi
fi
done
fi
echo "$result"
}
v="$(jdk_version)"
echo $v
This returns 8 for Java 8 ("1.8.0_151" etc), and 9 for Java 9 ("9-Debian" etc), which should make it easier to do the further comparison.

You could also compare the jdk class version number with javap:
javap -verbose java.lang.String | grep "major version" | cut -d " " -f5
If its java 8, this will print 52, and 55 for java 11. With this you can make a simple comparison and you don't have to deal with the version string parsing.

Determining the Java version only using grep with Perl-style regex can be done like this:
java -version 2>&1 | grep -oP 'version "?(1\.)?\K\d+'
This will print the major version for Java 9 or higher and the minor version for versions lower than 9, for example 8 for Java 1.8 and 11 for Java 11.0.4.
It can be used like this:
JAVA_MAJOR_VERSION=$(java -version 2>&1 | grep -oP 'version "?(1\.)?\K\d+' || true)
if [[ $JAVA_MAJOR_VERSION -lt 8 ]]; then
echo "Java 8 or higher is required!"
exit 1
fi
The || true was added to prevent the script from aborting in case Java is not installed and the Shell option set -e was enabled.

A combination of different answers:
JAVA_VER=$(java -version 2>&1 | grep -i version | sed 's/.*version ".*\.\(.*\)\..*"/\1/; 1q')
Returns 7 for Java 7 and 8 for Java 8
Works with OpenJDK and with Oracle JDK
Works even if the JAVA_TOOL_OPTIONS is set

in TERMINAL
java -version |& grep 'version' |& awk -F\" '{ split($2,a,"."); print a[1]"."a[2]}'
in BASH
#!/bin/bash
echo `java -version 2>&1 | grep 'version' 2>&1 | awk -F\" '{ split($2,a,"."); print a[1]"."a[2]}'`
jver=`java -version 2>&1 | grep 'version' 2>&1 | awk -F\" '{ split($2,a,"."); print a[1]"."a[2]}'`
if [[ $jver == "1.8" ]]; then
echo $jver is java 8
else
echo $jver is java 11
fi
Works in SUN and Linux

The method I ended up using is:
# Work out the JAVA version we are working with:
JAVA_VER_MAJOR=""
JAVA_VER_MINOR=""
JAVA_VER_BUILD=""
# Based on: http://stackoverflow.com/a/32026447
for token in $(java -version 2>&1 | grep -i version)
do
if [[ $token =~ \"([[:digit:]])\.([[:digit:]])\.(.*)\" ]]
then
JAVA_VER_MAJOR=${BASH_REMATCH[1]}
JAVA_VER_MINOR=${BASH_REMATCH[2]}
JAVA_VER_BUILD=${BASH_REMATCH[3]}
break
fi
done
It will work correctly even if JAVA_TOOL_OPTIONS is set to something due to filtering done by grep.

Another way is:
A file called release is located in $JAVA_HOME.
On Java 15 (Debian) it has as content:
IMPLEMENTOR="Debian"
JAVA_VERSION="15.0.1"
JAVA_VERSION_DATE="2020-10-20"
MODULES="java.base java.compiler java.datatransfer java.xml java.prefs java.desktop java.instrument java.logging java.management java.security.sasl java.naming java.rmi java.management.rmi java.net.http java.scripting java.security.jgss java.transaction.xa java.sql java.sql.rowset java.xml.crypto java.se java.smartcardio jdk.accessibility jdk.internal.vm.ci jdk.management jdk.unsupported jdk.internal.vm.compiler jdk.aot jdk.internal.jvmstat jdk.attach jdk.charsets jdk.compiler jdk.crypto.ec jdk.crypto.cryptoki jdk.dynalink jdk.internal.ed jdk.editpad jdk.hotspot.agent jdk.httpserver jdk.incubator.foreign jdk.internal.opt jdk.jdeps jdk.jlink jdk.incubator.jpackage jdk.internal.le jdk.internal.vm.compiler.management jdk.jartool jdk.javadoc jdk.jcmd jdk.management.agent jdk.jconsole jdk.jdwp.agent jdk.jdi jdk.jfr jdk.jshell jdk.jsobject jdk.jstatd jdk.localedata jdk.management.jfr jdk.naming.dns jdk.naming.rmi jdk.net jdk.nio.mapmode jdk.sctp jdk.security.auth jdk.security.jgss jdk.unsupported.desktop jdk.xml.dom jdk.zipfs"
OS_ARCH="x86_64"
OS_NAME="Linux"
SOURCE=""
So you see JAVA_VERSION, which contains the version number.
For example,
major=$(echo $JAVA_VERSION | cut -d. -f1)
sets major to the value 15 (=Major version number), which you can use further.
It's a really simple solution.

I had a similar problem, I wanted to check which version of java was installed to perform two types of application launches.
The following code has solved my problem
jdk_version() {
local result
local java_cmd
if [[ -n $(type -p java) ]]
then
java_cmd=java
elif [[ (-n "$JAVA_HOME") && (-x "$JAVA_HOME/bin/java") ]]
then
java_cmd="$JAVA_HOME/bin/java"
fi
local IFS=$'\n'
# remove \r for Cygwin
local lines=$("$java_cmd" -Xms32M -Xmx32M -version 2>&1 | tr '\r' '\n')
if [[ -z $java_cmd ]]
then
result=no_java
else
for line in $lines; do
if [[ (-z $result) && ($line = *"version \""*) ]]
then
local ver=$(echo $line | sed -e 's/.*version "\(.*\)"\(.*\)/\1/; 1q')
# on macOS, sed doesn't support '?'
if [[ $ver = "1."* ]]
then
result=$(echo $ver | sed -e 's/1\.\([0-9]*\)\(.*\)/\1/; 1q')
else
result=$(echo $ver | sed -e 's/\([0-9]*\)\(.*\)/\1/; 1q')
fi
fi
done
fi
echo "$result"
}
_java="$(jdk_version)"
echo $_java
if [[ "$_java" > "$8" ]]; then
echo version is more than 8
else
echo version is less than 8
fi
source
I hope to be proved helpful

There is a nice portable bash function for this here: http://eed3si9n.com/detecting-java-version-bash
jdk_version() {
local result
local java_cmd
if [[ -n $(type -p java) ]]
then
java_cmd=java
elif [[ (-n "$JAVA_HOME") && (-x "$JAVA_HOME/bin/java") ]]
then
java_cmd="$JAVA_HOME/bin/java"
fi
local IFS=$'\n'
# remove \r for Cygwin
local lines=$("$java_cmd" -Xms32M -Xmx32M -version 2>&1 | tr '\r' '\n')
if [[ -z $java_cmd ]]
then
result=no_java
else
for line in $lines; do
if [[ (-z $result) && ($line = *"version \""*) ]]
then
local ver=$(echo $line | sed -e 's/.*version "\(.*\)"\(.*\)/\1/; 1q')
# on macOS, sed doesn't support '?'
if [[ $ver = "1."* ]]
then
result=$(echo $ver | sed -e 's/1\.\([0-9]*\)\(.*\)/\1/; 1q')
else
result=$(echo $ver | sed -e 's/\([0-9]*\)\(.*\)/\1/; 1q')
fi
fi
done
fi
echo "$result"
}

Getting java version string, using Bash's built-in parsing capabilities:
#!/usr/bin/env bash
java_cmd=${1:-java}
# Captures version string between double-quotes
IFS='"' read -r _ java_version_string _ < <("${java_cmd}" -version 2>&1)
# Splits version string at dots and (underscore for patch number)
IFS='._' read -r \
java_version_major \
java_version_minor \
java_version_build \
java_version_patch \
<<<"$java_version_string"
# Debug demo output
declare -p java_version_major java_version_minor java_version_build \
java_version_patch
Examples
System default Java 17:
declare -- java_version_major="17"
declare -- java_version_minor="0"
declare -- java_version_build="3"
declare -- java_version_patch=""
Specific Java 8 path provided as argument:
declare -- java_version_major="1"
declare -- java_version_minor="8"
declare -- java_version_build="0"
declare -- java_version_patch="352"

I know this is a very old thread but this will still be of help to others.
To know whether you're running Java 5, 6 or 7, firstly type java -version.
There will be a line in your output that looks similar to this: java version "1.7.0_55"
Then you use this table for converting the 'jargon' result to the version number.
1.7.0_55 is Java 7
1.6.0_75 is Java 6
1.5.0_65 is Java 5
Information taken from a page on the Oracle site
http://www.oracle.com/technetwork/java/javase/7u55-relnotes-2177812.html

Using bashj, an extended bash version (https://sourceforge.net/projects/bashj/), you get a rather compact solution:
#!/usr/bin/bashj
echo System.getProperty("java.runtime.version")
The answer is provided by an integrated JVM (in ~0.017'' execution time for the script , and ~0.004'' for the call itself).

I found the following works rather well. It is a combination of the answers from Michał Šrajer and jmj
version=$(java -version 2>&1 | sed -n ';s/.* version "\(.*\)\.\(.*\)\..*"/\1\2/p;' | awk '{ print $1 }').
I have openJDK 13 installed and without the awk command the first answer printed: 130 2020-01-14. Thus, adding awk '{ print $1 }' provides me with just the version.

Related

Get the Java version in specific format using bash

My Java version is:
openjdk version "1.8.0_312"
OpenJDK Runtime Environment (build 1.8.0_312-b07)
OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)
How can I get the Java version in below format using bash script:
1.8.0
I tried multiple options like java -version 2>&1 | head -n 1 | cut -d'"' -f2, but I'm unable to get the desired output.
To check for a string (your expected version), you can use grep:
if java -version 2>&1 | head -n 1 | grep --fixed-strings '"1.8.0'
then echo expected version
else echo unexpected version
fi
Note the --fixed-strings (short -F), otherwise grep treats . as a RegEx and it will match any character. Thx Gorodon for pointing that out!
I also added the leading " quote so it will not match 11.8.0.
$ IFS='"_' read -r _ ver _ < <(java -version 2>&1)
$ echo "$ver"
1.8.0
You can try this one
java --version | head -n1 |cut -d " " -f1,2
output
openjdk 8.0
You can use grep (or egrep) to print matching patterns:
java --version | head -n 1 | grep -Po '[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}'
java --version | head -n 1 | egrep -o '[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}'

How to access Java property in terminal

I know I can get all the Java System properties from the terminal using
java -XshowSettings:properties -version
How do I access just one specific java system property?
For example, like "user.name"?
I want to do this in the terminal, not with Java.
Solution as a one liner script. Just change the val variable to the key you want to print:
val='java.library.path'; java -XshowSettings:properties -version 2>&1 | sed -re 's/^ +[^=]+ =/_&/' | gawk -v key=$val 'BEGIN{ RS="_"; IFS=" = "} { if($1 ~ key){ print $0 }}'
Details
Some property values like java.library.path contain new lines so we need to mark records before filtering and printing them.
sed allows us to do that, then awk can be used to filter and print.
java -XshowSettings:properties -version 2>&1 |\
sed -re 's/^ +[^=]+ =/_&/' |\
gawk -v key=java.library.path 'BEGIN{ RS="_"; IFS=" = "} { if($1 ~ key){ print $0 }}'
Result:
java.library.path = /usr/java/packages/lib/amd64
/usr/lib64
/lib64
/lib
/usr/lib
Pipeline parts explained:
2>&1: properties are printed to stderr so we need to redirect them to stdin.
sed -re 's/^ +[^=]+ =/_&/' : add an underscore in front of interesting lines, those starting with 4 spaces and containing =.
gawk -v key=java.library.path: set keyawk variable to the selected property key.
'BEGIN{ RS="_"; IFS=" = "}: set record separator to '_' and input field separator IFS to =.
If you need the current logged in user in bash just use whoami command. If you want to get the java property from terminal you can use the following command
java -XshowSettings:properties -version 2>&1 | grep user.name
which will print
$java -XshowSettings:properties -version 2>&1 | grep user.name
user.name = user
If you just need the user name only
java -XshowSettings:properties -version 2>&1 | grep user.name | cut -c 16-100
which will print
$java -XshowSettings:properties -version 2>&1 | grep user.name | cut -c 16-100
user
You can't.
What you can do is create a java file to get the information and run with java, here the documentation.
Since you already said that you don't want this, you can grep (filter) the output of
java -XshowSettings:properties -version 2>&1 | grep java.home
java.home = /usr/java/jdk1.8.0_112/jre
If you want to know the system properties of a running jvm, use the jcmd tool
jcmd PID VM.system_properties

Does java daemonize itself?

I have this code in a java wrapper shell script, written by a third party:
TEMPFILE=$(mktemp java-wrapper-XXXXX)
"$#" | awk -v t=${TEMPFILE} '/unable to fund java heap account/ {print 1 > t} {print}'
RC=$?
if [ 0 -eq ${RC} -o ! -s ${TEMPFILE} ]; then
exit
fi
$# is supposed to hold the java command line, along with the arguments. The logic here is to:
rerun the java command if it fails with heap issues, after invoking another script to defragment memory
do nothing and let the java process run in the background, otherwise
In "$#" | awk ..., java is being called in the foreground. Does java daemonize itself if it bootstraps successfully? That doesn't seem to be making sense to me. Unless there is a memory issue, this code would lead to java process running normally with its output being piped to awk, isn't it?
Please help me understand this. I welcome any suggestions to improve the logic. Please ignore the uppercase variables and other issues that can be found through shellcheck.
Here is the complete script:
#!/bin/bash
# Java Wrapper takes java command as input
# and runs java. If java fails due to zing memory
# it attempts to run get2Mpages.sh (az_fragger binary) for
# the given Xmx and them runs java again
set -o pipefail
BASE=$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)
NAME=$(basename "${BASH_SOURCE[0]}")
GET_2MPAGES=${BASE}/get2Mpages.sh
fail() {
echo "ERROR: $#" >&2
echo "Usage: $NAME java [<args>...] -XmxNNN [<args>...]" >&2
exit 1
}
[ $# -gt 0 ] || fail "No command specified"
# extract the Xmx value
XMX=$(echo "$#" | sed -n 's/.*-Xmx\([0-9]*.\).*/\1/p')
[ -n "${XMX}" ] || fail "Unable to extract Xmx argument from the command-line"
trap on_exit SIGTERM SIGQUIT EXIT
on_exit() {
rm -f "${TEMPFILE}"
exit ${RC}
}
TEMPFILE=$(mktemp java-wrapper-XXXXX)
"$#" | awk -v t=${TEMPFILE} '/unable to fund java heap account/ {print 1 > t} {print}'
RC=$?
if [ 0 -eq ${RC} -o ! -s ${TEMPFILE} ]; then
exit
fi
# OOM Detected
cat << EOF >&2
Info: Failed to run JAVA due to insufficient 2MB pages
Info: Now running $GET_2MPAGES ${XMX}
EOF
${GET_2MPAGES} ${XMX} && {
echo "Info: attempting to run JAVA again" >&2
echo
"$#"
}
RC=$?

Generate JNI header files for class files in JDK 10

An integral part of the Java Native Interface (JNI), is the bridging of JVM code and native code through C headers. The way to generate these header files used to be quite straight forward: simply call the command line utility javah on class files. This process would then generate prototypes for any method marked with the native modifier.
As of Java 10 however, the javah utility has been removed, and its suggested replacement is a new flag "-h" to javac. The replacement works fine if one has the Java source files available, however falls short in cases where only compiled class files are available. (The issue that sparked this question is that I'm trying to generate JNI bindings from Scala sources. My current approach has been to compile them first and then run javah over the resulting class files.)
In a situation where only compiled class files are available, is there a way to generate C header files, similar to the way javah used to?
You can always go via javap. I know, I know. It's ugly, has lots of assumptions, but in case you desperately need to generate headers for lots of files it might be the only option.
#!/bin/bash
# FIRST_ARG - full class name (with package)
# SECOND_ARG - class path
CLASS_NAME=`javap -cp $2 $1 | \
grep -v "Compiled from" | \
grep "public class" | \
cut -f3 -d" " | \
awk -F"." '{ print $NF }'`
PACKAGE_NAME=`javap -cp $2 $1 | \
grep -v "Compiled from" | \
grep "public class" | \
cut -f3 -d" " | \
sed s/\.${CLASS_NAME}$//`
DIR_NAME=`echo $PACKAGE_NAME | sed 's|\.|/|g'`
mkdir -p java_jni/${DIR_NAME}
JAVA_FILE_NAME="java_jni/${DIR_NAME}/${CLASS_NAME}.java"
echo "package ${PACKAGE_NAME};" > ${JAVA_FILE_NAME}
echo "public class ${CLASS_NAME} {" >> ${JAVA_FILE_NAME}
javap -cp $2 $1 | grep "native" | while read line; do
param=0
comma=`echo $line | grep "," | wc -l`
while [ $comma -gt 0 ]; do
line=`echo $line | sed "s/,/ param_${param}|/"`
let param=param+1
comma=`echo $line | grep "," | wc -l`
done
line=`echo $line | sed "s/)/ param_${param})/" | sed 's/|/,/g'`
echo " $line" >> ${JAVA_FILE_NAME}
done
echo "}" >> ${JAVA_FILE_NAME}
mkdir -p c_header
javac -h c_header ${JAVA_FILE_NAME}
I bet it can be made way more beautiful.
For me, now, when I slowly start to think about inevitable move towards Java 10, and all these cases, where I might be surprised by non existing Java source code, I think it's not a bad idea to have some tool at my disposal. Just in case.
We can use gjavah to generate JNI header files.
The best solution is just install a jdk8, i think.
And no need to uninstall jdk10, just modify the environment variable.

Running Different Bash Commands Based on Java Version

I'm trying to develop a bash build script for a Java project that will be run on Ubuntu and Fedora. Ubuntu uses the gcj compiler while Fedora uses IcedTea.
Both report their errors and warning in slightly different ways, and I want to ignore the warnings (I know, not generally a good idea, but some of the warnings are simply idiotic).
For gcj, I want to run:
javac *.java 2>&1 | grep -A 4 "error:"
but for IcedTea, I want to run:
javac *.java 2>&1 | grep -A 4 "error:\|errors\|.java:"
I'm still new to bash, so how would I write an if statement that would run one versus the other based upon the javac version?
Assuming your java and javac binaries match, and that icedtea is the special case.
#!/bin/bash
ERROR="error:"
java -version 2>&1 | grep -i icedtea > /dev/null
if [ $? -eq 0 ]; then
ERROR="error:\|errors\|.java:"
fi
javac *.java 2>&1 | grep -A 4 $ERROR
On my system, icedtea and sun have the same output for "javac -version", but not for "java -version".
Writing Java build scripts in bash (or any other shell language) has a number of problems:
scripts tend to be non-portable due to shell differences, different command locations, incompatible command options and so on ... even if you try to make the portable.
scripts cannot cope with dependencies (or at least not easily)
scripts cannot cope with recompiling only stuff that has changed
Instead, I suggest that you write a "build.xml" file and use the Ant build tool. Ant has the advantage of running on any build platform that runs Java, and of taking care of the vast majority of platform differences. It is sort of like a better "Make" designed specifically for building Java.
#!/bin/sh
JAVAC_VERSION="`java -version 2>&1 /dev/null | awk '/IcedTea/ {print $4}' | sed -e 's/[\(0-9]//g'`"
ICEDTEA="IcedTea"
if [ ${JAVAC_VERSION} = ${ICEDTEA} ]; then
javac *.java 2>&1 | grep -A 4 "error:\|errors\|.java:"
else
javac *.java 2>&1 | grep -A 4 "error:"
fi
exit 0
That should do it - if i understood your question correctly. How you get the version - im not quite sure of, but if my javac -version is incorrect just change it accordingly to your needs.

Categories