Search code examples
bashpalindrome

Printing all palindromes from text file


I looked at this question: BASH Palindrome Checker. This is what the question answer shows from that thread:

grep -E '^[a-z]{3,45}$' /usr/share/dict/words | while read -r word;
    do [ $word == `echo $word | rev` ] && echo $word;
done;

I understand that this is reading from "words" but have trouble trying to modify it to read from a text file instead of /usr/share/dict/words. I want to have it to read any text file I request, so I can put:

source palindrome *filename*

and this will print out the palindromes found in the file in the console. Also later to have it so I can output to a output file:

source palindrome *filename* >> output.txt

I have tried to do this but it doesn't work and I am really not sure what I have to change to get it to read my file:

#!usr/bin/bash

function search
{
    filename=$1

    grep -E '^[a-z]{3,45}$' "$filename" | while read -r word;
        do [ $word == `echo $word | rev` ] && echo $word;
    done;
}
search $1

If any solutions are given could they be in a similar format? I haven't learned too many other techniques yet. If more complicated solutions are given could you explain the code given a little please.


The input file is from an eBook, it is extremely long so a small snippet is: (I do realise this doesn't show off palindromes in the snippet but it is just to show what kind of text file it is)

O and that lotion mustn't forget.
Fever near her mouth. Your head it simply. Hair braided over: shell with
seaweed. Why do they hide their ears with seaweed hair?

When running source palindrome filename there is no error message. I press enter and the terminal lets me type anything I want in again. It doesn't look as if it is running through the script


Solution

  • #!/bin/bash
    
    function search
    {
        grep -oiE '[a-z]{3,}' "$@" | tr '[:upper:]' '[:lower:]' | while read -r word; do
            [[ $word == $(rev <<< "$word") ]] && echo "$word"
        done
    }
    
    search "$@"
    

    The dictionary this original code was written for had a single lowercase word on each line. To parse a text file with multiple mixed case words per line you need a few modifications:

    • Remove the ^ and $ anchors from the regex to find words anywhere on a line.
    • Use grep -o to print out the matching words.
    • Use grep -i to match both upper and lowercase.
    • Use tr to switch uppercase letters to lowercase.

    Other fixes:

    • Changed the shebang line to #!/bin/bash. It ought to be an absolute path, and the two preferred forms are either #!/bin/bash or #!/usr/bin/env bash.
    • Changed £word to $word.
    • Use "$@" so search() can accept multiple file names.

    Improvements:

    • There's no particular reason to limit words to 45 characters. {3,} removes the upper limit.
    • Double brackets [[ ]] are better than single brackets [ ].
    • $(cmd) is better than `cmd`.
    • rev <<< "$word" is better than echo "$word" | rev.