Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

In a bash script I define a function that is called from find. The problem is that the scope of variables does not extend to the function. How do I access variables from the function? Here is an example:

variable="Filename:"

myfunction() {
    echo $variable $1
}

export -f myfunction

find . -type f -exec bash -c 'myfunction "{}"' \;

This will output filenames but without the string "Filename:".

Is there perhaps a better way to invoke a function from find such that it is called for every file that find finds, and variables are still defined?

share|improve this question

4 Answers 4

up vote 3 down vote accepted

You could declare variable as an environment variable, i.e.,

export variable="Filename:"
share|improve this answer

Not an answer to your question, but do not do:

find . -type f -exec bash -c 'myfunction "{}"' \;

Beside the fact that it's not portable, it's also very dangerous, because the file names end up being interpreted by bash as shell code. Consider for instance what would happen if there was a file called $(rm -rf ~) down there. Write it:

find . -type f -exec bash -c 'myfunction "$1"' find+bash {} \;

Or even better (to avoid running one bash per file):

find . -type f -exec bash -c 'for file do
  myfunction "$file"; done' find+bash {} +

Now an answer to your question to be on topic:

You could do:

{ find . -type f -exec printf '%s\0' {} + | while IFS= read -ru3 -d '' file; do
  myfunction "$file"; done 3<&0 <&4 4<&-; } 4<&0

That way, you're calling myfunction within the current bash shell, so you don't need to export myfunction or run additional bash shells.

If your find supports the -print0 predicate (like GNU, busybox and some BSDs finds), you can replace -exec printf '%s\0' {} + with -print0

share|improve this answer
    
Ah, I see. Note taken. I was guessing that my approach was not the best choice. –  DustByte Aug 27 '13 at 15:12
    
Great, thanks. I will look into your updated answer. –  DustByte Aug 27 '13 at 21:55

I would do it as follow

#!/bin/bash

myfunction() {
    local var="Filename: "
    local file=$1 
        echo "$var" "$file"
    }

export -f myfunction
    find . -type f -exec bash -c 'myfunction {}' \;

Declare vars as local and pass '{}' to the function which is $1 first positional parameter.

share|improve this answer
    
The variable I want to use within the function (var in your example) is defined outside the function in my case. –  DustByte Aug 27 '13 at 14:58
    
Sure, You've defined a global variable but why since the variable is going to run/be part of the function itself. –  val0x00ff Aug 27 '13 at 15:03
    
Well, this was just an example. In my real script, the variable is used at many places, in particular outside the function. –  DustByte Aug 27 '13 at 15:10

When you assign to a variable, it starts out as a shell variable. In order to get an environment variable that is passed to subprocesses, you need to export it.

variable="Filename:"
export variable

You can put the assignment on the same line as export: export variable="Filename:".

The variable will be visible to the shells started by find, but not the functions. In bash, you can also export functions. This possibility is not present in ksh, dash and other shells that are often used as sh because they are faster and leaner.

export -f myfunction    # bash only

Never use {} inside a string in find -exec … (or xargs …), unless you know that your file names are alphanumeric. If there are any special characters in the file names, they will be parsed as such by the inner shell. Instead, pass the file names on the shell's command line.

find . -type f -exec bash -c 'for x; do myfunction "$x"; done' _ {} +

With shells other than bash, define the function in the inner shell, or just put its code directly.

Alternatively, in bash, you can use recursive globbing instead of find. Beware that bash traverses symbolic links to directories.

variable="Filename:"
myfunction () { … }
shopt -s globstar dotglob
for x in **/*; do
  if [[ -f $x ]]; then myfunction "$x"; fi
done
share|improve this answer
    
Thanks for the info. What initially confused me was that the variable was indeed accessible when calling the function on its own (not from find), e.g. having a line saying just myfunction "hello.txt". –  DustByte Aug 28 '13 at 9:46

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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