1

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.

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
  • Hint: run help select in your bash shell. Also, why in the name of common sense are you using head -2 here? – Wildcard Nov 28 '17 at 8:32
  • @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 Nov 28 '17 at 9:22
  • I removed the head - 2, it stayed in there from some testing, my bad. – Johano Fierra Nov 28 '17 at 9:25
  • 2
    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. – Wildcard Nov 28 '17 at 9:29
  • @Wildcard thank you for clarifying. I really like your answer... up-voted. :) – Johano Fierra Nov 28 '17 at 15:56
3

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 {} +
  • 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? – Tasbir Dec 5 '17 at 4:41
  • 1
    @Tasbir, as noted, "If EOF is read, the command completes." Use Ctrl-D to send end of file signal. – Wildcard Dec 5 '17 at 5:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.