Wednesday, January 31, 2024

[SOLVED] Execute function on results of `find` - sh

Issue

I'm writing a shell script to run on a docker image based on Alpine. It's shell is /bin/sh.

What I'm trying to do is execute a function for the results of a find command. The following works in my local bash and sh shells.

myscript.sh:

#!/bin/sh

function get_tags {
  # do stuff
}


export -f get_tags

# get all YAML files in ./assets/config that have 'FIND' somewhere in the filename
# pass each to the get_tags function
find ./assets/config -type f \( -iname "Find*.yaml" -or -iname "Find*.yml" \) -exec sh -c 'get_tags "$0"' {} \;

When I run it on the alpine image, however, I get the following error:

./myscript.sh: export: line 31: illegal option -f

Is there another way I can do this?

My question is NOT "what is the difference between sh and bash". My question is: how do I accomplish the task of running a function on the output of the find command.


Solution

You need to use bash, like this:

#!/bin/bash
fun() { echo "fun ${1}" ; }
export -f fun
find . -name 'foo' -exec bash -c 'fun "${1}"' -- {} \;

The key here is to run bash -c 'fun "${1}"' -- {} \;. You can't call the function directly (and pass arguments to it). You need to wrap it into a minimal script where this minimal script receives the argument passed by find and passes it through to the function.


Note: I'm passing two arguments to bash -c: the string -- and the actual filename {}. I'm doing this by convention, because argument counting starts at $0 when a script is executed by bash -c, in opposite to $1 when running a script the normal way (in a file, not via bash -c)

bash -c 'fun "${0}"' {} \; would work, but people might think $0 is the script name like they know it from normal scripts.



Answered By - hek2mgl
Answer Checked By - Willingham (WPSolving Volunteer)