Embedding other scripting languages in Bash

Mix It Up

© Lead Image © Nebari, Fotolia.com

© Lead Image © Nebari, Fotolia.com

Article from Issue 293/2025
Author(s):

Solve Bash blind spots by embedding other scripting languages into your Bash scripts to get the features you need. Pete shows you solutions for floating-point math, charting, GUIs, and hardware integration.

While Bash has a huge variety of command-line tools that you can integrate into your code, sometimes you can still get stuck. Bash is extremely powerful, but, like all programming languages, it has its weakness. Some typical areas that can be challenging when using Bash include:

  • Floating-point math
  • Charting
  • Graphical user interfaces (GUIs)
  • Hardware integration

Command-line utilities can help address many of these topics, such as bc for floating-point math or Zenity [1] for user dialogs. While these tools are extremely useful, they may not give you the functionality or customization that you need. A common approach to solving this issue is to write standalone programs in another language, like Python, and then have your Bash script call that program.

You can also solve Bash limitations by embedding code from another scripting language (e.g., Lua, NodeJS, PHP, Python, or Tcl/Tk) within your Bash script. This method offers the advantage of keeping all the code in one script. In this article, I'll look at solving the issues of floating-point math, charting, GUIs, and hardware integration by embedding other scripting languages into Bash.

How to Embed Other Languages

There are several techniques for embedding other programming languages in Bash. For very small code bits, a string of text can be piped to the other scripting language's interpreter as follows:

$ # Use Python to find Pi
$ echo "import math;print(math.pi)" | python3
3.141592653589793

Here, a string of commands are piped to Python. A semicolon is used to separate each command. To return the result to Bash, a Python print statement is used.

Some languages, such as Julia and Python, have command-line options to pass in a string of commands (see Listing 1). Command-line options offer the slight advantage of not using a pipe or an echo statement.

Listing 1

Passing in a String of Commands

$ # Print Hello World with Julia, using -e option
$ julia -e 'main(ARGS) = println("Hello World!"); @main'
Hello World!
$ # Print Hello World with Python, using - c option
$ python3 -c 'print("Hello World")'
Hello World

The disadvantage of both approaches is that writing multiple lines of code can be awkward to manage. It's especially challenging if you are using Python with indented lines.

Another approach is to use the << characters for the redirection of the input. A Heredoc marker is used to identify the end of the redirected input block:

$ # Use Python to show Pi
$ python3 << END
> # Python code here
> import math
> print(math.pi)
> END
3.141592653589793

In this example, an END marker was used as a terminator for the block of text that was redirected into Python.

The Bash/Python example in Listing 2 passes in a Bash variable (a), and then the Python output is stored in a second Bash variable (ans).

Listing 2

Passing a Bash Variable into Python

# Pass a Bash variable into Python
# Get the Python output into a Bash variable
a=4
ans=`python3 << END
# Python code below
import math
b = $((a)) + math.pi
print(b)
END`
# Back in Bash, show the answer
echo "answer is: $ans"

For my last example, a Bash variable is referenced in the redirected Python block with $((my_variable)). To get the output from a Python embedded statement into a Bash variable, you can use backtick (`) characters around the entire command string.

Passing multiple variables between Bash and another scripting language can also be done using environment variables. When I create GUI interfaces later, I'll show an example of this.

Now that you've have an understanding of how to embed other scripting languages into Bash, I will cover solutions to some common Bash issues.

Floating-Point Math

Bash works great with integer math, but floating-point math isn't directly supported. This example returns a zero instead of the correct decimal value:

$ # Bash math of 1/3 gives 0
$ echo $(( 1 / 3 ))
0

Tools such as bc, printf, and awk can be used for floating-point math. These code statements use bc to show 1/3 with two decimals:

$ # Use bc for Bash math
$ echo $(bc -l <<< 'scale=2; 1/3')
.33
$ # To show a leading zero
$ printf '%3.2f\n' $(bc <<< 'scale=2; 1/3')
0.33

Using Bash command-line tools works, but it can often be a little confusing to read the syntax. Listing 3 shows some examples using embed NodeJS, PHP, and Python calls within a Bash shell to solve the same 1/3 question. If you're interested in doing any advanced math or statistics, embedding another language in your code could make things a little easier.

Listing 3

Embedding Calls for Floating-Point Math

# Use other programming languages
# to do math in a Bash shell
#
# Solve 1/3 = 0.33 (2 decimals)
#
# NodeJS
echo "Math.round(1/3*100)/100" | node -p
# PHP
echo '<?php print round(1/3,2);?>' | php
# Python
echo 'print(round(1/3,2))' | python3

Charting

Charting in Bash is usually limited to text-based graphics. There are some powerful command-line charting options such as Gnuplot [2] that can be used, but these tools may not have all the features that you need. Scripting languages like Julia, Python, and R have advanced charting options that should meet most of your application requirements.

One such charting limitation is speedometer charts. While not supported in Gnuplot, speedometer charts are available in the Python tk_tools library [3], which can be installed with

pip3 install tk_tools

To create these charts, the script gauge2.sh (Listing 4) uses Bash to find the CPU zone 0 and 1 temperatures (lines 6 and 7). Two gauges are created with labels and ranges (lines 19-24). The Bash temperature values are passed into the gauge objects in lines 29 and 30. Figure 1 shows output from gauge2.sh along with the Bash commands for finding the zone 0 and 1 temperatures.

Listing 4

Bash Using the Python tk_tool Library

01 # gauge2.sh - get CPU temps then create 2 gauges
02 #           - use Python tk_looks library for the gauges
03 #!\bin\bash
04
05 # Get CPU temp for zone 0 and 1 (value is in milli_DegC)
06 zone0=$(cat /sys/class/thermal/thermal_zone0/temp)
07 zone1=$(cat /sys/class/thermal/thermal_zone1/temp)
08
09 #Python Code - Redirect input with "<<" until END statement
10 python3 << END
11 # Python Two Gauge Example
12 import tkinter as tk
13 import tk_tools
14
15 root = tk.Tk()
16 root.title('CPU Temperature Zones 0 and Zone 1')
17
18 # Create 2 gauges
19 gauge0 = tk_tools.Gauge(root, max_value=70.0,\
20         label='Zone 0 Temp', unit='Deg C',\
21         width=600, height=300)
22 gauge1 = tk_tools.Gauge(root, max_value=70.0,\
23         label='Zone 1 Temp', unit='Deg C',\
24         width=600, height=300)
25 gauge0.grid()
26 gauge1.grid()
27
28 # Update the zone temps, divide by 1000 for DegC
29 gauge0.set_value($((zone0)) / 1000)
30 gauge1.set_value($((zone1)) / 1000)
31
32 root.mainloop()
33
34 END
Figure 1: A Bash script embeds the Python tk_tool library to create speedometer charts.

The popular, well-documented Python Matplotlib library [4] is another excellent option for building different chart types. In Listing 5, the Bash script bars3.sh is used to gather memory usage data from vmstat, the virtual memory stats tool (lines 7-9). Embedded Python code is used to create a Matplotlib bar chart (lines 24-35). The chart data is configured with Bash variables (line 20) using the syntax of $((bash_variable)) within the redirected Python code block. Figure 2 shows vmstat's results along with the bars3.sh script output using a Matplotlib bar chart.

Listing 5

Bash Using a Python Matplotlib Chart

01 # bars3.sh - pass Bash data to an embedded Python call
02 #          - get vmstat data into Bash variables
03 #          - use matplotlib bar chart to show Bash data
04 #!\bin\bash
05
06 # Get vmstat data in kbytes
07 vmfree=$(vmstat | awk 'NR == 3 {print int($4/1000)}')
08 vmbuff=$(vmstat | awk 'NR == 3 {print int($5/1000)}')
09 vmcache=$(vmstat | awk 'NR == 3 {print int($6/1000)}')
10
11
12 # Python Code to create 3 vertical bars
13 # Bash variables are passed as data values
14 # Redirect input with "<<" until END statement
15 python3 << END
16 import numpy as np
17 import matplotlib.pyplot as plt
18
19 # creating the dataset using Bash variables
20 data = {'Free':$((vmfree)), 'Buffer':$((vmbuff)), 'Cache':$((vmcache))}
21 features = list(data.keys())
22 values = list(data.values())
23
24 fig = plt.figure(figsize = (10, 5))
25
26 # creating the bar plot and add values on the bars
27 plt.bar(features, values, width = 0.4)
28 for i in range(len(features)):
29     plt.text(i,values[i],values[i])
30
31 plt.xlabel("Memory Value")
32 plt.ylabel("kbytes")
33 plt.title("Show Bash VMSTAT Data in Matplotlib")
34
35 plt.show()
36 END
Figure 2: A Bash script gets the vmstat data. Then, using embedded Python, Matplotlib charts the data.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Tutorial – Shell Scripting

    You do not need to learn low-level programming languages to become a real Linux power user. Shell scripting is all you need.

  • Bash 5.0

    It's a coincidence that the Linux kernel and Bash jumped to version 5.0 at about the same time. While Linus assigns the numbers as he sees fit, Bash changes its version when major adjustments are made. Here's what users can expect in Bash 5.

  • Xonsh

    Create lightweight Raspberry Pi scripts with Xonsh, a Python shell that lets you write scripts in Python with Bash commands mixed in.

  • Ren'Py

    Ren'Py helps you create Android, Linux, macOS, Windows, and HTML5 games and apps.

  • How Does ls Work?

    A simple Linux utility program such as ls might look simple, but many steps happen behind the scenes from the time you type "ls" to the time you see the directory listing. In this article, we look at these behind-the-scene details.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News