bash


Bash: How to iterate over associative array and print all key/value pairs

The values of an associative array are accessed using the following syntax ${ARRAY[@]}.

To access the keys of an associative array in bash you need to use  an exclamation point right before the name of the array: ${!ARRAY[@]}.

To iterate over the key/value pairs you can do something like the following example

# For every key in the associative array..
for KEY in "${!ARRAY[@]}"; do
  # Print the KEY value
  echo "Key: $KEY"
  # Print the VALUE attached to that KEY
  echo "Value: ${ARRAY[$KEY]}"
done

NOTE: The use of quotes around the array of keys ${!ARRAY[@]} in the for statement (plus the use of @ instead of *) is necessary in case any keys include spaces.


Bash: Determinate state of variable

A variable in bash (and any POSIX-compatible shell) will be in one of the three following states:

  • unset
  • set but empty
  • set and not empty

Variables in bash do not have data types. A variable can contain a number, a character, a string of characters or the empty string. Assigning a value to the reference of a variable will create it, you do not need to declare it beforehand.

Following, you will find some tests that allow you to find which state the variable is in. They will allow you to distinguish between ‘unset’ and ‘set but empty’ states.

# Test if VAR is 'unset' or 'set but empty'
if [ -z "${VAR}" ]; then
	echo "VAR is 'unset' or 'set but empty'";
fi

# Test if VAR is 'unset'
if [ -z "${VAR+BFNET}" ]; then
	echo "VAR is 'unset'";
fi
# Where ${VAR+BFNET} is a parameter expansion which evaluates to null if VAR is 'unset',
# if VAR is set to anything (including the empty string) the construct expands to BFNET.

# Test if VAR is 'set but empty'
if [ -z "${VAR-BFNET}" ]; then
	echo "VAR is 'set but empty'";
fi
# Where ${VAR-BFNET} is a parameter expansion which evaluates to the value of VAR if VAR is 'set' (either empty or not),
# if VAR 'unset' the construct expands to BFNET.
# This is useful for providing default values
# For example:
#function custom_echo () {
#    local DEFAULT_MSG="No message passed."; # Doesn't really need to be a local variable.
#    MESSAGE=${1:-$DEFAULT_MSG}; # Defaults to default message.
#    echo -e "$MESSAGE";
#    return;
#}
# In the above function if the user does not provide the message to be printed with the function call, then the default message will be used.

# Test if VAR is 'set and not empty'
if [ -n "${VAR}" ]; then
	echo "VAR is 'set and not empty'";
fi

Following is a truth table for the above:

                        +-------+-------+--------+
                VAR is: | unset | empty | filled |
+-----------------------+-------+-------+--------+
| [ -z "${VAR}" ]       | true  | true  | false  |
| [ -z "${VAR+BFNET}" ] | true  | false | false  |
| [ -z "${VAR-BFNET}" ] | false | true  | false  |
| [ -n "${VAR}" ]       | false | false | true   |
+-----------------------+-------+-------+--------+

HOWTO: Make Terminator Terminal Act Like Guake Terminal in Fedora 23 1

We tried to toggle the visibility of the terminator window using the default keybinding which is (Shift+Ctrl+Alt+A) and failed. Changing the configuration in the ‘Terminator Preferences’ under Keybindings to a new key-bind also did not do any good. We could not get the hide_window keybinding to work and so we could not toggle the window visibility with the keyboard.

We propose this alternative solution that requires two additional packages: xdotool and wmctrl.

In Fedora you can install them using sudo dnf install xdotool wmctrl and in Ubuntu using sudo apt-get install xdotool wmctrl

After the installation is complete, you need to paste the following code in a file and make it an executable.

e.g From a terminal issue nano ~/toggle_visibility.sh, then paste the code and hit CTRL+X to exit. When prompted if you want to save press ‘Y’ and enter.

#!/bin/bash

#The purpose of this script is to allow the user to toggle the visibility of (almost) any window.
#Please note it will work on the first match, so if there are multiple instances of an application it would be a random window of them the one to be affected.
#Usually it will control the window with the smallest PID.

#Checking that all dependencies are met, since we cannot proceed without them.
declare -a DEPENDENCIES=("xdotool" "wmctrl");
declare -a MANAGERS=("dnf" "apt-get");

for DEPENDENCY in ${DEPENDENCIES[@]}; do
    echo -n "Checking if $DEPENDENCY is available";
    if hash $DEPENDENCY 2>/dev/null; then
        echo "- OK, Found";
    else
        echo "- ERROR, Not Found in $PATH";
        for MANAGER in ${MANAGERS[@]}; do
            if hash $MANAGER 2>/dev/null; then
                echo -n "$DEPENDENCY is missing, would you like to try and install it via $MANAGER now? [Y/N] (default is Y): ";
                read ANSWER;
                if [[ "$ANSWER" == "Y" || "$ANSWER" == "y" || "$ANSWER" == "" ]]; then
                    sudo "$MANAGER" install "$DEPENDENCY";
                else
                    echo "Terminating";
                    exit -1;
                fi
            fi
        done
    fi
done

APPLICATION="$1";

#Checking if the application name provided by the user exists
if ! hash $APPLICATION 2>/dev/null; then
    echo -e "$APPLICATION does not seem to be a valid executable\nTerminating";
    exit -2;
fi

#Checking if the application is running. We are using pgrep as various application are python scripts and we will not be able to find them using pidof. pgrep will look through the currently running processes and list the process IDs of all the processes that are called $APPLICATION.
PID=$(pgrep --exact $APPLICATION | head -n 1);

#If the application is not running, we will try to launch it.
if [ -z $PID ]; then
  echo "$APPLICATION not running, launching it..";
    $APPLICATION;
else
    #Since the application has a live instance, we can proceed with the rest of the code.
    #We will get the PID of the application that is currently focused, if it is not the application we passed as parameter we will change the focus to that. In the other case, we will minimize the application.
  echo -n "$APPLICATION instance found - ";
    FOCUSED=$(xdotool getactivewindow getwindowpid);
    if [[ $PID == $FOCUSED ]]; then
    echo "It was focused so we are minimizing it";
        #We minimize the active window which we know in this case that it is the application we passed as parameter.
        xdotool getactivewindow windowminimize;
    else
    echo "We are setting the focus on it";
        #We set the focus to the application we passed as parameter. If it is minimized it will be raised as well.
        wmctrl -x -R $APPLICATION;
    fi
fi

exit 0

Afterwards, you need to make the script an executable so you should issue chmod +x ~/toggle_visibility.sh to do that.

Then, execute ~/toggle_visibility.sh in your terminal once. We need to do that in order to install any missing dependencies for the tool.

Finally, you need to create a custom shortcut that will call the script using the key combination you like at any point.

For Fedora,

  1. Issue the following in a terminal gnome-control-panel to start the gnome control panel.
  2. In the newly appeared window, click on the ‘keyboard’ icon that is in the category ‘Hardware’.
  3. After that, click on the tab ‘Shortcuts’
  4. and on the left list, click on custom shortcuts.
  5. You will see a button with the + sign, click that.
  6. In the dialog box that will appear enter the following:
    – In the name field enter anything you like. e.g ‘Toggle Terminator Visibility’
    – In the command field enter ‘/home/<USER>/toggle_visibility.sh terminator’ where user enter your own username.
    – Click apply.
  7. You will see a new row with two columns with the name you just set in the first column. Click on the second column, where it should say ‘Disabled’ and the press the key combination you want for toggling terminator e.g F12

For Ubuntu, go to System Settings and follow the same procedure after step 2.

You are ready to go 🙂

Just try the key combination you just provided and terminator will appear in front of you. Pressing it once more it will hide it.


Tail logs with color for Monolog 1

#1. Copy/paste the below lines in your .bashrc

tailf-with-colors () {
    if [ -z "$1" ] ; then
        echo "Please specify a file for monitoring"
        return
    fi

    tail -f $1 | awk '
                {matched=0}
                /INFO:/    {matched=1; print "\033[0;37m" $0 "\033[0m"}   # WHITE
                /NOTICE:/  {matched=1; print "\033[0;36m" $0 "\033[0m"}   # CYAN
                /WARNING:/ {matched=1; print "\033[0;34m" $0 "\033[0m"}   # BLUE
                /ERROR:/   {matched=1; print "\033[0;31m" $0 "\033[0m"}   # RED
                /ALERT:/   {matched=1; print "\033[0;35m" $0 "\033[0m"}   # PURPLE
                matched==0            {print "\033[0;33m" $0 "\033[0m"}   # YELLOW
        '
}

#2. Run source ~/.bashrc -- to reload 

#3. Run tailf-with-colors <filename>