Git Product home page Git Product logo

esh's Introduction

ESH – Embedded SHell

Build Status

esh (embedded shell) is a templating engine for evaluating shell commands embedded in arbitrary templates. It’s like ERB (Embedded RuBy) for shell, intended to be used for templating configuration files. Unlike ERB it provides support for including one ESH template into another (since version 0.2.0).

The template processing consists of two phases: conversion to a shell script and evaluation of that script. You can even run just the conversion phase: esh -d will dump a shell script that you can execute directly by a shell (even on a system without esh installed). However, in that case, you will not get error messages source-mapped to point to the locations in the template file.

esh is implemented in ~290 lines (LoC) of shell and awk.

Requirements

  • POSIX-sh compatible shell (e.g. Busybox ash, dash, ZSH, bash, …)

  • common Unix userland with awk and sed (e.g. from Busybox, GNU, …)

  • (Asciidoctor to build a man page)

Installation

Packaging status

Alpine Linux

Install package esh from the Alpine’s community repository:

apk add esh

Fedora

dnf install esh

Void Linux

xbps-install esh

On CI

You can add esh as a submodule into your repository or download it on demand, e.g.:

wget https://raw.githubusercontent.com/jirutka/esh/v0.3.2/esh \
    && echo '9084e3e8e70e4ea81c40cd1cf85559196c0fa2cc  esh' | sha1sum -c \
    || exit 1

Manually

  1. Download and unpack release tarball:

    wget https://github.com/jirutka/esh/archive/v0.3.2/esh-0.3.2.tar.gz
    tar -xzf esh-0.3.2.tar.gz
    cd esh-0.3.2
  2. Run tests:

    make test
  3. Build a man page and install esh:

    make install prefix=/usr/local DESTDIR=/

    or just copy esh whether you want, it’s a plain shell script.

Usage

Read man page esh(1).

Examples

http {
    access_log <%= $logs_dir/access.log %> main;

    resolver <%= $(sed -En 's/^nameserver ([^#]+)/\1/p' /etc/resolv.conf) %>;

    <% if nginx -V 2>&1 | grep -q lua-nginx-module; then -%>
    lua_package_path '<%= $(pkg-config --variable=INSTALL_LMOD lua) %>/?.lua';
    <% fi -%>

    <%+ ./http-common.esh %>

    <%# The rest of the config is omitted %>
}

Credits

ESH template syntax is based on ERB (Embedded Ruby).

Tests and some concepts are inspired by shellcat.

License

This project is licensed under MIT License. For the full text of the license, see the LICENSE file.

esh's People

Contributors

jirutka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

esh's Issues

Conditional

From the examples I expect this to work:

<% if [ $TEST ]; then %>
This should be printed when TEST is not empty.
<% fi %>

But it doesn't. Am I doing something wrong?

feature request: include once

While it should be possible to implement the logic for include-once with the current functionality,
I'm wondering if you have thought of having that functionality built-in and if you'd accept a PR.

Thanks for creating esh btw ❗

Loops

Hi and thank you for this project that I've been using a bit recently !

Does it support loops ?

Thanks in advance

Question/idea: escaping long or special arguments

Firstly, thanks for sharing this tool, I'm use ESH to generate SQL scripts from a template and it just works great :)

This is the shell script I'm using:

#!/bin/sh

ESH_VERSION=0.1.1
ESH_FILE_NAME=esh-${ESH_VERSION}
ESH_DOWNLOAD_FILE_NAME=${ESH_FILE_NAME}.tar.gz
ESH_DOWNLOAD_BASE_DIR=/tmp
ESH_DOWNLOAD_DIR=/tmp/${ESH_FILE_NAME}
ESH_DOWNLOAD_PATH=${ESH_DOWNLOAD_BASE_DIR}/${ESH_DOWNLOAD_FILE_NAME}
ESH_BIN=${ESH_DOWNLOAD_BASE_DIR}/${ESH_FILE_NAME}/esh

usage () { echo "Script usage: $(basename $0) -p prefix -c collection_name"; }

while getopts ':p:c:h' OPTION; do
  case "$OPTION" in
    p) PREFIX="$OPTARG";;
    c) COLLECTION="$OPTARG";;
    h) usage; >&2 exit 1;;
    :) echo "Invalid option: $OPTARG requires an argument" 1>&2;;
    ?) usage; >&2 exit 1;;
  esac
done
shift "$(($OPTIND -1))"

# Download ESH if it wasn't downloaded before
if [ ! -d "$ESH_DOWNLOAD_DIR" ]; then
  wget https://github.com/jirutka/esh/archive/v${ESH_VERSION}/${ESH_DOWNLOAD_FILE_NAME} -O ${ESH_DOWNLOAD_PATH}
  tar -xzf ${ESH_DOWNLOAD_PATH} -C ${ESH_DOWNLOAD_BASE_DIR}
fi

printf "/*\n"
printf "+------------------------------------------+\n"
printf "            Scripts start here\n"
printf "+------------------------------------------+\n"
printf "*/\n"
printf "\n"

${ESH_BIN} template.sql PREFIX=${PREFIX} COLLECTION=${COLLECTION}
printf "\n"

printf "\n"
printf "\n"

printf "/*\n"
printf "+------------------------------------------+\n"
printf "             Scripts end here\n"
printf "+------------------------------------------+\n"
printf "*/\n\n"

Probably not the best script, but I have to acknowledge the fact that I'm not very good at shell/bash scripting 😅

Usage example: ./script.sh -p zz -c repliesworks just fine-

Now let's say I want to support an option -f whose argument is a set of table fields in the DDL script, for example: ./script.sh -p zz -c replies -f "my_field varchar(500) DEFAULT NULL"

It would fail with: esh: illegal argument: varchar(500)

Is there a way to somehow escape a variable's value? If not, IMHO it would be a nice feature :)

Cheers.

Use loop with if

Hello. I try to use esh, but I need to create more comprehensive solution.

How to correct use for with if in template?

For example:

<% for application in ${applications} ; do -%>
        <% if "${application}" == "root" ; then -%>
                <% contextPath="/" -%>
        <% else -%>
                <% contextPath="/$application" -%>
        <% fi -%>

        <Context path="<%= $contextPath %>"  docBase="<%= $application %>.war"/>
<% done -%>

applications="root demo app"

but with this template I get next error:

test.xml:3: root: not found
test.xml:3: demo: not found
test.xml:3: app: not found

esh 0.3.1

The same process that declared a variable will also report an error!

image

nginx.tpl.esh

access_log <%= ${HOME}/nginx_access.log %> main;
    error_log <%= ${HOME}/error.lg %>;

    root <%= ${HOME}/APP_NAME %>; 

    <% if  [ -z "$location" ] || [ "${location}" == "/" ] && [ -n "$backend_url" ];then %>
    location / {
      proxy_pass <%= $backend_url %>;
      proxy_set_header Host $host;
    }
    <% fi %>

The same process that declared a variable will also report an error!

`esh` does not honor the umask value of its parent process when using the `-o` option

Hi!
First of all, kudos for this great little template engine! The attention to portability and correctness is really appreciated. 👏

I stumbled upon a little issue today as I was using the -o option: I realized that esh does not honor the umask value of its parent process and always produces files with the UNIX mode u+rw (0600) as the file is created by mktemp and not by esh itself:

esh/esh

Lines 360 to 363 in 4f1bc6e

tmpfile="$(mktemp)"
trap "rm -f '$tmpfile'" EXIT HUP INT TERM
process "$INPUT" "$vars" "$EVALUATE" > "$tmpfile"
mv "$tmpfile" "$OUTPUT"

This behavior can cause bugs in situations where the output file mode is expected to be something like rw-r--r-- (i.e. with the common and default 0022 umask value).
I think that replacing the mv command with something like cat "$tmpfile" > "$OUTPUT" should fix this.


On another note (not related to the issue above but I've realized this when reading the code), I think that the following line:

esh/esh

Line 361 in 4f1bc6e

trap "rm -f '$tmpfile'" EXIT HUP INT TERM

would be safer with the quotation marks (the single and double quotes) placed the other way around (and also because the $tmpfile variable seems to be always set on the occasions where the trap command would be triggered).

In a quite twisted scenario (i.e. where an adversary is able to create arbitrary directories in the filesystem and controlling the TMPDIR environment variable exposed to esh using the -o option) the trap command would then be vulnerable to a shell injection attack.

Empty lines are not preserved

First of all great job
I have always been looking for a template engine that works only with shell binaries.
The advantage for me is I don't need to install any annoying dependencies like with other template engines for example on python.

But I have a problem. The new lines are not present on the output if the preview line is a esh tag.
I am working with openwrt and I do have a lot off image. So that I do not have duplicated configuration files.
I sqashed them in one file as an template for esh to generate the different uci image configuration out of this file.
It works as expected, with a small bug I think.

The new line --><--- NEWLINE MISSING IN OUTPUT (see: Example code) is not present on the output. And if i remove the - in the if statement the line above <% fi %>. I get two new lines! So I do not know what is the problem. Is this maybe a bug?

Example code:

config interface 'lan'
<% if [ "$common_customer" = "abc" ]; then -%>
	option proto 'none'
<% else -%>
	option proto 'static'
	option netmask '255.255.255.0'
<% fi -%>
	option ifname 'eth1.1'
	option force_link '1'
<% if [ "$common_customer" = "123" ]; then -%>
	option ipaddr '192.168.0.1'
<% elif [ "$common_customer" = "xyc" ]; then -%>
	option ipaddr '192.168.0.254'
<% elif [ "$common_customer" = "abc" ]; then -%>
<% else -%>
	option ipaddr '192.168.0.50'
<% fi -%>
--><--- NEWLINE MISSING IN OUTPUT
config interface 'wan'
	option ifname 'eth0'
<% if [ "$network_wan_auto" != "yes" ]; then -%>
	option auto '0'
<% fi -%>
<% if [ "$network_wan_metric" != "default" ]; then -%>
	option metric '<%= $network_wan_metric %>'
<% else -%>
<% if [ "$common_customer" != "123" ]; then -%>
	option metric '100'
<% fi -%>
<% fi -%>
	option proto 'dhcp'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.