可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Is there a way to get find
to execute a function I define in the shell? For example:
dosomething () {
echo "doing something with $1"
}
find . -exec dosomething {} \;
The result of that is:
find: dosomething: No such file or directory
Is there a way to get find
's -exec
to see dosomething
?
回答1:
Since only the shell knows how to run shell functions, you have to run a shell to run a function. You also need to mark your function for export with export -f
, otherwise the subshell won't inherit them:
export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;
回答2:
find . | while read file; do dosomething "$file"; done
回答3:
Add quotes in {}
as shown below:
export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;
This corrects any error due to special characters returned by find
,
for example files with parentheses in their name.
回答4:
Jak's answer above is great but has a couple of pitfalls that are easily overcome:
find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done
This uses null as a delimiter instead of a linefeed, so filenames with linefeeds will work. It also uses the -r
flag which disables backslash escaping, without it backslashes in filenames won't work. It also clears IFS
so that potential trailing whitespaces in names are not discarded.
回答5:
Have the script call itself, passing each item found as an argument:
#!/bin/bash
if [ ! $1 == "" ] ; then
echo "doing something with $1"
exit 0
fi
find . -exec $0 {} \;
exit 0
When you run the script by itself, it finds what you are looking for and calls itself passing each find result as the argument. When the script is run with an argument, it executes the commands on the argument and then exits.
回答6:
Processing results in bulk
For increased efficiency, many people use xargs
to process results in bulk, but it is very dangerous. Because of that there was an alternate method introduced into find
that executes results in bulk.
Note though that this method might come with some caveats like for example a requirement in POSIX-find
to have {}
at the end of the command.
export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +
find
will pass many results as arguments to a single call of bash
and the for
-loop iterates through those arguments, executing the function dosomething
on each one of those.
The above solution starts arguments at $1
, which is why there is a _
(which represents $0
).
Processing results one by one
In the same way, I think that the accepted top answer should be corrected to be
export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;
This is not only more sane, because arguments should always start at $1
, but also using $0
could lead to unexpected behavior if the filename returned by find
has special meaning to the shell.
回答7:
For those of you looking for a bash function that will execute a given command on all files in current directory, I have compiled one from the above answers:
toall(){
find . -type f | while read file; do "$1" "$file"; done
}
Note that it breaks with file names containing spaces (see below).
As an example, take this function:
world(){
sed -i 's_hello_world_g' "$1"
}
Say I wanted to change all instances of hello to world in all files in the current directory. I would do:
toall world
To be safe with any symbols in filenames, use:
toall(){
find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}
(but you need a find
that handles -print0
e.g., GNU find
).
回答8:
I find the easiest way as follow, repeating two commands in single do
func_one () {
echo "first thing with $1"
}
func_two () {
echo "second thing with $1"
}
find . -type f | while read file; do func_one $file; func_two $file; done
回答9:
It is not possible to executable a function that way.
To overcome this you can place your function in a shell script and call that from find
# dosomething.sh
dosomething () {
echo "doing something with $1"
}
dosomething $1
Now use it in find as:
find . -exec dosomething.sh {} \;
回答10:
Put the function in a separate file and get find
to execute that.
Shell functions are internal to the shell they're defined in; find
will never be able to see them.
回答11:
For reference, i avoid this scenario using:
for i in $(find $dir -type f -name "$name" -exec ls {} \;); do
_script_function_call $i;
done;
Get Output of find in current script file and iterate over the output as you may want.
I do agree with accepted answer, but i don't want to expose function outside of my script file.
回答12:
Not directly, no. Find is executing in a separate process, not in your shell.
Create a shell script that does the same job as your function and find can -exec
that.
回答13:
I would avoid using -exec
altogether. Why not use xargs
?
find . -name <script/command you're searching for> | xargs bash -c