0

I am trying to write a script to fetch the java version which is installed in a specific location.

When I use the below task with the full path, the returned output is empty. When I give the shell command as java -version without the path, it works fine.

- name: Fetch Java version from location
  shell: /usr/java/jdk-1.8/bin/java -version 2>&1 | awk -F '"' '/version/ {print $2}'
  register: java_version_from_path
      
- name: Display the java version from the java location
  debug:
    msg: "{{ java_version_from_path.stdout }}"
Zeitounator
  • 38,476
  • 7
  • 53
  • 66
JCDani
  • 307
  • 7
  • 20
  • Try to escape the `#2` - the hash sign starts a comment, which might not be what you want to achieve? – Nico Haase Jun 01 '23 at 11:21
  • @NicoHaase Modified the code , sorry was a typo – JCDani Jun 01 '23 at 11:22
  • 1
    What do you mean "doesn't work well"? Add an explicit description of what goes wrong (exactly how that symptom manifests in practice) to the text of the question. – Charles Duffy Jun 01 '23 at 11:48
  • 1
    You might also consider storing the output of _just_ `/usr/java/jdk-1.8/bin/java -version` and deferring the postprocessing -- that way you can see what awk is getting as input, and what exit status is being returned. (As it is you'd need to enable `pipefail` to make the larger shell command reflect a failure from `/usr/java/jdk-1.8/bin/java`) – Charles Duffy Jun 01 '23 at 11:49
  • 2
    BTW, silly question, but how certain are you that the path in question is actually _correct_ on the operating system your playbook is running against? – Charles Duffy Jun 01 '23 at 11:51
  • Does this answer your question? [Whats the best way in Ansible to check if a command is available?](https://stackoverflow.com/questions/70808742/whats-the-best-way-in-ansible-to-check-if-a-command-is-available) – U880D Jun 01 '23 at 12:43
  • I don't know ansible, but could it be that `{{....}}` is catching only the standard output of a command? – user1934428 Jun 01 '23 at 14:02
  • 1
    @user1934428 you can explore `{{ java_version_from_path.stderr }}` if you feel there's an error. Meanwhile, your real problem lies in pipes in a shell task and the last command being successful despite the first one being in error. See @larsk answer below. – Zeitounator Jun 01 '23 at 22:07
  • Much Appreciate your answers and effort in helping me with this... I just had to run the task as a root user , I had added 'become: yes' as the path owner is oracle. – JCDani Jun 05 '23 at 06:32

1 Answers1

2

Your code works for me as written, but while testing it I mis-typed the path and got the same behavior as you.

Because you're using a shell task with a pipe, if the first command in the pipe fails the error is effectively suppressed. Consider this:

$ false | echo hello
hello
$ echo $?
0

The first command fails, but there's no way to tell. That's exactly the behavior I was seeing running the playbook locally, and it may be the cause of your problem.

If you copy and paste the full command line into your shell and run it, does it work? What if you just copy and paste the ../java -version part?


For this sort of task it's often simpler to do the text parsing in ansible rather than using awk; something like this:

- hosts: localhost
  gather_facts: false
  tasks:
  - name: Fetch Java version from location
    command: /usr/java/jdk-17.0.2/bin/java -version
    register: java_version_from_path

  - name: Display the java version from the java location
    debug:
      msg: >-
        {{
          (
            java_version_from_path.stderr_lines |
            select('search', 'version') |
            first
          ).split('"')[1]
        }}

On my system, this produces:

TASK [Fetch Java version from location] *****************************************************************
changed: [localhost]

TASK [Display the java version from the java location] **************************************************
ok: [localhost] => {
    "msg": "17.0.2"
}

By performing the text parsing in Ansible, we can use a command task, rather than shell, and without that shell pipeline errors in the java command will propagate to the playbook and we'll see the failure:

TASK [Fetch Java version from location] *****************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "cmd": "/usr/java/jdk-1.8/bin/java -version", "msg": "[Errno 2] No such file or directory: b'/usr/java/jdk-1.8/bin/java'", "rc": 2, "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
larsks
  • 277,717
  • 41
  • 399
  • 399
  • 2
    When pipes in shell are not avoidable, a possible (yet non universal) workaround to capture failures is to switch the shell to bash and start the script with `set -o pipefail`. This way the shell will return an error whatever command fails in the pipe. – Zeitounator Jun 01 '23 at 13:46
  • ... Though that has some side effects which you probably want to understand (some commands emit error codes in scenarios where perhaps you would like for them to just "work"); replacing non-obvious shell pipelines with logic in your native host language is often a better solution because then it's obvious to anyone who understands the host language what is actually being done, and also you have more control over error cases etc. – tripleee Jun 02 '23 at 05:01