Bashful Plumber.

Goal: A script that continuously show date and time, with Dzen2, and Conky.

Before you dip your toe to scripting, you might desire to know the reason by reading this overview.

Reading


Piping and Forking in Many Languages

This article is one part of a collection. All integrated, one related to another. So we can compare each other quickly.

Tutorial/ Guidance/ Article: [ Pipe Overview ] [ BASH ] [ Perl ] [ Python ] [ Ruby ] [ PHP ] [ Lua ] [ Haskell ]

Source Code Directory: [ BASH ] [ Perl ] [ Python ] [ Ruby ] [ PHP ] [ Lua ] [ Haskell ]


A Very Bashful Start

Welcome to n00berland. Begin with simple script. We will use this loop as a source feed to pipe. This step won’t introduce Pipe nor Fork.

This script only show an infinite loop showing local time. Each updated in one second interval. We manage this interval by delaying, using sleep code.

Source:

#!/usr/bin/env bash

# endless loop
while true; do 
    date +'%a %b %d %H:%M:%S'
    sleep 1
done

Call to this simple code would produce time marching, one after another, below the command line prompt.

Pipe: Basic


Port to other Language

I respect the reader, that you are smart. You might be wondering, why I have to put this very basic script in this tutorial. This script may looks simple in BASH, and have very similar looks in most popular scripting language. But it would have very different approach in other language. As you can see in an obscure language below, Haskell has no loop, but using forever control.

Source:

import Data.Time.LocalTime
import Data.Time.Format

import Control.Concurrent
import Control.Monad

-- ----- ----- ----- ----- -----
-- wrap Funktion

myTimeFormat = "%a %b %d %H:%M:%S"

wFormatTime :: FormatTime t => t -> String
wFormatTime myUtcTime = formatTime 
  Data.Time.Format.defaultTimeLocale myTimeFormat myUtcTime

wSleep :: Int -> IO ()
wSleep mySecond = threadDelay (1000000 * mySecond)

printDate = do
     now <- getZonedTime
     let nowFmt = wFormatTime now
     putStrLn nowFmt
     wSleep 1

-- ----- ----- ----- ----- -----
-- main

main = forever $ printDate

External Command as Source Feed

Beside previous simple loop that is used as Internal Command, this tutorial also provide Conky as External Command in asset directory. I made it as simple as possible.

Source:

conky.config = {
    out_to_x = false,
    out_to_console = true,
    short_units = true,
    update_interval = 1
}

conky.text = [[\
${time %a %b %d %H:%M:%S}\
]]

A Native Pipe Between External Command

This step is overview of Pipe between two external command. This is a very simple. Only using | character. This very short script is using conky as pipe source feed and less as pipe target. Showing time and date forever in the console.

This infinite pipe run in time-less fashioned.

I had made additional dirname function, relative to the BASH source, to locate the conky script assets.

Source:

#!/usr/bin/env bash

dirname=$(dirname $(readlink -f "$0"))
path="$dirname/../assets"

cmdin="conky -c $path/conky.lua"
cmdout="less" # or dzen2

$cmdin | $cmdout

You can see, how simple it is. This would have less output similar to this below.

Pipe: to Less

Your wallpaper might be different than mine.

A Native Pipe from Internal Function

Using internal function as source feed to external command is straight forward. This should be self explanatory.

Other language has more complex mechanism for this. Most common is using subProcess or Popen. From this step forward, this would looks different in other language.

Source:

#!/usr/bin/env bash

generated_output() {
    # endless loop
    while :; do 
      date +'%a %b %d %H:%M:%S'
      sleep 1
    done
}

cmdout="less" # or dzen2

generated_output | $cmdout

Fork Overview

Fork in bash is also simple. All it takes is just & character.

This step use internal function as source feed, as continuation of previous step.

This step use dzen2, with complete parameters. This dzen2 is forked, running in the background. Detached from the script, no need to wait for dzen2 to finish the script.

Source:

#!/usr/bin/env bash

generated_output() {
    # endless loop
    while :; do 
      date +'%a %b %d %H:%M:%S'
      sleep 1
    done
}

xpos=0
ypos=0
width=640
height=24
fgcolor="#000000"
bgcolor="#ffffff"
font="-*-fixed-medium-*-*-*-12-*-*-*-*-*-*-*"

parameters="  -x $xpos -y $ypos -w $width -h $height" 
parameters+=" -fn $font"
parameters+=" -ta c -bg $bgcolor -fg $fgcolor"
parameters+=" -title-name dzentop"

# ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----
# main

# remove all dzen2 instance
pkill dzen2

generated_output | dzen2 $parameters &

This step also add system command that kill any previous dzen2 instance. So it will be guaranteed, that the dzen2 shown is coming from the latest script.

Fork Code in Function

Let’s have a look again at the line below. It is the heart above script.

generated_output | dzen2 $parameters &

In more complex situation that need more flexibility, we can separate the pipe | in different function.

#!/usr/bin/env bash

function get_dzen2_parameters() { 
    xpos=0
    ypos=0
    width=640
    height=24

    fgcolor="#000000"
    bgcolor="#ffffff"
    font="-*-fixed-medium-*-*-*-12-*-*-*-*-*-*-*"

    parameters="  -x $xpos -y $ypos -w $width -h $height" 
    parameters+=" -fn $font"
    parameters+=" -ta c -bg $bgcolor -fg $fgcolor"
    parameters+=" -title-name dzentop"
}

function generated_output() {
    # endless loop
    while :; do 
      date +'%a %b %d %H:%M:%S'
      sleep 1
    done
}

function run_dzen2() {
    get_dzen2_parameters    
    command_out="dzen2 $parameters"
    
    {
        generated_output 
    } | $command_out
}

function detach_dzen2() {    
    run_dzen2 &
}

# ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----
# main

# remove all dzen2 instance
pkill dzen2

# run process in the background
detach_dzen2

Source:


Polishing The Script

This step, we use conky again, as a source feed. And also parameterized dzen2 as continuation of previous step.

This step add optional transset transparency, detached from script. So we have two forks, dzen and transset.

# optional transparency
sleep 1 && exec `(transset .8 -n dzentop >/dev/null 2>&1 &)` & 

Source:

Finally, we have this complete script.

#!/usr/bin/env bash

function get_dzen2_parameters() { 
    xpos=0
    ypos=0
    width=640
    height=24

    fgcolor="#000000"
    bgcolor="#ffffff"
    font="-*-fixed-medium-*-*-*-12-*-*-*-*-*-*-*"

    parameters="  -x $xpos -y $ypos -w $width -h $height" 
    parameters+=" -fn $font"
    parameters+=" -ta c -bg $bgcolor -fg $fgcolor"
    parameters+=" -title-name dzentop"
}

function generated_output() {
    dirname=$(dirname $(readlink -f "$0"))
    path="$dirname/../assets"
    conky -c "$path/conky.lua"
}

function run_dzen2() {
    get_dzen2_parameters    
    command_out="dzen2 $parameters"
    
    {
        generated_output 
    } | $command_out
}

function detach_dzen2() {    
    run_dzen2 &
}

function detach_transset() { 
    {
        sleep 1
    
        # you may use either xorg-transset or transset-df
        # https://github.com/wildefyr/transset-df    
        exec `(transset .8 -n dzentop >/dev/null)`
    } &
}

# ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----
# main

# remove all dzen2 instance
pkill dzen2

# run process in the background
detach_dzen2

# optional transparency
detach_transset

Source:

This would have dzen2 output similar to this below.

Pipe: to Dzen2

You may use transset-df instead of transset.

Lemonbar

I also provide Lemonbar, instead of Dzen2. The code is very similar.

Source:


Coming up Next

There already an advance case of Pipe and Fork. Multitier, and bidirectional.


There above are some simple codes I put together. I’m mostly posting codes so I won’t have any problems finding it in the future.

Thank you for reading.