Transform lines of a file into a list of items

Viewed 92

I have to deal with a given file that contains crontab directives:

##Batch IdRef2Virtuoso
*/1 * * * * /home/batch/autorites/current/bin/exportAutorites2TS.sh > /dev/null 2>&1
*/1 * * * * /home/batch/autorites/current/bin/exportBiblio2TS.sh > /dev/null 2>&1

I want to cut it so as to get a yaml file with a list of items and then proceed with ansible. I'm able to do such a thing with regular awk:

 while read -r line; do printf '%s\n' "$line"| awk '{ split($0, ip, /'\ '/); printf("- title1: \"%s\"\n  title2: \"%s\"\n  minute: \"%s\"\n  hour: \"%s\"\n  day_month: \"%s\"\n  month: \"%s\"\n  day_week: \"%s\"\n  day: \"%s\"\n", ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8]);}'; done <"/tmp/crontab-DEV.txt"

Results in:

{
    "msg": [
        {
            "day_month": "*",
            "day_week": "*",
            "hour": "*",
            "job": "/home/batch/autorites/current/bin/exportAutorites2TS.sh",
            "minute": "*/1",
            "month": "*"
        },
        {
            "day_month": "*",
            "day_week": "*",
            "hour": "*",
            "job": "/home/batch/autorites/current/bin/exportBiblio2TS.sh",
            "minute": "*/1",
            "month": "*"
        }
    ]
}

It works, but it is not very "ansible", how can I obtain the same result?

2 Answers

Given the file

shell> cat crontab 
*/1 * * * * /home/batch/autorites/current/bin/exportAutorites2TS.sh > /dev/null 2>&1
*/1 * * * * /home/batch/autorites/current/bin/exportBiblio2TS.sh > /dev/null 2>&1

the tasks below

    - command: cat crontab
      register: result
    - set_fact:
        cron_conf: "{{ cron_conf|default([]) + [dict(keys|zip(vals[0:6]))] }}"
      loop: "{{ result.stdout_lines }}"
      vars:
        vals: "{{ item.split() }}"
        keys: [minute, hour, day_month, month, day_week, job]

create the list

  cron_conf:
  - day_month: '*'
    day_week: '*'
    hour: '*'
    job: /home/batch/autorites/current/bin/exportAutorites2TS.sh
    minute: '*/1'
    month: '*'
  - day_month: '*'
    day_week: '*'
    hour: '*'
    job: /home/batch/autorites/current/bin/exportBiblio2TS.sh
    minute: '*/1'
    month: '*'

Update 09/2022

You can use the filter community.general.dict and avoid the iteration. For example, the declarations below give the same result.

cron_keys: [minute, hour, day_month, month, day_week, job]
cron_conf: "{{ result.stdout_lines|
               map('split')|
               map('zip', cron_keys)|
               map('map', 'reverse')|
               map('community.general.dict') }}"

It is kind of a naive approach I am going to propose here, as it does not cover cases like:

  • There is a comment line with 6 words or more, e.g. # some comment here with six words
  • The job is actually a more complex job than an expression in "one word" (/path/to/bin), e.g. service httpd restart
  • And possibly other quirks that do not come directly in my mind

All this said, what you can do is to use a combination of:

  • the file lookup to get the content of the file
  • Python method str.splitline() to split the content of the file per line
  • Python method str.split() to split the line in the different tokens
  • then use vars, on a task level to transform the string in a dictionary

Given the playbook:

- hosts: all
  gather_facts: no

  tasks:
    - set_fact: 
        cron_expressions: "{{ cron_expressions + [cron_expression] }}"
      loop: "{{ lookup('file', '/tmp/crontab-DEV.txt').splitlines() }}"
      when: cron_line[5] is defined
      vars:
        cron_expressions: []
        cron_line: "{{ item.split() }}"
        cron_expression: 
          minute: "{{ cron_line[0] }}"
          hour: "{{ cron_line[1] }}"
          day_month: "{{ cron_line[2] }}"
          month: "{{ cron_line[3] }}"
          day_week: "{{ cron_line[4] }}"
          job: "{{ cron_line[5] }}" 
          # job: "{{ cron_line[5:] | join('') }}" 
          # ^-- could be an alternative to recover some possible data loss
          # in the job, using an array slice

    - debug:
        var: cron_expressions

This yields the recap:


PLAY [all] **********************************************************************************************************

TASK [set_fact] *****************************************************************************************************
skipping: [localhost] => (item=## Batch IdRef2Virtuoso) 
ok: [localhost] => (item=*/1 * * * * /home/batch/autorites/current/bin/exportAutorites2TS.sh > /dev/null 2>&1)
ok: [localhost] => (item=*/1 * * * * /home/batch/autorites/current/bin/exportBiblio2TS.sh > /dev/null 2>&1)

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "cron_expressions": [
        {
            "day_month": "*",
            "day_week": "*",
            "hour": "*",
            "job": "/home/batch/autorites/current/bin/exportAutorites2TS.sh",
            "minute": "*/1",
            "month": "*"
        },
        {
            "day_month": "*",
            "day_week": "*",
            "hour": "*",
            "job": "/home/batch/autorites/current/bin/exportBiblio2TS.sh",
            "minute": "*/1",
            "month": "*"
        }
    ]
}

PLAY RECAP **********************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
Related