<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: John Robertson</title>
    <description>The latest articles on DEV Community by John Robertson (@jrbrtsn).</description>
    <link>https://dev.to/jrbrtsn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F465638%2Fb2d2c29b-5e26-41fb-8679-1b55b1fa268d.jpeg</url>
      <title>DEV Community: John Robertson</title>
      <link>https://dev.to/jrbrtsn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jrbrtsn"/>
    <language>en</language>
    <item>
      <title>TUI select fn</title>
      <dc:creator>John Robertson</dc:creator>
      <pubDate>Tue, 30 Mar 2021 21:14:20 +0000</pubDate>
      <link>https://dev.to/jrbrtsn/hkselect-2cjh</link>
      <guid>https://dev.to/jrbrtsn/hkselect-2cjh</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uLf1uLSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/va7hx7fkv12mchnms18g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uLf1uLSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/va7hx7fkv12mchnms18g.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those of you familiar with &lt;strong&gt;bash&lt;/strong&gt;'s &lt;em&gt;select&lt;/em&gt; builtin know that the automated layout of choices is a nice feature. Last week while prototyping some SQL I became frustrated with &lt;em&gt;select&lt;/em&gt;, mostly because you must type in a number and then press the Enter key.  Remembering the TUI interfaces of the 1980's, I set about writing &lt;em&gt;hkselect&lt;/em&gt;, a hotkey driven replacement for &lt;em&gt;select&lt;/em&gt;. Here are the main features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automated layout of choices.&lt;/li&gt;
&lt;li&gt;Assign item hotkey with &amp;amp;.&lt;/li&gt;
&lt;li&gt;Automatic hotkey assignment if &amp;amp; is missing.&lt;/li&gt;
&lt;li&gt;Bind escaped keys to callback functions.&lt;/li&gt;
&lt;li&gt;Transparently caches layout information and composed labels.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full project is &lt;a href="https://github.com/jrbrtsn/bash_plus_plus"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below is an example &lt;strong&gt;bash&lt;/strong&gt; program with a functioning hotkey TUI that includes callbacks for F1, F2, and F3:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


</description>
      <category>bash</category>
    </item>
    <item>
      <title>Where is Your Data?</title>
      <dc:creator>John Robertson</dc:creator>
      <pubDate>Fri, 18 Sep 2020 01:52:20 +0000</pubDate>
      <link>https://dev.to/jrbrtsn/where-is-your-data-n92</link>
      <guid>https://dev.to/jrbrtsn/where-is-your-data-n92</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Making source code easier to read without significantly increasing its size is always a win. For &lt;strong&gt;C&lt;/strong&gt; and &lt;strong&gt;C++&lt;/strong&gt; I've developed a simple convention to make clear the distinction between data residing in one of two types of static data segments, data which resides on the stack, and the visibility of static data (global or single source file). This is very useful when writing multi-threaded apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible Data Locations
&lt;/h2&gt;

&lt;p&gt;In terms of where data can exist within a given process space, there are only four locations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Static data segment&lt;/li&gt;
&lt;li&gt;Thread specific static data segment&lt;/li&gt;
&lt;li&gt;Heap&lt;/li&gt;
&lt;li&gt;Stack&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. Really. If you are writing a multi-threaded application, it is &lt;strong&gt;vitally&lt;/strong&gt; important that you know the location of all data. No amount of OOP wizardry can defer for very long your need to know this. It is to your advantage to leverage each of these locations for appropriate purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Globally Visible Static Data
&lt;/h2&gt;

&lt;p&gt;Despite what programming language ideologues may say about the evils of global static data, I find some is usually necessary when writing non-trivial applications. Also, I've found it useful in my source code to make clear whenever global data is involved. Here is how I do this, usually in a file named: &lt;em&gt;"NameOfProject.h"&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct Global {
   int nClients; // or whatever
   /* and so on */
};
extern struct Global G;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now when I need to do something with &lt;em&gt;nClients&lt;/em&gt;, it must appear prefixed with 'G.'&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;printf("Client count is: %d\n", G.nClients);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You will of course need to instance 'G' in a &lt;em&gt;.c&lt;/em&gt; or &lt;em&gt;.cc&lt;/em&gt; file somewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Single Sourcefile Static Data
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;C&lt;/strong&gt; and &lt;strong&gt;C++&lt;/strong&gt;, (outside of any class declarations) the "static" keyword is overloaded; in the context of data instances, it means that the data will be located in the static data segment, &lt;strong&gt;and&lt;/strong&gt; that the symbol will not be visible in the corresponding object file for the purpose of linking; which is to say that the symbol is &lt;em&gt;invisible&lt;/em&gt; to other source files. This is a lot simpler than other mechanisms for scoping static data, and it works in both &lt;strong&gt;C&lt;/strong&gt; and &lt;strong&gt;C++&lt;/strong&gt;. You will need to place code similar to this in your &lt;em&gt;.c&lt;/em&gt; or &lt;em&gt;.cc&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static struct {
   /* Correction specifies _Atomic.
    * Thanks Andrew Clayton for pointing out
    * this omission!
    */
   _Atomic int n_someFuncCalled;
   /* Other stuff */
} S;

int someFunc()
{
   ++S.n_someFuncCalled;
   /* Works no matter how many threads */
   return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here the source file scoped data which resides in the static data segment is prefixed with 'S.'.&lt;br&gt;
Likewise, for thread-specific static data you can do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static _Thread_local struct {
   int n_someFuncCalled;
   /* count is only for the current thread */
} TS;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Accessing this data requires the prefix 'TS.' Data found in &lt;em&gt;TS&lt;/em&gt; resides in the static data segment which is unique for each thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce Function Argument Counts
&lt;/h2&gt;

&lt;p&gt;One advantage of using the static data segments is reducing the number of arguments you must pass to some functions. This can, of course, be exploited to write sloppy and unmaintainable code. On the other hand, with proper understanding it can be used to write more robust and efficient code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I cannot overstate the importance of knowing where your data resides in a multi-threaded process. This article presents a simple and practical way to represent this in source code. If you'd like to see an example of a real project using this technique, please have a look at &lt;a href="https://github.com/jrbrtsn/ban2fail"&gt;ban2fail&lt;/a&gt;&lt;br&gt;
Happy coding!&lt;/p&gt;

</description>
      <category>c</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Bash++ Parsing</title>
      <dc:creator>John Robertson</dc:creator>
      <pubDate>Mon, 14 Sep 2020 21:47:14 +0000</pubDate>
      <link>https://dev.to/jrbrtsn/bash-parsing-3opi</link>
      <guid>https://dev.to/jrbrtsn/bash-parsing-3opi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This morning when I took a walk with my son, I asked him to come up with a &lt;strong&gt;Bash&lt;/strong&gt; issue that needs improvement.  He mentioned regex parsing, which I agree is a chore. Afterwards I sat down and wrote the 'regex_read' function, which supplies a convenient 'read' like API for this purpose. I will introduce the 'regex_read' function further below, but first let's review using the builtin 'read' command for parsing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing with 'read'
&lt;/h2&gt;

&lt;p&gt;A common way to parse input in &lt;strong&gt;Bash&lt;/strong&gt; is to use the builtin 'read' command. This command reads one line (or record) at a time, and then splits out tokens into variables. Consider this typical example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while read TOK1 TOK2 ALL_ELSE; do
# Do something with $TOK1 and $TOK2
done &amp;lt;/some/input/file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here the file '/some/input/file' is opened and connected to the shell's &lt;strong&gt;stdin&lt;/strong&gt; within the scope of this loop, thus providing input for 'read'. There are implied defaults in this example of which you need to be aware:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each invocation of 'read' consumes bytes up to and including a delimiter character, which by default is &lt;em&gt;newline&lt;/em&gt;. You can change this value with the '-d' switch.&lt;/li&gt;
&lt;li&gt;'read' splits each record into tokens according to $IFS, the contents of the &lt;em&gt;input field separator&lt;/em&gt; variable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Usually $IFS includes unprintable characters. If you'd like to know what those characters are, the 'printf' builtin command comes to the rescue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Issue command
printf '%q\n' "$IFS"
$' \t\n'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we can see that $IFS contains a &lt;em&gt;space&lt;/em&gt;, a &lt;em&gt;tab&lt;/em&gt;, and a &lt;em&gt;newline&lt;/em&gt;. If you need to parse a CSV file, you'll want something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IFS=$',\n'
read COL1 COL2 COL3 COL4 ALL_ELSE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This syntax is fairly concise and convenient. If it works for the problem at hand, there is nothing to fix. Sometimes being able to specify the delimiter character and the contents of $IFS isn't enough, though. If you are faced with this, then regex parsing is your best option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing with Regex's
&lt;/h2&gt;

&lt;p&gt;Parsing with regex's is a common approach for complex input data. Parsing with regex's in &lt;strong&gt;Bash&lt;/strong&gt; is a bit clunky, and it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while IFS= read; do
   [[ $REPLY =~ systemd\[([^:]*)\]:\ (.*)$ ]] || continue
   full_match="${BASH_REMATCH[0]}"
   pid="${BASH_REMATCH[1]}" 
   msg="${BASH_REMATCH[2]}"
   echo "systemd message: pid= '$pid', msg= '$msg'"
done &amp;lt;/var/log/syslog
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see, fishing the tokens out of $BASH_REMATCH[] is a boilerplate chore. In the example it is noteworthy that $IFS is set to nothing, to prevent 'read' from splitting tokens. $REPLY is used to retrieve the record; this is the documented default return variable if none are supplied.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing with 'regex_read'
&lt;/h2&gt;

&lt;p&gt;In a previous post I introduced the &lt;strong&gt;Bash++&lt;/strong&gt; &lt;a href="https://dev.to/jrbrtsn/bash-return-stack-1lnh"&gt;return stack facility&lt;/a&gt;.  Now I will use that facility to eliminate some drudgery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash
############################################################
# Example script to demonstrate bash++ regex_read function
#
# John Robertson &amp;lt;john@rrci.com&amp;gt;
# Initial release: Mon Sep 14 10:29:20 EDT 2020
#

# Halt on error, no globbing, no unbound variables
set -efu

# import oop facilities and other goodies
source ../bash++

###################################
### Execution starts here #########
###################################

# Open file to supply input on file descriptor $FD.
# Use recent bash syntax to assign next unused file descriptor to variable FD.
# We do this so all standard streams remain available inside of loop for
# interactive commands.
exec {FD}&amp;lt;/var/log/syslog

# Loop until no more data available from $FD
# Regex matches 'systemd' syslog entries, breaks out date stamp, pid, and message
while regex_read '^([^ ]+ [^ ]+ [^ ]+) .*systemd\[([^:]*)\]: (.*)' -u $FD; do

   # First fetch the number of matches from the return stack.
   RTN_pop n_matches

   # Not interested in less than perfect match
   (( n_matches == 4 )) || continue

   # Pop match results into variables
   RTN_pop full_match dateStamp pid msg
   # Clear the terminal
   clear
#  "Full match is: '$full_match'"
   echo "systemd message: pid= '$pid', dateStamp= '$dateStamp',  msg= '$msg'"

   # Use builtin bash menuing to branch on user's choice
   PS3='Action? '
   select action in 'ignore' 'review' 'quit'; do

      case $action in

         ignore) ;; # no worries

         review) read -p 'Chase up all relevant information, present to user. [Return to continue] ';;

         quit) exit 0;;

      esac

      # go get another line from syslog
      break
   done # End of 'select' menu loop

done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are several points of interest in this example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The input file is opened and assigned an unused file descriptor, which is placed in $FD. This leaves the shell's &lt;strong&gt;stdin&lt;/strong&gt; available for user interaction within the 'regex_read' loop.&lt;/li&gt;
&lt;li&gt;'regex_read' syntax is just like 'read', except that the regex itself must be the first argument; all arguments after the first are passed through to 'read'. Match results will all be placed on the return stack.&lt;/li&gt;
&lt;li&gt;The builtin 'select' command is used within the loop to present a menu to the user and branch on the user's choice.&lt;/li&gt;
&lt;li&gt;With the return stack it is possible to accomodate a variable number of complex return values from a function.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  'regex_read' Implementation
&lt;/h2&gt;

&lt;p&gt;The implementation of 'regex_read' is simple, as it is essentially derived from 'read' itself. Here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function regex_read ()
############################################################
# Similar to bash 'read' builtin, but parses subsequent
# read buffer using the supplied regular expression.
# Arguments:
#   regex pattern
# Returns:
#   logical TRUE if read was successful
#   or logical FALSE on end-of-file condition.
# Return stack:
#   Full string match (if any)
#   token1_match (if any)
#   ...
#   Last argument is _always_ number of matches found. Pop it first.
#   
{
   # Stash the regular expression
   local ndx count regex="$1"

   # All other args are for 'read'
   shift

   # Call read with other supplied args. Fails on EOF
   IFS= read $@ || return 1

   # Apply regular expression parsing to read buffer
   if [[ $REPLY =~ $regex ]]; then
      # Place results on return stack
      count=${#BASH_REMATCH[@]}
      for (( ndx= 0; ndx &amp;lt; count; ++ndx )); do
         RTN_push "${BASH_REMATCH[$ndx]}"
      done
      # Last stack arg is number of match results
      RTN_push $count

   else
      # regex failed to match
      RTN_push 0
   fi
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt; can parse data using regular expressions. This task is made much simpler by using the 'regex_read' function, which is imported by &lt;em&gt;source'ing&lt;/em&gt; &lt;a href="https://github.com/jrbrtsn/bash_plus_plus"&gt;bash++&lt;/a&gt;. All files in the post are available from the same &lt;a href="https://github.com/jrbrtsn/bash_plus_plus"&gt;Github repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>CLI and Streams</title>
      <dc:creator>John Robertson</dc:creator>
      <pubDate>Sun, 13 Sep 2020 20:22:18 +0000</pubDate>
      <link>https://dev.to/jrbrtsn/cli-and-streams-1i6n</link>
      <guid>https://dev.to/jrbrtsn/cli-and-streams-1i6n</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;For those who are new to *nix shells like &lt;strong&gt;Bash&lt;/strong&gt;, the underlying framework of streams may seem a little hazy. Even less clear is the value proposition of developing a firm understanding of these facilities. In this article I will endeavor to pass along some information I wish someone had told me 30 years ago, when my *nix shell journey began.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streams
&lt;/h2&gt;

&lt;p&gt;Every program, including the shell into which you may type commands, a shell program you may be writing, the 'ls' command, etc. have three standard I/O streams commonly referred to as &lt;strong&gt;stdin&lt;/strong&gt;, &lt;strong&gt;stdout&lt;/strong&gt;, and &lt;strong&gt;stderr&lt;/strong&gt;. These streams are always assigned file descriptor numbers 0, 1, and 2 repectively. The purpose of these streams is twofold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Route information between a running process (program) and a file.&lt;/li&gt;
&lt;li&gt;Route information between two running processes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These particular streams are half duplex, meaning data flows in one direction only for each stream.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stream Content
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;stdin&lt;/strong&gt; is used to move data into a process, whereas &lt;strong&gt;stdout&lt;/strong&gt; and &lt;strong&gt;stderr&lt;/strong&gt; are two channels for moving data out of a process. &lt;strong&gt;stdout&lt;/strong&gt; is normally for the information you get from a program if everything goes well, while &lt;strong&gt;stderr&lt;/strong&gt; is normally reserved for information about things which have gone wrong. Each stream passes a series of bytes; whether or not these bytes are text, binary, JSON, XML, etc. is only a matter of concern to the sender and receiver. This is important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your CLI
&lt;/h2&gt;

&lt;p&gt;When you use a shell such as &lt;strong&gt;Bash&lt;/strong&gt; interactively, the three streams mentioned earlier are connected as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;stdin&lt;/strong&gt; of your shell is connected to your terminal program, which feeds in the strings you have typed when you press the Enter key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;stdout&lt;/strong&gt; and &lt;strong&gt;stderr&lt;/strong&gt; of the shell are also connected to your terminal program, which prints the output of these streams to the terminal window so you can read them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is possible to redirect these streams as necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subprocesses
&lt;/h2&gt;

&lt;p&gt;Let's look at what happens when you issue the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/bin/ls
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Your shell invokes the kernel &lt;em&gt;fork()&lt;/em&gt; call to create a child process which is identical to the interactive shell you are using, save for the return value from the &lt;em&gt;fork()&lt;/em&gt; call itself.  The main implication here is that &lt;strong&gt;stdin&lt;/strong&gt;, &lt;strong&gt;stdout&lt;/strong&gt; and &lt;strong&gt;stderr&lt;/strong&gt; are all connected to the same endpoints as the shell you are using. Because the command line began with '/bin/ls', this child process then invokes the kernel &lt;em&gt;exec()&lt;/em&gt; call to overlay the '/bin/ls' program - which is to say that the child process becomes '/bin/ls' while retaining the stream assignments of the parent. Now '/bin/ls' does its thing chasing up file meta-information, and sends the resulting series of text characters to &lt;strong&gt;stdout&lt;/strong&gt;, which you'll recall gets printed by your terminal program.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redirect to a file
&lt;/h2&gt;

&lt;p&gt;If you wish to store the output from '/bin/ls' in a file, you may &lt;em&gt;redirect&lt;/em&gt; &lt;strong&gt;stdout&lt;/strong&gt; of '/bin/ls' to a file like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1&amp;gt;ls-output.txt /bin/ls
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here I've placed the redirection at the beginning of the command. You can also place it after the command. Location will become important only when the command line is more complex, involving more than one command.&lt;br&gt;
This time the interactive shell creates (or truncates if it already exists) a file, "ls-output.txt", and connects it to the &lt;strong&gt;stdout&lt;/strong&gt; stream of the child process. When '/bin/ls' does its thing, the result gets written to the "ls-ouput.txt" file. Your terminal program doesn't get involved in any way with this redirection. This is a key concept.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation
&lt;/h2&gt;

&lt;p&gt;Using a terminal program is just one of many ways to move data in and out of a shell, albeit a very handy one for interactive use. If you have a need to automate something which you can accomplish by typing interactive commands, then automating this will be very straightforward. This is probably the most compelling reason to learn how to do things from a command line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Understanding standard streams is central to the endeavor of shell programming on a *nix system. Once you become confident about how streams work, you will be better able to create mashup applications quickly using &lt;strong&gt;Bash&lt;/strong&gt; (or other shells). This is the key to automation, which is probably the largest value proposition &lt;strong&gt;Bash&lt;/strong&gt; has to offer.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>bash</category>
      <category>linux</category>
      <category>cli</category>
    </item>
    <item>
      <title>Bash++ Classes</title>
      <dc:creator>John Robertson</dc:creator>
      <pubDate>Sat, 12 Sep 2020 15:52:35 +0000</pubDate>
      <link>https://dev.to/jrbrtsn/bash-classes-5e4e</link>
      <guid>https://dev.to/jrbrtsn/bash-classes-5e4e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt; is an interpreted language not often associated with object oriented programming (OOP), probably because it does not provide first class facilities for this purpose. However, any language which can dynamically allocate memory and supports pointers can be used for OOP; in the end OOP is really just a style of managing data which is potentially located on the heap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Historical Context
&lt;/h2&gt;

&lt;p&gt;Back in the 1990's I got a gig writing a warehouse inventory app for a 16 bit Psion handheld computer.  My choice of languages for this platform was either &lt;strong&gt;Basic&lt;/strong&gt; or &lt;strong&gt;C&lt;/strong&gt;. By that time I had been coding in &lt;strong&gt;C&lt;/strong&gt; for ten years, and &lt;strong&gt;C++&lt;/strong&gt; for five. Given the available options, I of course chose &lt;strong&gt;C&lt;/strong&gt;.&lt;br&gt;
My experience with &lt;strong&gt;C++&lt;/strong&gt; had already skewed my thought processes towards OOP, so I endeavored to carry that forwards in &lt;strong&gt;C&lt;/strong&gt;. A few hours of fiddling produced something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Class declaration
typedef struct _Class {
   int ival1;
   char *dyn_str;
   /* ... And so on ... */
} Class

// Constructor
Class* Class_constructor(
   Class *self,
   int ival,
   const char *str
)
{
   self-&amp;gt;ival1= ival;
   self-&amp;gt;dyn_str= strdup(str);
   return self;
}

// Destructor
void* Class_destructor(Class *self)
{
   if(self-&amp;gt;dyn_str)
      free(self-&amp;gt;dyn_str);
   return self;
}

// Member function
void Class_printStuff(const Class *self)
{
   printf("%s %d\n", self-&amp;gt;dyn_str, self-&amp;gt;ival1);
}

// Program starts here
int main()
{
   Class obj;
   Class_constructor(&amp;amp;obj, 25, "You feel like you are");
   Class_printStuff(&amp;amp;obj);
   Class_destructor(&amp;amp;obj);
   return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Hmmm, that looks pretty similar to &lt;strong&gt;C++&lt;/strong&gt;, doesn't it?  I added a few short macros to provide functionality of &lt;strong&gt;C++&lt;/strong&gt;'s 'new' and 'delete' operators, and I was off to the races. The client was well pleased with the final product, which had a GUI that looked a whole lot like smart phone apps look today. With this success under my belt I continued to pursue OOP in &lt;strong&gt;C&lt;/strong&gt;. Within a year I realized that was I writing OO code much faster in &lt;strong&gt;C&lt;/strong&gt; than I could in &lt;strong&gt;C++&lt;/strong&gt;. Furthermore, the compatibility of various &lt;strong&gt;C++&lt;/strong&gt; compilers at that time was deplorable.&lt;br&gt;
I went on to work out polymorphism and virtual functions, as well as a standard toolkit with all the modern facilities required for secure multithreaded OO programming. To this day I am far more productive writing OO programs in &lt;strong&gt;C&lt;/strong&gt; than &lt;strong&gt;C++&lt;/strong&gt;, and do so whenever &lt;strong&gt;C&lt;/strong&gt;, or a mix of &lt;strong&gt;C&lt;/strong&gt; and &lt;strong&gt;C++&lt;/strong&gt; is a practical option.&lt;/p&gt;
&lt;h2&gt;
  
  
  Fast Forward
&lt;/h2&gt;

&lt;p&gt;Roughly 5 years ago I started toying with the idea of OOP in &lt;strong&gt;Bash&lt;/strong&gt;. After a few hours working on this I ran up against &lt;strong&gt;Bash&lt;/strong&gt;'s lack of a generalized function return facility (without invoking a subshell which wreaks havoc with data concurrency). Paying work and life intervened, and it is only this week the idea bubbled back up to the fore of my thoughts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Extending Bash
&lt;/h2&gt;

&lt;p&gt;Let me be clear: I have no intention of modifying the source code of &lt;strong&gt;Bash&lt;/strong&gt; itself; &lt;strong&gt;Bash&lt;/strong&gt;'s main value proposition is being a robust, flexible, and &lt;em&gt;mature&lt;/em&gt; interpreter which is already installed on just about every POSIX platform - read: embedded Linux &amp;amp; BSD.  Throw in non-interactive use of &lt;strong&gt;ssh&lt;/strong&gt; and you can now query and/or administrate tens of thousands of heterogeneous embedded systems in parallel.&lt;/p&gt;
&lt;h2&gt;
  
  
  Return Facility: done
&lt;/h2&gt;

&lt;p&gt;Earlier this week I posted an implementation in &lt;strong&gt;Bash&lt;/strong&gt; for a &lt;a href="https://dev.to/jrbrtsn/bash-return-stack-1lnh"&gt;return stack&lt;/a&gt;. This not only solves the original problem, but supports quite well multiple return values from any function, &lt;em&gt;a la&lt;/em&gt; Python.&lt;/p&gt;
&lt;h2&gt;
  
  
  OOP
&lt;/h2&gt;

&lt;p&gt;Subsequently I have implemented 'malloc', as well as operators 'new', 'delete', 'fetch' (accessor), and 'call' (member functions).&lt;br&gt;
This is how a working &lt;strong&gt;Bash&lt;/strong&gt; class looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# import oop facilities
source ../bash++

function FirstClass::FirstClass ()
###################################
# Constructor for FirstClass
#
{
   local opts=$(shopt -o -p nounset)
   # Necessary to avoid "unbound variable" exception
   set +u

   local this=$1
   shift

   # Concatenate all constructor args separated by a space
   local catStr
   while [[ -n $1 ]]; do

      if [[ -z $catStr ]]; then
         catStr="$1"
      else
         catStr="$catStr $1"
      fi

      shift
   done

   # Assign value to class member
   eval $this[catstr]='$catStr'

   # Restore options
   eval $opts
}

function FirstClass::~FirstClass ()
###################################
# Destructor for FirstClass
#
{
   local this=$1

   # Free resources
   eval unset $this[catstr]
}

function FirstClass::wordCount ()
###################################
# Return the word count of catstr
# Arguments:
#   None
# Returns:
#   Word count on the return stack
#
{
   # For clarity
   local this=$1

   # Retrive the catstr member value
   fetch $this.catstr; RTN_pop R1

   # Run through 'wc' command, store result
   # on return stack.
   RTN_push $(wc -w &amp;lt;&amp;lt;&amp;lt;"$R1")

}

###################################
### Execution starts here #########
###################################

# Create an instance of FirstClass
new FirstClass 'Here are' '3 constructor' 'arguments'

# Pop the address of the object into a handle
RTN_pop h_fc

# Debug print object to stdout
show $h_fc

# Access a member value
fetch $h_fc.catstr; RTN_pop str

# Print member value
echo "catstr= '$str'"

# Get the word count
call $h_fc.wordCount; RTN_pop n
echo "word count= $n"

# Delete object
delete $h_fc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you find this sort of thing interesting, please check out my &lt;a href="https://github.com/jrbrtsn/bash_plus_plus"&gt;bash++&lt;/a&gt; project on Github!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>c</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Bash++ Recursion</title>
      <dc:creator>John Robertson</dc:creator>
      <pubDate>Thu, 10 Sep 2020 20:10:32 +0000</pubDate>
      <link>https://dev.to/jrbrtsn/bash-recursion-12l9</link>
      <guid>https://dev.to/jrbrtsn/bash-recursion-12l9</guid>
      <description>&lt;p&gt;In a &lt;a href="https://dev.to/jrbrtsn/bash-return-stack-1lnh"&gt;previous post&lt;/a&gt; I introduced the &lt;em&gt;return stack&lt;/em&gt; facility for &lt;strong&gt;Bash&lt;/strong&gt;. Now I present an example of how to implement recursion using this facility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash
############################################################
# Example script to demonstrate recursion without the
# use of subshells
#
# John Robertson &amp;lt;john@rrci.com&amp;gt;
# Initial release: Thu Sep 10 11:58:23 EDT 2020
#

# Halt on error, no globbing, no unbound variables
set -efu

# import return stack tools
source ../bash++

# "static" array of phrases to be assembled into
# a sentence by recurse()
declare -a Phrase_arr=(\
[0]='Now is' \
[1]='the time for' \
[2]='all good' \
[3]='men to come' \
[4]='to the aide of' \
[5]='their country'\
)

# Convenient "constant"
Phrase_arr_sz=${#Phrase_arr[@]}

function recurse ()
#######################################################
# Example recursive function works without subshells
# Arguments:
#   Recursion level
# Returns:
#   contcatenated phrases
#
{
   local lvl=$1

   # Continue recursing until out of phrases
   if (( lvl + 1 &amp;lt; Phrase_arr_sz )); then

      # Call ourself again with incremented lvl
      recurse $(( lvl + 1 ))

      # Pop the result into R1
      RTN_pop R1

      # Push concatenated result on return stack
      RTN_push "${Phrase_arr[$lvl]} $R1"

   else

      # Push final phrase on return stack
      RTN_push "${Phrase_arr[$lvl]}"

   fi
}

###################################
### Execution starts here #########
###################################

# NOTE: We'll reserve R1 R2 R3 ... global
# variables to fetch return values from
# return stack.

# Recursively assemble sentence from Phrase_arr
recurse 0

# Retrieve sentence from return stack
RTN_pop R1

# Print result
echo "Result: '$R1'"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;... and the result is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Result: 'Now is the time for all good men to come to the aide of their country'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;All files referenced here are available on &lt;a href="https://github.com/jrbrtsn/bash_plus_plus"&gt;Github&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>devops</category>
    </item>
    <item>
      <title>Bash++ Return Stack</title>
      <dc:creator>John Robertson</dc:creator>
      <pubDate>Thu, 10 Sep 2020 20:03:08 +0000</pubDate>
      <link>https://dev.to/jrbrtsn/bash-return-stack-1lnh</link>
      <guid>https://dev.to/jrbrtsn/bash-return-stack-1lnh</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;To say that &lt;strong&gt;Bash&lt;/strong&gt; has many idiosyncrasies is an understatement. One of the most troubling is the lack of an obvious way to return something besides an integer from a function without invoking the function in a subshell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subshells
&lt;/h2&gt;

&lt;p&gt;The term &lt;em&gt;subshell&lt;/em&gt; means that &lt;strong&gt;Bash&lt;/strong&gt; fork()'s a child process, and executes the function within that separate but initially identical process. If you want to write a multi-process application, this is a wonderful feature. If you simply want to get a complex return value from a function, this can be an exercise in masochism.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complex Return Values
&lt;/h2&gt;

&lt;p&gt;Again: you can already return an integer from any &lt;strong&gt;Bash&lt;/strong&gt; function without invoking a subshell; the problem is how to go about returning anything else.&lt;br&gt;
One possible way to relay the result of a function is to store it in a global variable, and then retrieve it after the function returns. There isn't a lot of immediate appeal to this strategy since a single global variable dedicated for this purpose precludes the straightforward implementation of recursion, or having any function return more than one value.&lt;/p&gt;
&lt;h2&gt;
  
  
  Return Stack
&lt;/h2&gt;

&lt;p&gt;Stacks have been used in computing forever. Implementing one in &lt;strong&gt;Bash&lt;/strong&gt; only takes 77 well-spaced lines. First off, there must be global array which can be used for this stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Global array to use as a return stack
declare -g -a __RTN_STK
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice the leading underscores to avoid namespace collisions. Next we'll need a way to push things onto this stack. Since &lt;strong&gt;Bash&lt;/strong&gt; is casual about function prototypes, we'll leave it up to the caller to decide how many items to push on our stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function RTN_push
############################################################
# Use this to push things onto the global return stack
# Arguments:
#    Whatever strings you wish placed on the stack
#
{
   # Necessary to avoid "unbound variable" exception for empty stack
   local OPTS=$(shopt -o -p nounset errexit)
   set +ue

   # For readability, store array index value here before using
   local -i ndx

   # Push arguments onto the stack
   while [[ -n "$1" ]]; do

      # Array index is current size of array
      ndx=${#__RTN_STK[@]}

      # Place argument onto stack
      __RTN_STK[$ndx]="$1"

      # Discard argument from argv
      shift

   done

   # Restore options
   eval $OPTS
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Excellent. Now we'll need a way to pop the values from the stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function RTN_pop
############################################################
# Use this to pop things off of the return stack
# Arguments:
#    Names of global varibles to be loaded by popping
#    strings from the stack.
#
{
   # Necessary to avoid "unbound variable" exception
   local OPTS=$(shopt -o -p nounset)
   set +u

   local -i arg ndx
   for (( arg= $#; arg ; --arg )); do
      ndx=${#__RTN_STK[@]}
      (( --ndx ))
      eval ${!arg}="\${__RTN_STK[\$ndx]}"
      # pop from stack, free memory
      unset __RTN_STK[$ndx]
   done

   # Restore options
   eval $OPTS
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, an example of how this may be used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash
############################################################
# Example script to demonstrate a function which returns
# multiple objects without the use of subshells
#
# John Robertson &amp;lt;john@rrci.com&amp;gt;
# Initial release: Thu Sep 10 11:58:23 EDT 2020
#

# Halt on error, no globbing, no unbound variables
set -efu

# import return stack tools
source ../bash++

function returns_3_strings ()
#######################################################
# Example function with 3 returns objects
# Arguments:
#   none
# Returns:
#  3 strings
#
{
   RTN_push 'string #1' 'string #2' 'string #3'
}

###################################
### Execution starts here #########
###################################

# NOTE: We'll reserve R1 R2 R3 ... global
# variables to fetch return values from
# return stack.

# Call our function
returns_3_strings

# Pop the results into global return "registers"
RTN_pop R1 R2 R3

# print the results
echo "R1= '$R1', R2= '$R2', R3= '$R3'"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;... and the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;R1= 'string #1', R2= 'string #2', R3= 'string #3'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Both files referenced in this article are available on &lt;a href="https://github.com/jrbrtsn/bash_plus_plus"&gt;Github&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>bash</category>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>Bash++</title>
      <dc:creator>John Robertson</dc:creator>
      <pubDate>Thu, 10 Sep 2020 19:13:05 +0000</pubDate>
      <link>https://dev.to/jrbrtsn/bash-2lga</link>
      <guid>https://dev.to/jrbrtsn/bash-2lga</guid>
      <description>&lt;p&gt;Announcing the birth of the &lt;strong&gt;Bash++&lt;/strong&gt; project on &lt;a href="https://github.com/jrbrtsn/bash_plus_plus"&gt;Github&lt;/a&gt;!&lt;br&gt;
The goal of this project is to provide accessible tools and examples for &lt;strong&gt;Bash&lt;/strong&gt; programmers who are keen to write cleaner and more efficient scripts.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>Returning an Array from a Bash Function</title>
      <dc:creator>John Robertson</dc:creator>
      <pubDate>Wed, 09 Sep 2020 18:34:13 +0000</pubDate>
      <link>https://dev.to/jrbrtsn/returning-an-array-from-a-bash-function-378a</link>
      <guid>https://dev.to/jrbrtsn/returning-an-array-from-a-bash-function-378a</guid>
      <description>&lt;p&gt;At first glance it may seem that returning an array from a &lt;strong&gt;Bash&lt;/strong&gt; function is not possible. This is not strictly true; taking a cue from the way C/C++ programmers might do this, you can pass the result array to the function by reference. This approach allows the function itself to remain free of any global variable references. Here is a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Example of how to return an array from a function
# using pass-by-reference.

# Halt on error, no globbing, no undeclared vars
set -efu

function getFnames
####################################################
# Load all filenames into an array. 
# Arguments:
#    $1 = name of return array (must be global)
#    $2 = first path for 'find' command to search
#    ...  other paths for 'find' to search
#
{
   # store the name of the global array for return.
   local h_rtnArr=$1
   # discard first argument in argv
   shift

   # mapfile does heavy lifting.  See: help mapfile
   mapfile $h_rtnArr &amp;lt; &amp;lt;(find $@ -type f)

   # TODO: return error code
}

# Global array
declare -a FnameArr

# Pass FnameArr by reference
getFnames FnameArr /boot

# List results to stdout
arrSz=${#FnameArr[@]}
for (( ndx=0; ndx &amp;lt; $arrSz; ++ndx )); do
   echo -n "${ndx}: ${FnameArr[$ndx]}"
done

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Running this script will produce an enumerated list of all files on your &lt;em&gt;/boot&lt;/em&gt; partition.&lt;br&gt;
Enjoy!&lt;/p&gt;

</description>
      <category>bash</category>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>Event Driven Bash</title>
      <dc:creator>John Robertson</dc:creator>
      <pubDate>Tue, 08 Sep 2020 16:21:27 +0000</pubDate>
      <link>https://dev.to/jrbrtsn/event-driven-bash-5a6b</link>
      <guid>https://dev.to/jrbrtsn/event-driven-bash-5a6b</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YTJ7kOVx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/plomcbcgtbr348ue5gai.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YTJ7kOVx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/plomcbcgtbr348ue5gai.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Bash&lt;/strong&gt; is very useful when it comes to automating system administration tasks.  Sometimes you need to take action based on external events, and there aren't a lot of examples of how this can be done. It's pretty straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash -eu

# Launch inotifywait monitoring the syslog in a subprocess.
# Redirect stdout of subshell to pipe #3
exec 3&amp;lt; &amp;lt;(exec inotifywait -m /var/log/syslog)

# Read each line of output from inotifywait
while read -u 3 FILE OPS; do

   # stdin, stdout, stderr all available in loop

   echo "FILE= '$FILE', OPS= '$OPS'"

   # OPS are comma separated. Swap comma for space, deal with each individually.
   for op in ${OPS//,/ }; do

      # Branch on $op
      case $op in

         MODIFY)
            echo "$FILE was modified.";;

         ACCESS)
            echo "$FILE was accessed.";;

         CLOSE_NOWRITE)
            echo "$FILE was closed without changes."
            break 2;; 

# Other actions go here
      esac
   done
done

# Close pipe
exec 3&amp;lt;&amp;amp;-

# Only get here on loop exit, or if inotifywait quits.
exit 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To exercise the script, try running it and then pulling up your syslog in a pager. When you exit the pager, your script should exit as well.&lt;br&gt;
This and other examples are available on &lt;a href="https://github.com/jrbrtsn/bash_plus_plus"&gt;Github&lt;/a&gt;.&lt;br&gt;
I'll be happy to try and answer any questions.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
