Text

To Find by Content

You’re usually looking for something in a file. Some phrase or word, and you don’t know where the file is at or what you named it.

For occasional search through file content at the command line you’d use grep. Something like:

grep --ignore-case --files-with-matches --recursive foo /some/file/path

This can take quite a while. There are some tweaks to grep you can add, but for source code it’s become fashionable to use use ripgrep or ugrep.

sudo apt install ugrep

# a drop-in for grep in most cases
ug --ignore-case --files-with-matches --recursive foo /some/file/path

You can also use ugrep interactively with the -Q option. Though you may prefer a whiptail style menu - see the bottom.

To Find by File Name

If you know the name of the file you want, it’s usually a matter of using the find utility or locate.

find /some/place -iname "*something*"

On large filesystems, the locate utility is much faster, as it pre-builds a database.

# 'plocate' is the modern replacement for the locate utility
sudo apt install plocate

# a systemd timer updates this database daily, but to use immediately after install you must:
sudo updatedb

sudo locate -i "*something*"

You can also use a utility like fzf which uses some fuzzy name matching and includes shell integration. This has become more popular recently.

How to Build a Menu Around This

This isn’t about search per se, but this is a great place to mention whiptail, a text user interface. This let’s you wrap a TUI around your search regardless what tool you use.

sudo apt instal whiptail

# We'll name our search utility 'f'
vi ~/bin/f
#!/bin/bash

[ -z "$1" ] && { echo "You must supply a search term."; exit; }

# Pipe the output of the search to a while loop that will build two arrays; One is a list
# of files and the other a list of those file's basenames we can use for a pretty menu.
# We'll add numbers to the menu array so that when the user chooses a basename, we will
# know what filename to pull from the other array.

FILENUMBER=0
FILENAME=
LIST=()
MENU=()
LOCATION=/home/allen/projects

ug --ignore-case --files-with-matches --recursive "$1"  $LOCATION  | \
{
while read FILENAME; do
	LIST=( "${LIST[@]}" "$FILENAME" )

	DATE=$(stat -c '%.10y' "$FILENAME")
	BASENAME=$(basename "$FILENAME")
	MENU=( "${MENU[@]}" "$FILENUMBER" "$DATE $BASENAME" )

	((FILENUMBER++))
done

# If there are no results
[ -z "${LIST[0]}" ] && { echo "No results"; exit; }

# Return the filenumber from the user's choice so we can access that array element
while [ 1 ]; do
	KEY=$( whiptail --title "Search Results" --notags --menu "Search Results" 25 78 16 "${MENU[@]}" 3>&1 1>&2 2>&3 )
	[ -z $KEY ] && exit
	less "${LIST[$KEY]}"
done

} # The braces are required so that the while loops can modify 'global' variables

Notes

Why don’t we index the file contents?

You’d think an index would be great - but it turns out for unstructured text files you’re indexing every word in every file, making the index is larger than the things it’s indexing.

Though lucene/elasticseach and spinx come up in conversation.


Last modified April 3, 2026: Added whiptail and find details (bfa2fb4)