.. index:: single: script (tutorial) single: ECF_HOME (tutorial) .. _tutorial-defining-a-task: Defining the first task ======================= With the :term:`suite definition` ready, the next step is to create the :term:`task` script, also called :term:`ecf script`. This script typically uses extension :code:`.ecf`. Task Script Structure --------------------- A common pattern used for defining an :term:`ecf script` is to consider three sections: 1. A *head* section, that sets up the necessary execution environment, including error handling, for the :term:`task`; and eventually notifies the :term:`ecflow_server` that the :term:`task` has started 2. A *body* section, which contains the actual work to be done by the :term:`task` 3. A *tail* section, that performs the necessary clean up for the :term:`task`; and eventually notifies the :term:`ecflow_server` that the :term:`task` has completed The *head* and *tail* sections are common amongst all :term:`tasks `, while the *body* section is specific to each :term:`task`. To avoid code duplication, ecFlow enables placing the *head* and *tail* sections in separate files, and then include them in the :term:`ecf script` using "C-like" preprocessing :term:`directives`. The process of preprocessing the :term:`ecf script` and including *head* and *tail* files is called the :term:`job file ` generation. Task Head and Tail ------------------ The *head* and *tail* sections are placed in separate files, called include files. These files use :term:`variables ` as a configuration mechanism. The :term:`variables ` are introduced in between %% characters (e.g. :code:`%VARIABLE_NAME%`) and are substituted when the :term:`job file ` is created. These files are introduced into the :term:`ecf script` using the :code:`%include` directive. .. note:: The use of :code:`%include` directives is a generic code reuse mechanism, and can be used to avoid code duplication. Whenever several :term:`tasks ` need to perform the same operation, this can be placed in a separate file, and then included in the :term:`ecf script` using the :code:`%include` directive. .. tabs:: .. tab:: head.h The following :file:`head.h` file, containing the *header* section, performs the following at the start of the :term:`ecf script`: * Setup the environment for communication with the :term:`ecflow_server` * Define script error handling, e.g. when the script fails a trap is raised, and the server is informed that the :term:`task` has :term:`aborted`. * Inform the server that job has started, using the :code:`init` :term:`child command` . .. note:: Notice how variables, such as :code:`ECF_HOST` and :code:`ECF_PORT`, are used in the script. These variables are automatically generated by ecFlow and can be used as part of any section of the :term:`ecf script`. .. code-block:: shell :linenos: :caption: $HOME/course/head.h #!/usr/bin/env bash set -e # stop the shell on first error set -u # fail when using an undefined variable set -x # echo script lines as they are executed # # Important: Adjust the following to point at the ecFlow install location # ECFLOW_ROOT_DIR=/path/to/ecflow/%ECF_VERSION% # Define the variables that are needed for any communication with ECF export ECF_PORT=%ECF_PORT% # The server port number export ECF_HOST=%ECF_HOST% # The name of ecf host that issued this task export ECF_NAME=%ECF_NAME% # The name of this current task export ECF_PASS=%ECF_PASS% # A unique password export ECF_TRYNO=%ECF_TRYNO% # Current try number of the task export ECF_RID=$$ # Define the path where to find ecflow_client # make sure client and server use the *same* version. # Important when there are multiple versions of ecFlow export PATH=${ECFLOW_ROOT_DIR}/bin:$PATH # Tell ecFlow the task has started ecflow_client --init=$$ # Define a error handler ERROR() { set +e # Clear -e flag, so we don't fail wait # wait for background process to stop ecflow_client --abort=trap # Notify ecFlow that something went wrong trap 0 # Remove the trap exit 0 # End the script } # Trap any calls to exit and errors caught by the -e flag trap ERROR 0 # Trap any signal that may cause the script to fail trap '{ echo "Killed by a signal"; ERROR ; }' 1 2 3 4 5 6 7 8 10 12 13 15 .. tab:: tail.h The following :code:`tail.h` file, containing the *tail* section, performs the following at the end of :term:`ecf script`: * Waits for any background processes to complete * Informs the server that the :term:`task` has completed successfully * Cleans up any error handling previously defined .. code-block:: shell :linenos: :caption: $HOME/course/tail.h wait # wait for background process to stop ecflow_client --complete # Notify ecFlow of a normal end trap 0 # Remove all traps exit 0 # End the shell Task Script ----------- The following :file:`t1.ecf` file is the :term:`ecf script` for the :term:`task` :code:`t1`. .. tabs:: .. tab:: t1.ecf This script includes the *head* and *tail* sections from the files :file:`head.h` and :file:`tail.h`, respectively. The *body* section of this script simply prints a message to standard output, including the value of the variable :code:`ECF_HOME`. ecFlow expects task files to be in a directory structure under :code:`ECF_HOME` that reflects the hierarchy in the suite. In the example, task :code:`t1` is part of the suite :code:`test`, so the script for task :code:`t1` is expected to be in sub-directory :code:`test`. .. code-block:: shell :linenos: :caption: $HOME/course/test/t1.ecf %include "../head.h" echo "I am part of a suite that lives in %ECF_HOME%" %include "../tail.h" .. important:: When using the :code:`%include ""` directive, the path to the include file is given relative to the location of the :term:`ecf script` file, thus the :code:`../` in the example above. .. note:: Notice how the :code:`ECF_HOME` variable is used in the script. This variable is defined in the :term:`suite definition` file. **What to do:** * Create the :code:`$HOME/course/head.h` and :code:`$HOME/course/tail.h` include files based on the examples provided above. Make sure to adjust the :code:`ECFLOW_ROOT_DIR` variable in the :file:`head.h` file to point to the correct ecFlow installation location. * Create the directory structure, and the :code:`$HOME/course/test/t1.ecf` task script file based on the example provided above.