Shell Script to find string in log file

17,833

Solution 1

Reading your question reminded me always wanting to have a simple script that would make it easier for me to search files for a particular string and then look at one of the files containing the string. Building upon your script and the suggestion by saga to use an array, I have created my script and also finished yours. :)

Please note: This script is /bin/bash, not /bin/sh, because I don't know how to get the array working in /bin/sh...

Your script:

#!/bin/bash
# Collect Customer ID as input
read -p "Enter Customer ID: " custid
echo "Searched customer ID $custid found in following logs: "
# Find the customer id as string in specified directory

arr=( $(find /usr/local/tomcat9/logs/ -type f -exec grep -l "$custid" {} \; | sort -r) )

if [ ${#arr[@]} -eq 0 ]; then
    echo "No matches found."
else
    arr+=('Quit')
    select opt in "${arr[@]}"
    do
        case $opt in
            "Quit")
                break
                ;;
            *)
                vim $opt
                break
                ;;
        esac
    done
fi

EDIT: While the script above will work perfectly fine for the the original question, I have built upon Wildcard's answer, so my script can handle files with empty spaces and offers various tools to open the selected file.

My script:

#!/bin/bash
# Find string in files of given directory (recursively)

read -p "Enter search string: " text
read -p "Enter directory: " directory

#arr=( $(find $directory -type f -exec grep -l "$text" {} \; | sort -r) )
#find $directory -type f -exec grep -qe "$text" {} \; -exec bash -c '

file=$(find $directory -type f -exec grep -qe "$text" {} \; -exec bash -c 'select f; do echo $f; break; done' find-sh {} +;)

if [ -z "$file" ]; then
    echo "No matches found."
else
    echo "select tool:"
    tools=("nano" "less" "vim" "quit")
    select tool in "${tools[@]}"
    do
        case $tool in
            "quit")
                break
                ;;
            *)
                $tool $file
                break
                ;;
        esac
    done
fi

Solution 2

Just use select (the bash builtin).

$ help select
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.
$

So the code you want is probably:

read -p 'Enter Customer ID: ' custid
select f in $(find /usr/local/tomcat9/logs -type f -exec grep -q -e "$custid" {} \; -print); do
  vim "$f"
done

Note that if your filenames contain whitespace this will break. See also:


However, if you call the select builtin directly from find, this will handle whitespace with ease. So the following is actually better in all cases I can think of:

read -p 'Enter customer ID: ' custid
find /usr/local/tomcat9/logs -type f -exec grep -qe "$custid" {} \; -exec bash -c '
  select f; do vim "$f"; done' find-sh {} +
Share:
17,833

Related videos on Youtube

Tasbir
Author by

Tasbir

I am a B.Sc Engg graduate in Telecommunication and Electronic Engineering and post graduated in Computer Science. I have served in 2 leading ISPs in Bangladesh worked as System Engineer in a reputed private Bank. Currently I am serving in Karnaphuli Gas Distribution Company Ltd. as Network Administrator. I have a keen thrust to be more and more skilled in Linux environment.

Updated on September 18, 2022

Comments

  • Tasbir
    Tasbir over 1 year

    I have a script that matches a string in a directory from number of log files as below:

    #!/bin/sh
    # Collect Customer ID as input
    read -p "Enter Customer ID: " custid
    echo "Searched customer ID $custid found in following logs: "
    # Find the customer id as string in specified directory
    find /usr/local/tomcat9/logs/ -type f -exec grep -l "$custid" {} \;
    

    This outputs a list of log files that contains the searched string. For example:

    Enter Customer ID: 2001NM-100313
    Searched customer ID 2001NM-100313 found in following logs:
    /usr/local/tomcat9/logs/localhost_access_log.2017-10-04.txt
    /usr/local/tomcat9/logs/localhost_access_log.2017-07-11.txt
    /usr/local/tomcat9/logs/localhost_access_log.2017-11-02.txt
    /usr/local/tomcat9/logs/localhost_access_log.2017-09-11.txt
    /usr/local/tomcat9/logs/localhost_access_log.2017-08-09.txt
    /usr/local/tomcat9/logs/localhost_access_log.2017-06-11.txt
    

    I want this output as list like:

    1. /usr/local/tomcat9/logs/localhost_access_log.2017-10-04.txt
    2. /usr/local/tomcat9/logs/localhost_access_log.2017-07-11.txt
    3. /usr/local/tomcat9/logs/localhost_access_log.2017-11-02.txt
    4. /usr/local/tomcat9/logs/localhost_access_log.2017-09-11.txt
    5. /usr/local/tomcat9/logs/localhost_access_log.2017-08-09.txt
    6. /usr/local/tomcat9/logs/localhost_access_log.2017-06-11.txt
    

    And it will ask for the number 1/2/3/4/5/6 to input which will open that numbered file, i.e; if I press 4 it will send command

    vim /usr/local/tomcat9/logs/localhost_access_log.2017-09-11.txt
    

    and the string "2001NM-100313" will be searched all over that file.

    My target is to read the whole line/lines (there may be multiple lines with the string) that contain this string from the log files, there may be multiple log files that have this string with multiple date, I need to select any dated file and read the log.

  • Wildcard
    Wildcard over 6 years
    Hint: run help select in your bash shell. Also, why in the name of common sense are you using head -2 here?
  • Johano Fierra
    Johano Fierra over 6 years
    @Wildcard I am not sure what you are hinting towards, and why the down vote... sure it can be done better, but it does exactly what he wanted.
  • Johano Fierra
    Johano Fierra over 6 years
    I removed the head - 2, it stayed in there from some testing, my bad.
  • Wildcard
    Wildcard over 6 years
    I removed the downvote, which was for limiting results to two (with head -2). My hint is about your "always wanting to have a simple script that would make it easier for me to search files for a particular string and then look at one of the files containing the string." That can be done tremendously easily with select. Although depending on your workflow, :vimgrep (from inside Vim) may be more applicable.
  • Johano Fierra
    Johano Fierra over 6 years
    @Wildcard thank you for clarifying. I really like your answer... up-voted. :)
  • Tasbir
    Tasbir over 6 years
    Thanks @Wildcard This is what exactly I was looking for, thanks a lot. One more thing while I am searching and after finding a list and editing any from the searched file after I close that file the script still waits for another input, how to close the script while I am closing the searched file?