Category Archives: bash

Let the Mac Speak for Me

The Problem

I often lose my voice temporarily due to allergy. During that time, my main mode of communication is a notepad, or the electronic equivalent: Zen Brush. I was looking for a solution that can convert what I type into spoken words.

The Solution

Since I am using Mac both at home and at work, and Mac has the say command which is useful for this purpose; I decided to roll my own solution. It turned out that the solution is a very simple bash script, which I named speak4me.sh, which I saved in my ~/bin directory:

#!/bin/bash
while read line
do
    say -v Alex $line
done

To make the script executable, I issued the following command:

$ chmod +x ~/bin/speak4me.sh

Using speak4me.sh

To use speak4me.sh, from the terminal, issue the following command:

$ speak4me.sh

After that, start typing your message. As soon as you hit the return key, the script will “say” what you type. You can keep on typing and hitting return. To end the script, just type Ctrl+C.

Discussion

The script is very simple, pragmatic and free from bells and whistles, but it works the way I like it. The -v Alex part of the say command specifies a voice from Alex. OS X comes with a few voices which you can experiment with yourself. To list the voices your system has, issue the following command:

$ say -v ?
Agnes               en_US    # Isn't it nice to have a computer that will talk to you?
Albert              en_US    #  I have a frog in my throat. No, I mean a real frog!
Alex                en_US    # Most people recognize me by my voice.
Bad News            en_US    # The light you see at the end of the tunnel is the headlamp of a fast approaching train.
Bahh                en_US    # Do not pull the wool over my eyes.
Bells               en_US    # Time flies when you are having fun.
Boing               en_US    # Spring has sprung, fall has fell, winter's here and it's colder than usual.
Bruce               en_US    # I sure like being inside this fancy computer
Bubbles             en_US    # Pull the plug! I'm drowning!
Cellos              en_US    # Doo da doo da dum dee dee doodly doo dum dum dum doo da doo da doo da doo da doo da doo da doo
Deranged            en_US    # I need to go on a really long vacation.
Fred                en_US    # I sure like being inside this fancy computer
Good News           en_US    # Congratulations you just won the sweepstakes and you don't have to pay income tax again.
Hysterical          en_US    # Please stop tickling me!
Junior              en_US    # My favorite food is pizza.
Kathy               en_US    # Isn't it nice to have a computer that will talk to you?
Pipe Organ          en_US    # We must rejoice in this morbid voice.
Princess            en_US    # When I grow up I'm going to be a scientist.
Ralph               en_US    # The sum of the squares of the legs of a right triangle is equal to the square of the hypotenuse.
Trinoids            en_US    # We cannot communicate with these carbon units.
Vicki               en_US    # Isn't it nice to have a computer that will talk to you?
Victoria            en_US    # Isn't it nice to have a computer that will talk to you?
Whisper             en_US    # Pssssst, hey you, Yeah you, Who do ya think I'm talking to, the mouse?
Zarvox              en_US    # That looks like a peaceful planet.
Advertisements

Easy Way to Create Colorful Bash Prompt

The Problem

I often want to fiddle with the bash prompt, but don’t want to deal with bash prompt escape sequences. I wish for a utility which simplify setting a bash prompt. I finally wrote that utility myself: mkprompt

Install

Copy mkprompt to a directory in the path.

Using mkprompt

The best way to show mkprompt usage is via a couple of examples.

PS1=$(mkprompt "red workdir" space dollar)  
PS1=$(mkprompt "cyan Workdir" space "green dollar")  
mkprompt # display help

For more information, see my shell_tools page.

What’s Next?

The following are improvements which I plan for mkprompt, depends on my free time:

  • Implement the rest of the prompt escape sequences
  • Improve the help output
  • Implement installation script

The Script

I current host my script as part of my shell_tools collection on GitHub.

Automatically List a Directory’s Contents After Changing Dir

The Problem

After the cd command, the next command is almost always ls so we want to combine the two to automatically issuing the ls command right after the cd command.

The Solution

In bash, add the following line in either ~/.bash_profile or ~/.bashrc:

function cd() { builtin cd "${@:-$HOME}" && ls -l; }

If you are using csh or tcsh, add the following line to .cshrc:

alias cd 'cd \!*; ls -l'

Now, whenever we type a cd command, not only we are changing the work directory, but also list the files at the new location. I would like to thank Matt Jenkins for helping me out with the csh part.

Bash – Log to Screen and File Simultaneously

The Problem

In my bash script, I would like to print both the the standard output (typically the screen) and a file.

The Solution

Below is a simple script which demonstrate the solution.

#!/bin/sh

# whatis: Demo script that prints to both screen and a file

{
    echo Logging demo
    echo Output will go both the screen and logging.log
    # Other lines which might produce output here
    
} | tee logging.log

Discussion

Normally, the output from a bash script goes to the standard output, typically the screen. To redirect the output to both the standard output and a file, we employ the tee program. By surrounding the block of code with curly braces, we redirect the whole block, not just individual lines.

Restore Your SSH Session Working Directory

The Problem

I want to login to a remote Linux machine via SSH and to be in the same directory I was before my last log out.

The Solution

Since my login shell is bash, I present this solution in bash, but you can adapt it for your favorite shells. This tip relies on the two special files ~/.bash_profile and ~/.bash_logout. When a user logs out of a Linux system, the login shell (bash in this case) executes the ~/.bash_logout file, which is where we save our working directory:

# Contents of .bash_logout
rm -f $LASTDIRFILE
echo cd $PWD > $LASTDIRFILE

Likewise, we a user logs in, bash execute .bash_profile, so we put the instruction to restore the working directory there:

# Contents of .bash_profile
# ...

# Restore last directory
export LASTDIRFILE=~/.lastdir
test -f $LASTDIRFILE && source $LASTDIRFILE

Conclusion

The ~/.bash_logout is a wonderful file for saving your session’s details and its counterpart, ~/.bash_profile, is good for restoring them.

Adding Confirmation to bash

The Problem

Bash does not have a “confirm” command to solicit the user’s confirmation of an action. I realize that some commands, such as rm which can ask for the user’s confirmation via the -i flag, but many do not. In addition, when writing bash scripts, I often run into situations which requires the confirmation for a series of commands, not just a single one.

The Solution

I created a confirm command, really a bash function which we can include in our script. The interface for this function is simple: think of it as a stripped-down version of the echo command. Below is the contents of confirm.sh, where I defined the confirm function.

The Code

# ======================================================================
#
# Function: confirm
# Asks the user to confirm an action, If the user does not answer yes,
# then the script will immediately exit.
#
# Parameters:
# $@ - The confirmation message
#
# Examples:
# >  # Example 1
# >  # The preferred way to use confirm
# >  confirm Delete file1? && echo rm file1
# >  
# >  # Example 2
# >  # Use the $? variable to examine confirm's return value
# >  confirm Delete file2?
# >  if [ $? -eq 0 ]
# >  then
# >      echo Another file deleted
# >  fi
# >  
# >  # Example 3
# >  # Tell bash to exit right away if any command returns a non-zero code
# >  set -o errexit
# >  confirm Do you want to run the rest of the script?
# >  echo Here is the rest of the script
#
# ======================================================================

function confirm()
{
    echo -n "$@ "
    read -e answer
    for response in y Y yes YES Yes Sure sure SURE OK ok Ok
    do
        if [ "_$answer" == "_$response" ]
        then
            return 0
        fi
    done

    # Any answer other than the list above is considerred a "no" answer
    return 1
}

Discussion

To use the function, just save the contents of the file above and name it
confirm.sh. Before using the confirm command
in your script, include the confirm.sh:

source confirm.sh

The examples above should provide enough information to get started. For comments,
suggestions, bugs report, please post a comment to this blog post.

Sweeten Bash History by Adding Grep

The Problem

While I know about the Ctrl-R key combination in bash to perform an incremental reverse search the history; I often need to grep the history to find what I want. For example, to find out what directory I changed into, I issue the following command:

$ history | grep cd

That’s a lot of typing for a lazy guy like me. Imagine that. I rather spend my time writing this blog that repeating that command.

The Solution

To solve this problem, I created a simple function and placed it in my ~/.bash_profile file:

function h() {
    if [ -z "$1" ]
    then
        history
    else
        history | grep "$@"
    fi
}

Explanation

  • Line 2-5: If the user call the command h without any parameter, the function calls the history command
  • Line 6-7: Otherwise, issue the history command and use grep to search.

Going back to my original example, the command now becomes:

$ h cd

Clearly, this is the way life should be: short and sweet. See you in another post.