Search code examples
bashreusability

reuse command output multiple times in bash script


I have a bash script that operates several similar find commands and reads the output into variables:

f_list=`find /foo -name bar | sort`
f_number=`find /foo -name bar | wc -l`
f_first=`find /foo -name bar | sort | head -n 1`

All this works as expected.

Assuming the "find" command to be expensive (time wise), is there a clever way of re-using one of the results for the others?

What I tried (and failed with) is:

f_list=`find /foo -name bar | sort`
f_number=`echo "$f_list" | wc -l`
f_first=`echo "$f_list" | head -n 1`

This doesn't work, but I hope it shows what I want to achieve.

It seems that putting the results into a variable spoils some format of the original output, which breaks stuff when sent again to the other commands.

Is there some clever way of achieving what I want?

EDIT

I created a fully working example you could recreate. In my working dir I have a folder "foo" with 3 files "bar1", "bar2", "bar3".

find_result=`find ./foo -type f -iname "bar*" | sort`
find_count1=`echo "$find_result" | wc -l`
echo "$find_result"
echo $find_count1
find_count2=`find ./foo -type f -iname "bar*" | wc -l`
find ./foo -type f -iname "bar*"
echo $find_count2

results in the expected

./foo/bar1
./foo/bar2
./foo/bar3
3
./foo/bar3
./foo/bar2
./foo/bar1
3

but when the result is empty (I modified the search criteria to find nothing)

find_result=`find ./foo -type f -iname "bor*" | sort`
find_count1=`echo "$find_result" | wc -l`
echo "$find_result"
echo $find_count1
find_count2=`find ./foo -type f -iname "bor*" | wc -l`
find ./foo -type f -iname "bor*"
echo $find_count2

the two results differ (notice the empty result line in front of the "1")

 
1
0

And thus I thought the culprint to be the extra line break in the echo command. Therefore I removed that (notice the "-n" in the second line):

find_result=`find ./foo -type f -iname "bor*" | sort`
find_count1=`echo -n "$find_result" | wc -l`
echo "$find_result"
echo $find_count1
find_count2=`find ./foo -type f -iname "bor*" | wc -l`
find ./foo -type f -iname "bor*"
echo $find_count2

which solves the problem for empty results

 
0
0

but now when there are results, the "wc -l" counts the wrong way so

find_result=`find ./foo -type f -iname "bar*" | sort`
find_count1=`echo -n "$find_result" | wc -l`
echo "$find_result"
echo $find_count1
find_count2=`find ./foo -type f -iname "bar*" | wc -l`
find ./foo -type f -iname "bar*"
echo $find_count2

yields in

./foo/bar1
./foo/bar2
./foo/bar3
2
./foo/bar3
./foo/bar2
./foo/bar1
3

So the problem is one line break that I thought must have an easy resolution to avoid being different between empty and non-empty find results. I have the feeling of missing something very simple.


Solution

  • Use an array to store the null delimited list returned by find -print0 | sort -z:

    #!/usr/bin/env bash
    
    # map null delimited output of command-group into the f_list array
    mapfile -t f_list < <(find /foo -name bar -print0 | sort --zero-terminated)
    
    # Number of entries in the f_list array
    f_number=${#f_list[@]}
    
    # First entry of the f_list array
    f_first=${f_list[0]}