Checking for substring in a loop takes too long to complete.

I need to check if the files returned by ls command in the below script is a sub-string of the argument passed to the script i.e $1

The below script works fine but is too slow.

If the ls command take 12 secs to complete printing all files with while loop then; using posix substring check with grep -q in the script increases the total time to about 140 seconds.

ls -d "/app/scripts"/* | while read LINE
do
echo "File is:$LINE"
        if echo $1 | grep -q "$LINE"
        then
                echo "Match Found for File:$LINE"
        else
                echo "No Match Found"
        fi
done

If there a way to optimize the script for quicker results ?

How long does ls -d "/app/scripts"/* last, how many files are inside ?

What are your operating system and shell details ?
With what options is the filesystem mounted ?

Regards
Peasant.

How about

printf "Match Found for File: %s\n" /app/scripts/*$1*

I was going to say I agree with RudiC, but realise $LINE should be a subset of $1, not the other way round, so Rudi's solution probably won't get the results you want. So let's look at what is wrong with your code:

First line:

ls -d "/app/scripts"/* | while read LINE

In bash, when you pipe anything into a while loop you create a subshell. While I don't know what kind of performance hit you are getting from that, it does mean that any variables used in the loop are forgotten after the loop finishes. Not important here, but worth remembering. Second, the ls command is pointless here. The shell has expanded the file list for ls ; it is printing them according to your whim, which is to print the files one-per-line. This first line would be much better off:

for LINE in "/app/scripts"/*

Next:

if echo $1 | grep -q "$LINE"

This is where your performance hit is. Every time you run this line you fork a new process for the grep . Imagine 50 files. 50 invocations of grep ! Assuming bash (which I am), this would be much faster:

if [[ ${1} == *${LINE}* ]]

So this should be faster, unless I got it entirely wrong and Rudo got it right!

for LINE in "/app/scripts"/*
do
echo "File is:$LINE"
        if [[ ${1} == *${LINE}* ]]
        then
                echo "Match Found for File:$LINE"
        else
                echo "No Match Found"
        fi
done

Andrew

1 Like

@Andrew you got it Right :slight_smile: and sorry if my explanation in the OP was not clear.

I will test the performance of your code. By the way I'm using ksh and not bash on AiX 6.1

Okay, then my point about piping into a while statement creating a subshell was wrong - it is true for bash but not for ksh. I'm pretty sure my modifications will work for ksh.

Andrew

I think you misuse shell variables and the command line. Try to use a file or a pipe instead, from the very beginning!

1 Like
Moderator comments were removed during original forum migration.