Bash Automation and Scripting Basics (Part 2) – CloudSavvy IT


    Shutterstock/Mopic

    Bash is an ideal shell and coding language, allowing you to create high-end automation scripts. In this second part of series we will look at coding density, inline comments, and correct variable quoting using single or double quotes.

    Bash Automation and Scripting Basics

    If you have not done so, and are just starting in Bash, please read our Bash Automation and Scripting Basics Part 1 article. This is the second article in our three part series on Bash automation and scripting basics. In today’s article we will look, amongst other topics, at Bash coding density and it’s connection with developer preference. We will also look at inline comments in connection with this.

    Once we start creating small scripts, exploring variables and working with text strings, we quickly realize that there may be a conceptual difference between single and double quotes. There is, and in the second topic below we’ll dive into this.

    Bash coding density and terseness

    The Bash coding language can be very dense and terse, yet it can also be spacious and verbose. It depends to a good extent on developer preference.

    For example, the following Bash code:

    true || echo 'untrue'
    

    Which can be read as Do nothing, and do so successfully (exit code 0), and if that should fail (you may read the Bash idiom || as OR) output the text ‘untrue’, can also be written as:

    if [ ! true ]; then
      echo 'untrue'
    fi
    

    Which renders the code a little different, but basically does the same.

    This in turn can be read as If true is not (signified by the ! idiom) true, then output the text ‘untrue’.

    Two ways to code in Bash; same functionality, quite different code

    Both mini-scripts result in the same empty output, as true is not untrue.

    The multitude of commands and tools available (or installable) from and at the command line further magnify the possible offset between highly readable scripts and hard to understand, dense and terse code.

    Whereas the above examples are short and relatively easy to understand, when you create long one-liner (a term often used amongst Bash developers to indicate a piece of code, consisting of multiple commands, written in a single line) employing many of such commands, versus putting the same in a more verbose script, the difference becomes clearer. Consider:

    V="$(sleep 2 & fg; echo -n '1' | sed 's|[0-9]|a|')" && echo "${V}" | sed 's|[a-z]|2|g' || echo 'fail'
    

    A complex oneliner example

    This is a typical Bash one-liner script which uses the commands sleep, fg, echo, and sed as well as various Bash idioms and regular expressions to basically sleep 2 seconds, output some text and transform this text by using regular expressions. The script also regularly checks on conditions/results of previous commands by using the Bash idioms || (if not successful, do what follows) and && (if successful, do what follows)

    I translated this, with approximate matching functionality, to a fuller script, with some modifications. For example we swapped our fg (bring the sleep command placed into background back into the foreground, and wait for it to terminate as normal non-background processes) to wait which will wait for the PID of the sleep (started by eval and captured by using the Bash idiom $!) to terminate.

    #!/bin/bash
    
    CMD="sleep 2"
    eval ${CMD} &
    wait $!
    EXIT_CODE=${?}
    
    V="$(echo -e "${CMD}n1" | sed 's|[0-9]|a|')"
    
    if [ ${EXIT_CODE} -eq 0 ]; then
      echo "${V}" | sed 's|[a-z]|2|g'
      EXIT_CODE=${?}
      if [ ${EXIT_CODE} -ne 0 ]; then
        echo 'fail'
      fi
    fi
    

    The same complex oneline in a full script instead

    Quite a difference! And this is just one developer writing it in his way. Bash code written by others tends to be somewhat challenging to read, and such difficulty rapidly increases with density and terseness. Still, an expert level developer in Bash will quickly understand even highly dense and terse code written by others, with some exceptions, for example regular expressions.

    To learn more about writing regular expressions, have a look at How to Modify Text Using Regular Expressions With the sed Stream Editor.

    From these examples it is clear that your mileage will vary over time. In general however, coder peer friendliness (by writing highly readable code) is recommended whenever you start developing Bash scripts.

    If you have to create dense and terse code, you can provide plenty of inline comments. A line prefixed by a # is considered a comment/remark line, and the symbol # can even be used towards the end of a line after any executable command, to post a suffix comment explaining the command, conditional statement, etc. For example:

    # This code will sleep one second, twice
    sleep 1  # First sleep
    sleep 1  # Second sleep
    

    Single Quotes or Double Quotes?

    In Bash, text between single quotes (') are taken as literal text by the Bash interpreter, whereas text between double quotes (") is interpreted (parsed) by the Bash interpreter. Whereas the difference in how things work may not be immediately clear from this definition, the following example shows us what happens when we interchange ' for " and vice versa:

    echo ' $(echo "Hello world") '
    echo " $(echo 'Hello world') "
    

    Single quotes versus double quotes in Bash in a hello world example

    In the first example, the text $(echo "Hello world") is seen as literal text, and so the output is simply $(echo "Hello world") . For the second example however, the output is Hello world .

    The Bash interpreter parsed the text between double quotes to see if it would find any special Bash Idioms to act upon. One such idioms was found in $( ... ) which basically starts a subshell and executes whatever is present between the ( ... ) idioms. Think about it as a shell within a shell – a subshell – executing whatever you pass to it as a fully new command. Output of any such command or commands is then passed back to the top level shell and inserted at the exact place where the subshell was started.

    Thus, our subshell executed echo 'Hello world' of which the output is Hello world. Once this was done, the subshell terminated, and the text Hello world was inserted instead of the $( ... ) subshell invocation (think about it like all of the $( ... ) code is being replaced by whatever output the subshell generated.

    The result, for the top shell, is the following command: echo " Hello world ", of which the output is Hello world as we saw.

    Note that we changed the double quotes inside the subshell to single quotes. This is not necessary! One would expect to see a parsing error when a command takes the syntax of echo " ... " ... " ... ", in that the second double quotes would terminate the first one, followed by more test, and thus leading to an error. This is not the case however.

    And this is not because Bash is flexible with multi-quoted strings (it accepts echo 'test'"More test"'test' quite happily for example), but because the subshell is a shell all by itself, and thus double quotes can be used, again, inside the subshell. Let’s prove this with an additional example:

    echo "$(echo "$(echo "more double quotes")")"
    

    Bash readily accepts repeated double quotes inside a subshell

    This will work fine and will produce the output more double quotes. The two nested subshells (running within the main shell from which you are executing this) each in turn accept double quotes and no errors are generated, even if multiple double quotes are nested across the overall one-liner command. This shows some of the programming power of Bash.

    In Summary

    Having explored coding density, we realize the value of writing highly readable code. If we have to make dense and terse code nonetheless, we can add plenty of inline comments using # to aid readability. We looked at single and double quotes and how their functionality differs quite substantially. We also briefly looked at inline comments in scripts, as well as subshell functionality when executed from within a double quoted string. Finally we saw how a subshell can use another set of double quotes without thereby affecting the double quotes used at a higher level in any way. Stay tuned for part 3!



    Source link

    Previous articleApple Car LiDAR sensor report just tipped release date
    Next articleThis fake Google Alert wants to trick you into thinking Flash is still a thing