10 Tools To Add Some Spice To Your UNIX Shell Scripts

Posted on in Categories Linux, Shell scripting, UNIX last updated November 8, 2013

There are some misconceptions that shell scripts are only for a CLI environment. You can easily use various tools to write GUI and/or network (socket) scripts under KDE or Gnome desktops. Shell scripts can make use of some of the GUI widget (menus, warning boxs, progress bars etc). You can always control the final output, cursor position on screen, various output effects, and so on. With the following tools you can build powerful, interactive, user friendly UNIX / Linux bash shell scripts.

Creating GUI application is not just expensive task but task that takes time and patience. Luckily, both UNIX and Linux ships with plenty of tools to write beautiful GUI scripts. The following tools are tested on FreeBSD and Linux operating systems but should work under other UNIX like operating systems.

#1: notify-send Command

The notify-send command allows you to send desktop notifications to the user via a notification daemon from the command line. This is useful to inform the desktop user about an event or display some form of information without getting in the user’s way. You need to install the following package:
$ sudo apt-get install libnotify-bin
In this example, send simple desktop notification from the command line, enter:

notify-send "rsnapshot done :)"

Sample outputs:

Fig:01: notify-send in action
Fig:01: notify-send in action

Here is another code with additional options:

....
alert=18000
live=$(lynx --dump http://money.rediff.com/ | grep 'BSE LIVE' | awk '{ print $5}' | sed 's/,//g;s/\.[0-9]*//g')
[ $notify_counter -eq 0 ] && [ $live -ge $alert ] && { notify-send -t 5000 -u low -i   "BSE Sensex touched 18k";  notify_counter=1; }
...

Sample outputs:

Fig.02: notify-send with timeouts and other options
Fig.02: notify-send with timeouts and other options

Where,

  • -t 5000: Specifies the timeout in milliseconds ( 5000 milliseconds = 5 seconds)
  • -u low : Set the urgency level (i.e. low, normal, or critical).
  • -i gtk-dialog-info : Set an icon filename or stock icon to display (you can set path as -i /path/to/your-icon.png).

For more information on use of the notify-send utility, please refer to the notify-send man page, viewable by typing man notify-send from the command line:
man notify-send

#2: tput Command

The tput command is used to set terminal features. With tput you can set:

  • Move the cursor around the screen.
  • Get information about terminal.
  • Set colors (background and foreground).
  • Set bold mode.
  • Set reverse mode and much more.

Here is a sample code:

#!/bin/bash
 
# clear the screen
tput clear
 
# Move cursor to screen location X,Y (top left is 0,0)
tput cup 3 15
 
# Set a foreground colour using ANSI escape
tput setaf 3
echo "XYX Corp LTD."
tput sgr0
 
tput cup 5 17
# Set reverse video mode
tput rev
echo "M A I N - M E N U" 
tput sgr0
 
tput cup 7 15
echo "1. User Management"
 
tput cup 8 15
echo "2. Service Management"
 
tput cup 9 15
echo "3. Process Management"
 
tput cup 10 15
echo "4. Backup"
 
# Set bold mode 
tput bold
tput cup 12 15
read -p "Enter your choice [1-4] " choice
 
tput clear
tput sgr0
tput rc

Sample outputs:

Fig.03: tput in action
Fig.03: tput in action

For more detail concerning the tput command, see the following man page:
man 5 terminfo
man tput

#3: setleds Command

The setleds command allows you to set the keyboard leds. In this example, set NumLock on:

setleds -D +num

To turn it off NumLock, enter:

setleds -D -num
  • -caps : Clear CapsLock.
  • +caps : Set CapsLock.
  • -scroll : Clear ScrollLock.
  • +scroll : Set ScrollLock.

See setleds command man page for more information and options:
man setleds

#4: zenity Command

The zenity commadn will display GTK+ dialogs box, and return the users input. This allows you to present information, and ask for information from the user, from all manner of shell scripts. Here is a sample GUI client for the whois directory service for given domain name:

#!/bin/bash
# Get domain name 
_zenity="/usr/bin/zenity"
_out="/tmp/whois.output.$$"
domain=$(${_zenity} --title  "Enter domain" \
	            --entry --text "Enter the domain you would like to see whois info" )
 
if [ $? -eq 0 ]
then
  # Display a progress dialog while searching whois database
  whois $domain  | tee >(${_zenity} --width=200 --height=100 \
  				    --title="whois" --progress \
			            --pulsate --text="Searching domain info..." \
                                    --auto-kill --auto-close \
                                    --percentage=10) >${_out}
 
  # Display back output
  ${_zenity} --width=800 --height=600  \
	     --title "Whois info for $domain" \
	     --text-info --filename="${_out}"
else
  ${_zenity} --error \
	     --text="No input provided"
fi

Sample outputs:

Fig.04: zenity in Action
Fig.04: zenity in Action

See the zenity man page for more information and all other supports GTK+ widgets:
zenity --help
man zenity

#5: kdialog Command

kdialog is just like zenity but it is designed for KDE desktop / qt apps. You can display dialogs using kdialog. The following will display message on screen:

kdialog --dontagain myscript:nofilemsg --msgbox "File: '~/.backup/config' not found."

Sample outputs:

Fig.05: Suppressing the display of a dialog
Fig.05: Suppressing the display of a dialog

See shell scripting with KDE Dialogs tutorial for more information.

#6: Dialog

Dialog is an application used in shell scripts which displays text user interface widgets. It uses the curses or ncurses library. Here is a sample code:

#!/bin/bash
dialog --title "Delete file" \
--backtitle "Linux Shell Script Tutorial Example" \
--yesno "Are you sure you want to permanently delete \"/tmp/foo.txt\"?" 7 60
 
# Get exit status
# 0 means user hit [yes] button.
# 1 means user hit [no] button.
# 255 means user hit [Esc] key.
response=$?
case $response in
   0) echo "File deleted.";;
   1) echo "File not deleted.";;
   255) echo "[ESC] key pressed.";;
esac

See the dialog man page for details:
man dialog

A Note About Other User Interface Widgets Tools

UNIX and Linux comes with lots of other tools to display and control apps from the command line, and shell scripts can make use of some of the KDE / Gnome / X widget set:

  • gmessage – a GTK-based xmessage clone.
  • xmessage – display a message or query in a window (X-based /bin/echo)
  • whiptail – display dialog boxes from shell scripts
  • python-dialog – Python module for making simple Text/Console-mode user interfaces

#7: logger command

The logger command writes entries in the system log file such as /var/log/messages. It provides a shell command interface to the syslog system log module:

logger "MySQL database backup failed."
tail -f /var/log/messages
logger -t mysqld -p daemon.error "Database Server failed"
tail -f /var/log/syslog

Sample outputs:

Apr 20 00:11:45 vivek-desktop kernel: [38600.515354] CPU0: Temperature/speed normal
Apr 20 00:12:20 vivek-desktop mysqld: Database Server failed

See howto write message to a syslog / log file for more information. Alternatively, you can see the logger man page for details:
man logger

#8: setterm Command

The setterm command can set various terminal attributes. In this example, force screen to turn black in 15 minutes. Monitor standby will occur at 60 minutes:

setterm -blank 15 -powersave powerdown -powerdown 60

In this example show underlined text for xterm window:

setterm -underline on; 
echo "Add Your Important Message Here"
setterm -underline off

Another useful option is to turn on or off cursor:

setterm -cursor off

Turn it on:

setterm -cursor on

See the setterm command man page for details:
man setterm

#9: smbclient: Sending Messages To MS-Windows Workstations

The smbclient command can talk to an SMB/CIFS server. It can send a message to selected users or all users on MS-Windows systems:

smbclient -M WinXPPro <<EOF
Message 1
Message 2
...
..
EOF

OR

echo "${Message}" | smbclient -M salesguy2

See smbclient man page or read our previous post about “sending a message to Windows Workstation” with smbclient command:
man smbclient

#10: Bash Socket Programming

Under bash you can open a socket to pass some data through it. You don’t have to use curl or lynx commands to just grab data from remote server. Bash comes with two special device files which can be used to open network sockets. From the bash man page:

  1. /dev/tcp/host/port – If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket.
  2. /dev/udp/host/port – If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open a UDP connection to the corresponding socket.

You can use this technquie to dermine if port is open or closed on local or remote server without using nmap or other port scanner:

# find out if TCP port 25 open or not
(echo >/dev/tcp/localhost/25) &>/dev/null && echo "TCP port 25 open" || echo "TCP port 25 close"

You can use bash loop and find out open ports with the snippets:

echo "Scanning TCP ports..."
for p in {1..1023}
do 
  (echo >/dev/tcp/localhost/$p) >/dev/null 2>&1 && echo "$p open" 
done

Sample outputs:

Scanning TCP ports...
22 open
53 open
80 open
139 open
445 open
631 open

In this example, your bash script act as an HTTP client:

#!/bin/bash
exec 3<> /dev/tcp/${1:-www.cyberciti.biz}/80
 
printf "GET / HTTP/1.0\r\n" >&3
printf "Accept: text/html, text/plain\r\n" >&3
printf "Accept-Language: en\r\n" >&3
printf "User-Agent: nixCraft_BashScript v.%s\r\n" "${BASH_VERSION}"   >&3
printf "\r\n" >&3
 
while read LINE <&3 
do
   # do something on $LINE 
   # or send $LINE to grep or awk for grabbing data
   # or simply display back data with echo command 
   echo $LINE	
done

See the bash man page for more information:
man bash

A Note About GUI Tools and Cronjob

You need to request local display/input service using export DISPLAY=[user’s machine]:0 command if you are using cronjob to call your scripts. For example, call /home/vivek/scripts/monitor.stock.sh as follows which uses zenity tool:
@hourly DISPLAY=:0.0 /home/vivek/scripts/monitor.stock.sh

Have a favorite UNIX tool to spice up shell script? Share it in the comments below.

111 comment

    1. Maybe you were using dash (Ubuntu’s /bin/sh) not bash. Try it with bash!
      As for grep | awk, I like it. Pipelines with lots of simple components are fine, and it’s easy to understand. The person who stopped reading missed a great article.

      1. Output from “man bash | grep -A 12 tcp | head -11”:

        /dev/tcp/host/port
        If host is a valid hostname or Internet address, and port
        is an integer port number or service name, bash attempts
        to open a TCP connection to the corresponding socket.
        /dev/udp/host/port
        If host is a valid hostname or Internet address, and port
        is an integer port number or service name, bash attempts
        to open a UDP connection to the corresponding socket.

        NOTE: Bash, as packaged for Debian, does not support using the /dev/tcp
        and /dev/udp files.

        So, as you see, Debian (and thus, Ubuntu) can’t make use of #10 …

        1. Ubuntu v9.10 user here :)

          echo $BASH_VERSION
          4.0.33(1)-release
          lsb_release -d
          Description: Ubuntu 9.10
          echo >/dev/tcp/localhost/22 && echo "ssh open"
          ssh open

    2. The Debian version of bash has the socket capability removed.

      The Mandrive version has the builtin time command removed.

      There may be other stupidites perormed by distros. That’s why I always build bash from source.

    3. Debian built version of bash had the socket feature removed, because they decided, that netcat(1) provides the same feature (and probably more secure implementation wise).

      Pretty much any distros based on Debian, who take the source from them, has the feature disabled.

      And personally I don’t blame them, proper networking is a major headache to do right, can be a security hazard, and it’s more or less outside of the scope of what a shell should do. And if you want something with cryptography, you need those tools too.

      *goes back to work grumbling about the damn kids an their music*

    1. Grace us who are unworthy with your grepless/awkless version. I suppose you use perl, which is *never* required. Or do you apt-get some one-off program that just barley works on one particular Ubunt^W Linux distribution…

        1. Sure it WOULD do the job fine. But it also is not particularly better than the initial version (also it still uses grep.) I don’t know what CC is complianing about, grep and awk are perhaps not REQUIRED, but so what? UNIX gives multiple ways to get almost anything done, so the programmer can chose the one they prefer. I see 0 problems with using grep & awk if the user desires. I must admit, Eugene, I like to use tr & cut too 8-).
          Quite nice article!

        2. Not quite. The idea of CC was that
          grep ‘BSE LIVE’ | awk ‘{ print $5}’
          could by writed using only awk:
          awk ‘/BSE LIVE/{ print $5}’

          BTW one more version:
          lynx –dump http://money.rediff.com/ | awk ‘/BSE LIVE/{ print $5}’ | tr -d “,” | cut -d’.’ -f1

          1. You can short it further:

            lynx --dump http://money.rediff.com/ | awk '/BSE LIVE/{ gsub(/,|\.[0-9]+/, ""); print $5}'

            I can even ignore lynx and use bash sockets. There are multiple ways to solve the same problem.

            1. One more version. Maybe not so short, but in pure sed

              #!/bin/bash
              cat >cmd.sed<<__EOF__
              /BSE LIVE/{
              	s/,//g
              	#   [20]Update Now BSE LIVE      17573.99     +101.43    +0.58%
              	#^-------------1---------------^ ^-2-^.^-----------3-----------^
              	s/\(.*\)\ \([0-9]*\)\.\([0-9]*\ .*\)/\2/g
              	p
              }
              __EOF__
              lynx --dump http://money.rediff.com/ | sed -n -f cmd.sed
              
    2. As a sysadmin of multiple architectures, portability is generally my main concern. Sure each shell has special features, but my scripts have to run in ksh, bash, and sh as required. Pipelines with grep/sed/awk make my job easier and allows my scripts run in more than just one brand of shell.

    3. Thanks you stopped there — why dont you left w/o writing a comment.
      It seems your only purpose is to write comment! God may forgive u r sins.

  1. You’re biggest mistake, I think, is in assuming programs should communicate with end-users. I make scripts to do things I don’t want to have to deal with manually AT ALL. I don’t want to know when they run, what they’re doing, or if something went wrong. I want them to take care of it all for me and just keep things happy. If I personally was involved in what my scripts were doing I’d be wasting hours a day. If something really bad happens I’ll know because my system loggers will start bugging the crap out of me. (Really fun when a chain of errors spreads across many systems – oooh look 100,000 messages waiting in my inbox.)

    1. Wait wait even Bash has builtin function in order to read variables from stdin.

      Scripting language can be used for a wide range of uses even to interact with the users

  2. Great stuff. I am not quite yet using *nix, so wanted to download the PDF version. The attempt got me a 404 error.

  3. Basically a Linux list for the most part, not unix. /bin/bash gives it away (and the bashisms in the zenity example), also *bash* doesn’t come with /dev/{tcp,udp}, that is Linux.

    1. Wrong, Jay.
      A simple “man bash” followed by /tcp shows it. Unfortunately, it doesn’t work under Debian/Ubuntu, since this functionality is disabled by the Debian maintainer of bash.

  4. Kind of annoyed that of the 9 utilities listed, only 3 of them come with Mac OS X, which, unlike your test systems, is an actual UNIX-certified system. In other words, it was implied that these were standardized utilities, when they’re not. Oh well, I’m sure I can still find them if I really need them. =/

    1. Unfortunately Mac OS runs a nasty crippled version of Unix instead of Linux inside. Is highly frustrating sometimes. At least it isn’t as retarded as trying to use AIX. Using AIX makes me want to install Linux really REALLY badly but sadly it’s required for the service I run on it. I always wonder what is wrong with these other Unix platforms that they haven’t adopted all the improvements that have made it into Linux and the GNU tools.

      1. Mike, AIX is the most powerful unix platform I’ve ever worked with. I don’t want to get into semi-religious arguments, and I like linux too, but when it comes to servers stability, hardware management, workload management, etc.. it’s leading operating system if not the best.

        Sure, it lacks some of the GNU tools by default but you can find almost everything you need on AIX Linux Toolbox CD. Anyway I think AIX is mainly used in servers markets, so desktop utilities are usually irrelevant.

        And I’m not working for IBM, nor got any shares :)

    2. Mac OS X is missing a lot of commands you would expect to find in both Linux and *BSD out of the box. watch is the one I miss the most when working on OS X.

      Fact of the matter is that OS X is Unix certified (with 10.5 or higher on x86 hardware ONLY), but what does that really mean if the tools you expect from a standard *nix distribution aren’t there by default? (And yes, I know how to get them all with fink, macports or compiling them myself.)

    3. hey, AIX is awesome.. runs rings around any other OS… try and find me a OS where you can dynamically add and remove CPU and memory and storage on the fly without having to stop/start apps. And the hardware is awesome.
      And ksh is as good as any other shell, it has internal arithmatic and can perform nested loops.
      I’d like to hear you come up with a better real UNIX OS that can do what AIX and pSeries hardware can do…

      Oh, BTW, I liked this article.

      1. “try and find me a OS where you can dynamically add and remove CPU and memory and storage on the fly without having to stop/start apps.”

        You mean like Linux?

        CPU hotplug: http://www.cyberciti.biz/faq/debian-rhel-centos-redhat-suse-hotplug-cpu/
        RAM hotplug: http://lhms.sourceforge.net/
        Storage hotswap/hotplug is pretty standard in modern operating systems.

        Granted, all three of the above are hardware-dependent, but it is indeed possible under Linux so long as the necessary kernel support is compiled in.

  5. “A Note About GUI Tools and Cronjob” – In order to run zenity from cron, at least under Ubuntu, setting DISPLAY didn’t work for me, I had to set LANG as well…

    The list is nice indeed. I would’ve added bash built-in select command as well.

  6. Great article!

    I miss a really useful command from 4DOS in linux: “select”

    With “select” you could see a (curses) list of files, select some of them and run a command using the list as parameter.

    Perhaps this feature exists, but I haven’t found it yet.

    1. type “help select” into the bash shell:

      select: select NAME [in WORDS … ;] do COMMANDS; done

      The WORDS are expanded, generating a list of words. The
      set of expanded words is printed on the standard error, each
      preceded by a number. If `in WORDS’ is not present, `in “$@”‘
      is assumed. The PS3 prompt is then displayed and a line read
      from the standard input. If the line consists of the number
      corresponding to one of the displayed words, then NAME is set
      to that word. If the line is empty, WORDS and the prompt are
      redisplayed. If EOF is read, the command completes. Any other
      value read causes NAME to be set to null. The line read is saved
      in the variable REPLY. COMMANDS are executed after each selection
      until a break command is executed.

      Exit Status:
      Returns the status of the last command executed.

  7. some guys are just suckers. someone has made an effort and pointing us in the right direction which can open up a new world of possibilities. But some cold stiff upper lipped people just want to play down the efforts. if you cant do it better, then just shut up ! kudos to vivek for putting in the effort.

    1. My thoughts exactly! Why are these gurus wasting their time here in stead of creating a blog of their own and putting their link up here to share their vast knowledge with the rest of the plebs.

      Vivek must have been reading my thoughts, I was thinking about this this week, made my life a lot easier… Esp. the KDE link opened up a huge list of possibilities, so it seems.

      Thanks Vivek!

  8. The setleds command only works for console sessions. It doesn’t work from gnome-terminal. Who uses console sessions these days?

    1. I do a lot these days, while creating a secure X terminal distro. My only entry point to the terminal’s OS is with the tty1-6.

    2. My guess is probably about 99% of sys admins out there who need to manage multiple *NIX servers and do not want to do so by logging in via a GUI in every single machine? Just a thought.

      1. You can setup a GUI workstation (say CentOS with Gnome) and write a small script app that allows you to select remote server and run bunch of commands (on selected remote server) to find uptime, top memory consuming process, real time logs and much more.

    3. Everybody maintaining remote servers through ssh (Yes I know you can tunel VNC through ssh, but seriously get a bad link and its painful) + some server distros don’t even come with a default x session. generally system admisitration is MUCH quicker from the console if you know what your doing.

      1. A lot of people that are taking their first steps in the Linux world are using the GUI.

        This is something I observed for myself. I am also doing more and more through the console, but it took me a few years to get there.

        As for remote X sessions, I find NX very useful and very performant, even using slow links. All it takes is installing the software, enter one config command, DL the client, and you’re good to go. It uses SSH as transport medium, so you can use all the goodies that SSH provides out-of-the-box.

  9. I created a simple util that graphs a stream of numbers using a simple cursors based script (python that calls tput). awk/perl/etc.. a column and either tail to watch the current state, or cat a whole file to see a historical view.

    Link [code.activestate.com]

  10. @Vivek,

    Very intersting post. I have been using some of those tools already but not in the ways you have demonstated.

    I am very fond of:
    #4: zenity Command

    Thanks.
    jaysunn

  11. for #10 can we use this script to scan a remote host. if so what are the changes we need to make.bcoz this quite a good way and faster one!!!

    good idea vivek!!

  12. I do not like “zenity”. For the sole reason that it breaks compatibility to *all* the other dialog utilitys. Yes, kdialog, gdialog, xdialog, dialog & whiptail all share the same basic commandline switches. Only the zenity developers choose to break the standard. Let’s remind ourselves that destandardization is never a good thing; as open source users we should never forget that.

  13. Great article, Vivek! And timely, too. I don’t get a chance to script often in my position, but I just began working on a account management menu for another group to use. A lot of these tips will get some use from me.

    Thanks again!

    1. I once wrote a bash script that would parse old BBS-style “music” scripts (used by a certain portal or pager software I think) and play them using beep. I then used it for paging me when long jobs were done or someone wanted my attention.

  14. To CC,
    actually grep | awk | sed can be rolled up into a single command…
    awk '/BSE LIVE/ { gsub(",", "", $5); gsug("\.[0-9]", "", $5); print $5}'
    or this…
    sed -n -e '/BSE LIVE/{s/.* .* \(.*\)\.[0-9]*/\1/;s/,//gp}'

    1. I think the original:
      grep ‘BSE LIVE’ | awk ‘{ print $5}’ | sed ‘s/,//g;s/\.[0-9]*//g’
      is shorter and more intelligible. Plain awk is okay too, but please, not sed for this!
      I’m a fan of highly pipelined parallel processing, with very simple components. It’s a good way to program, although unix might not be able to implement this super-efficiently, and the shell is not the best possible language for it.

  15. Very nice guide. You might like to know that in the notify-send example with the -i flag, the gtk-dialog-info parameter is missing (but is still explained below).

  16. For zsh lovers out there,

    Even though zsh does not have the /dev/tcp fake device file, it has a similar feature through a loadable module zsh/net/tcp. You can use “autoload -U tcp_open” and then consult the manual page – “man zshtcpsys” – for details on this very powerful subsystem. You can handle the tcp connections in several ways, even using a file descriptor, or alternatively with the commands tcp_read and tcp_send and then tcp_close. You can also use “tcp_expect” for expect-like functionality.

  17. Even though zsh does not have the /dev/tcp fake device file, it has a similar feature through a loadable module zsh/net/tcp. You can use “autoload -U tcp_open” and then consult the manual page – “man zshtcpsys” – for details on this very powerful subsystem. You can handle the tcp connections in several ways, even using a file descriptor, or alternatively with the commands tcp_read and tcp_send and then tcp_close. You can also use “tcp_expect” for expect-like functionality.
    +1

  18. #11
    Do you know how to open bidirectional channel to any device from a terminal?
    Just use socat utility. With it’s help you can communicate with (for example) your printer and send it PostScript directly, to print white or black pages for example.

  19. I’m noob in this and i had problems to find documentation over how create windows
    this tutorial has given me more possibilities to programing in linux shell

    Thanks very much!

    Pd.
    do you have anything over the command expect ?
    I have not completely understand this command

  20. Vivek, you are great! this article has helped me a lot! is there something else I can read on bash socket programming?

  21. Hi Vivek,

    Thank your for very useful article. In the first example I had to add “info” to run the second script:

    $ […] notify-send -t 5000 -u low -i info “BSE Sensex touched 18k […]

    Otherwise the “no summary” error was reported by bash (Ubuntu 12.04 LTS).

    Best regards

Leave a Comment