-execdir简介
在man 2 find中, 会发现还有一个 -execdir的Action.
跟-exec类似, 它也有两种方式, 也是对那些符合条件而被找出来的文件,执行相应的操作.
-execdir command ;
-execdir command {} +
-execdir与-exec的区别
那么-execdir跟-exec有啥区别?
简单来说,
-exedir是进入所找到文件(所在)的目录执行command.
-exec是直接在find的起点目录(starting-point)中执行command.
先来看看执行的效果
[mg@fedora d2]$ pwd
/home/mg/code/foo/d2
[mg@fedora d2]$ find . -type f
./xx2
./dd2/xx1
./dd2/xx3
./dd2/xx1.BAK
./dd2/xx3.BAK
./dd3/nn1
./dd3/nn2
./ewe
./fsjk
# 对比 "-exec command ;" 与 "-execdir command ;"
[mg@fedora d2]$ find . -type f -exec ls -l {} \;
-rw-rw-r--. 1 mg mg 7 Sep 19 2019 ./xx2
-rw-rw-r--. 1 mg mg 0 Jul 4 12:20 ./dd2/xx1
-rw-rw-r--. 1 mg mg 0 Jul 4 12:20 ./dd2/xx3
-rw-rw-r--. 1 mg mg 0 Jul 11 16:48 ./dd2/xx1.BAK
-rw-rw-r--. 1 mg mg 0 Jul 11 16:48 ./dd2/xx3.BAK
-rw-rw-r--. 1 mg mg 0 Jul 4 12:21 ./dd3/nn1
-rw-rw-r--. 1 mg mg 0 Jul 4 12:21 ./dd3/nn2
-rw-rw-r--. 1 mg mg 0 Jul 4 12:22 ./ewe
-rw-rw-r--. 1 mg mg 0 Jul 4 12:22 ./fsjk
[mg@fedora d2]$ find . -type f -execdir ls -l {} \;
-rw-rw-r--. 1 mg mg 7 Sep 19 2019 ./xx2
-rw-rw-r--. 1 mg mg 0 Jul 4 12:20 ./xx1
-rw-rw-r--. 1 mg mg 0 Jul 4 12:20 ./xx3
-rw-rw-r--. 1 mg mg 0 Jul 11 16:48 ./xx1.BAK
-rw-rw-r--. 1 mg mg 0 Jul 11 16:48 ./xx3.BAK
-rw-rw-r--. 1 mg mg 0 Jul 4 12:21 ./nn1
-rw-rw-r--. 1 mg mg 0 Jul 4 12:21 ./nn2
-rw-rw-r--. 1 mg mg 0 Jul 4 12:22 ./ewe
-rw-rw-r--. 1 mg mg 0 Jul 4 12:22 ./fsjk
# 对比 "-exec command {} +" 与 "-execdir command {} +"
[mg@fedora d2]$ find . -type f -exec ls -l {} \+
-rw-rw-r--. 1 mg mg 0 Jul 4 12:20 ./dd2/xx1
-rw-rw-r--. 1 mg mg 0 Jul 11 16:48 ./dd2/xx1.BAK
-rw-rw-r--. 1 mg mg 0 Jul 4 12:20 ./dd2/xx3
-rw-rw-r--. 1 mg mg 0 Jul 11 16:48 ./dd2/xx3.BAK
-rw-rw-r--. 1 mg mg 0 Jul 4 12:21 ./dd3/nn1
-rw-rw-r--. 1 mg mg 0 Jul 4 12:21 ./dd3/nn2
-rw-rw-r--. 1 mg mg 0 Jul 4 12:22 ./ewe
-rw-rw-r--. 1 mg mg 0 Jul 4 12:22 ./fsjk
-rw-rw-r--. 1 mg mg 7 Sep 19 2019 ./xx2
[mg@fedora d2]$ find . -type f -execdir ls -l {} \+
-rw-rw-r--. 1 mg mg 7 Sep 19 2019 ./xx2
-rw-rw-r--. 1 mg mg 0 Jul 4 12:20 ./xx1
-rw-rw-r--. 1 mg mg 0 Jul 11 16:48 ./xx1.BAK
-rw-rw-r--. 1 mg mg 0 Jul 4 12:20 ./xx3
-rw-rw-r--. 1 mg mg 0 Jul 11 16:48 ./xx3.BAK
-rw-rw-r--. 1 mg mg 0 Jul 4 12:21 ./nn1
-rw-rw-r--. 1 mg mg 0 Jul 4 12:21 ./nn2
-rw-rw-r--. 1 mg mg 0 Jul 4 12:22 ./ewe
-rw-rw-r--. 1 mg mg 0 Jul 4 12:22 ./fsjk
[mg@fedora d2]$ find . -type f -exec echo {} \+
./xx2 ./dd2/xx1 ./dd2/xx3 ./dd2/xx1.BAK ./dd2/xx3.BAK ./dd3/nn1 ./dd3/nn2 ./ewe ./fsjk
[mg@fedora d2]$ find . -type f -execdir echo {} \+
./xx2
./xx1 ./xx3 ./xx1.BAK ./xx3.BAK
./nn1 ./nn2
./ewe ./fsjk
[mg@fedora d2]$ pwd
/home/mg/code/foo/d2
[mg@fedora d2]$ find . -type f
./xx2
./dd2/xx1
./dd2/xx3
./dd2/xx1.BAK
./dd2/xx3.BAK
./dd3/nn1
./dd3/nn2
./ewe
./fsjk
# 查看 -exec 和 -execdir 执行command时的当前工作目录
[mg@fedora d2]$ find . -type f -exec pwd \;
/home/mg/code/foo/d2
/home/mg/code/foo/d2
/home/mg/code/foo/d2
/home/mg/code/foo/d2
/home/mg/code/foo/d2
/home/mg/code/foo/d2
/home/mg/code/foo/d2
/home/mg/code/foo/d2
/home/mg/code/foo/d2
[mg@fedora d2]$ find . -type f -execdir pwd \;
/home/mg/code/foo/d2
/home/mg/code/foo/d2/dd2
/home/mg/code/foo/d2/dd2
/home/mg/code/foo/d2/dd2
/home/mg/code/foo/d2/dd2
/home/mg/code/foo/d2/dd3
/home/mg/code/foo/d2/dd3
/home/mg/code/foo/d2
/home/mg/code/foo/d2
[mg@fedora foo]$ rm -rf dirF
[mg@fedora foo]$ find dirE -name "*.c"
dirE/1.c
dirE/2.c
dirE/3.c
[mg@fedora foo]$
[mg@fedora foo]$ find dirE -name "*.c" -exec cp {} dirF/{}.bak \;
cp: cannot create regular file 'dirF/dirE/1.c.bak': No such file or directory
cp: cannot create regular file 'dirF/dirE/2.c.bak': No such file or directory
cp: cannot create regular file 'dirF/dirE/3.c.bak': No such file or directory
[mg@fedora foo]$ mkdir dirF
[mg@fedora foo]$ find dirE -name "*.c" -exec cp {} dirF/{}.bak \;
cp: cannot create regular file 'dirF/dirE/1.c.bak': No such file or directory
cp: cannot create regular file 'dirF/dirE/2.c.bak': No such file or directory
cp: cannot create regular file 'dirF/dirE/3.c.bak': No such file or directory
[mg@fedora foo]$ mkdir dirF/dirE
[mg@fedora foo]$ find dirE -name "*.c" -exec cp {} dirF/{}.bak \;
[mg@fedora foo]$ ls dirF
dirE
[mg@fedora foo]$ ls dirF/dirE/
1.c.bak 2.c.bak 3.c.bak
# 从上数执行的效果可知, -exec在执行command时, "当前工作目录" 依旧是find命令指定的起点目录.
[mg@fedora foo]$
[mg@fedora foo]$ rm -rf dirF
[mg@fedora foo]$ find dirE -name "*.c" -execdir cp {} dirF/{}.bak \;
cp: cannot create regular file 'dirF/./1.c.bak': No such file or directory
cp: cannot create regular file 'dirF/./2.c.bak': No such file or directory
cp: cannot create regular file 'dirF/./3.c.bak': No such file or directory
[mg@fedora foo]$ mkdir dirF
[mg@fedora foo]$ find dirE -name "*.c" -execdir cp {} dirF/{}.bak \;
cp: cannot create regular file 'dirF/./1.c.bak': No such file or directory
cp: cannot create regular file 'dirF/./2.c.bak': No such file or directory
cp: cannot create regular file 'dirF/./3.c.bak': No such file or directory
[mg@fedora foo]$ cp dirE/1.c dirF/./1.c.bak
[mg@fedora foo]$ ls dirF
1.c.bak
[mg@fedora foo]$ rm dirF/1.c.bak -f
[mg@fedora foo]$ mkdir -p dirE/dirF
[mg@fedora foo]$ find dirE -name "*.c" -execdir cp {} dirF/{}.bak \;
[mg@fedora foo]$ ls -l dirE
total 20
-rw-rw-r--. 1 mg mg 4 Jul 18 16:04 1.c
-rw-rw-r--. 1 mg mg 4 Jul 18 16:04 2.c
-rw-rw-r--. 1 mg mg 4 Jul 18 16:04 3.c
-rw-rw-r--. 1 mg mg 4 Jul 18 16:04 4.txt
-rw-rw-r--. 1 mg mg 4 Jul 18 16:05 5.h
drwxrwxr-x. 2 mg mg 51 Jul 18 16:11 dirF
# 从上数执行的效果可知, -execdir 执行command时, "当前工作目录"是正被处理的文件所在的目录.
从上述对比可知,
-exec执行时, 都是基于find命令中指定的起点目录 (即, find [starting-point...] [expression]
中的starting-point), 来对文件进行操作. 执行command时, “当前工作目录” 依旧是find命令指定的起点目录.
-execdir 执行时, 是进入到文件所在的子目录, 对文件进行操作, 执行command时, "当前工作目录"是正被处理的文件所在的目录.
对于 “-exec/execdir command {} +” , -exec将所有找到的文件列成一串参数, 一次性传给command, 而-execdir是将每个子目录中的文件, 先列成一串参数传给command, 有多个子目录, 则command会被调用多次.
===========================
以下来自 https://unix.stackexchange.com/questions/389705/understanding-the-exec-option-of-find
中的一段描述,
Using -execdir
There is also -execdir (implemented by most find variants, but not a standard option).
This works like -exec with the difference that the given shell command is executed with the directory of the found pathname as its current working directory and that {} will contain the basename of the found pathname without its path (but GNU find will still prefix the basename with ./, while BSD find will not do that).
Example:
find . -type f -name '*.txt' \
-execdir mv {} done-texts/{}.done \;
This will move each found *.txt-file to a pre-existing done-texts subdirectory in the same directory as where the file was found. The file will also be renamed by adding the suffix .done to it.
This would be a bit trickier to do with -exec as we would have to get the basename of the found file out of {} to form the new name of the file. We also need the directory name from {} to locate the done-texts directory properly.
With -execdir, some things like these becomes easier.
The corresponding operation using -exec instead of -execdir would have to employ a child shell:
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done"
done' sh {} +
or,
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "${name%/*}/done-texts/${name##*/}.done"
done' sh {} +
share improve this answer follow
edited Dec 19 '19 at 7:47
answered Sep 1 '17 at 8:18
Kusalananda♦
207k2626 gold badges385385 silver badges624
============================
man 2 find中, 对-execdir的说明
-execdir command ;
-execdir command {} +
Like -exec, but the specified command is run from the subdirec‐
tory containing the matched file, which is not normally the
directory in which you started find. This a much more secure
method for invoking commands, as it avoids race conditions during
resolution of the paths to the matched files. As with the -exec
action, the `+' form of -execdir will build a command line to
process more than one matched file, but any given invocation of
command will only list files that exist in the same subdirectory.
If you use this option, you must ensure that your $PATH environ‐
ment variable does not reference `.'; otherwise, an attacker can
run any commands they like by leaving an appropriately-named file
in a directory in which you will run -execdir. The same applies
to having entries in $PATH which are empty or which are not abso‐
lute directory names. If any invocation returns a non-zero value
as exit status, then find returns a non-zero exit status. If
find encounters an error, this can sometimes cause an immediate
exit, so some pending commands may not be run at all. The result
of the action depends on whether the + or the ; variant is being
used; -execdir command {} + always returns true, while -execdir
command {} ; returns true only if command returns 0.
PATH环境变量对-execdir的影响
上述man中, 可以看到它还提到与PATH环境变量相关的描述:
- 使用-execdir时, PATH环境变量中不能引用"."(即一个点, 表示当前目录)
- 使用-execdir时, PATH环境变量中不能有"空" (即两个冒号紧连着, 或者冒号结尾, 或者冒号开头)
- 使用-execdir时, PATH环境变量中不能有"非绝对路径方式的目录".
在 链接 (https://www.gnu.org/software/findutils/manual/html_mono/find.html#Single-File) 中, 3.3.1 Single Files中有专门对-execdir 对 PATH环境变量详细说明:
If you use ‘-execdir’, you must ensure that the ‘$PATH’ variable contains only
absolute directory names. Having an empty element in ‘$PATH’ or explicitly
including ‘.’ (or any other non-absolute name) is insecure. GNU find will refuse
to run if you use ‘-execdir’ and it thinks your ‘$PATH’ setting is insecure.
For example:
‘/bin:/usr/bin:’
Insecure; empty path element (at the end)
‘:/bin:/usr/bin:/usr/local/bin’
Insecure; empty path element (at the start)
‘/bin:/usr/bin::/usr/local/bin’
Insecure; empty path element (two colons in a row)
‘/bin:/usr/bin:.:/usr/local/bin’
Insecure; ‘.’ is a path element (. is not an absolute file name)
‘/bin:/usr/bin:sbin:/usr/local/bin’
Insecure; ‘sbin’ is not an absolute file name
‘/bin:/usr/bin:/sbin:/usr/local/bin’
Secure (if you control the contents of those directories and any access to them)
实际的尝试如下:
# 先看下原生初始的PATH环境变量的值
[mg@fedora bin]$ echo $PATH
/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin
[mg@fedora bin]$
# 我们先将原生初始的PATH环境变量保存到另一个变量中.
[mg@fedora bin]$ GOOD_SAVED_PATH="${PATH}"
[mg@fedora bin]$ echo $GOOD_SAVED_PATH
/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin
# 将冒号加到PATH环境变量的结尾处
[mg@fedora foo]$ find dirE
dirE
dirE/1.c
dirE/2.c
dirE/3.c
dirE/4.txt
dirE/5.h
dirE/dirF
dirE/dirF/1.c.bak
dirE/dirF/2.c.bak
dirE/dirF/3.c.bak
[mg@fedora bin]$ PATH="${PATH}:"
[mg@fedora bin]$ echo $PATH
/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin:
[mg@fedora bin]$
# 执行-execdir, 发现不行
[mg@fedora foo]$ find dirE -name "1.c" -execdir ls -l {} \;
find: The current directory is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove the current directory from your $PATH (that is, remove ".", doubled colons, or leading or trailing colons)
[mg@fedora foo]$
# 将冒号加到PATH环境变量的开始
[mg@fedora foo]$ echo $GOOD_SAVED_PATH
/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin
[mg@fedora foo]$ PATH=":${GOOD_SAVED_PATH}"
[mg@fedora foo]$ echo $PATH
:/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin
[mg@fedora foo]$
# 执行-execdir, 发现不行
[mg@fedora foo]$ find dirE -name "1.c" -execdir ls -l {} \;
find: The current directory is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove the current directory from your $PATH (that is, remove ".", doubled colons, or leading or trailing colons)
[mg@fedora foo]$
# 让PATH中有两个连续的冒号
[mg@fedora foo]$ echo $GOOD_SAVED_PATH
/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin
[mg@fedora foo]$ pwd
/home/mg/code/foo
[mg@fedora foo]$ PATH="${GOOD_SAVED_PATH}::/home/mg/code"
[mg@fedora foo]$ echo $PATH
/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin::/home/mg/code
[mg@fedora foo]$
[mg@fedora foo]$
# 执行-execdir, 发现不行
[mg@fedora foo]$ find dirE -name "1.c" -execdir ls -l {} \;
find: The current directory is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove the current directory from your $PATH (that is, remove ".", doubled colons, or leading or trailing colons)
[mg@fedora foo]$
# 让PATH环境变量中包含"."(即一个点, 表示当前目录)
[mg@fedora foo]$ echo $GOOD_SAVED_PATH
/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin
[mg@fedora foo]$ PATH="${GOOD_SAVED_PATH}:.:/home/mg/code"
[mg@fedora foo]$ echo $PATH
/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin:.:/home/mg/code
[mg@fedora foo]$
# 执行-execdir, 发现不行
[mg@fedora foo]$ find dirE -name "1.c" -execdir ls -l {} \;
find: The current directory is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove the current directory from your $PATH (that is, remove ".", doubled colons, or leading or trailing colons)
[mg@fedora foo]$
# 让PATH环境变量包含 非绝对路径方式的目录
[mg@fedora foo]$ echo $GOOD_SAVED_PATH
/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin
[mg@fedora foo]$
[mg@fedora foo]$ PATH="${GOOD_SAVED_PATH}:code/foo"
[mg@fedora foo]$ echo $PATH
/usr/libexec/python2-sphinx:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mg/.local/bin:/home/mg/bin:code/foo
[mg@fedora foo]$
# 执行-execdir, 发现不行
[mg@fedora foo]$ find dirE -name "1.c" -execdir ls -l {} \;
find: The relative path ‘code/foo’ is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove that entry from $PATH
[mg@fedora foo]$
-execdir为什么对PATH环境变量要有这些限制?
这个主要是出于安全考虑, 以下面的命令
find dirE -name "1.c" -execdir ls {} \;
为例.
这里面, command是 ls, 如果PATH环境变量中包含空目录(eg, 冒号在最前面, 冒号在坐后面, 或者两个连续的冒号), 当前目录, 或者相对目录, 则导致执行command(当前例子中是ls)时, 这个command不是从系统常用的/bin, /usr/bin/, /sbin/, /usr/sbin 等可执行文件的目录中找出来的, 而是从当前目录找出来的名字也叫command(即也叫ls, 它有可执行权限)的文件, 而这个同名的command, 可能是有危害的.
这篇文件写的比较清楚:
在路径中,命令行 为什么使用’execdir’操作对于目录是不安全的?
下面这段话就是摘抄自这篇文章.
The -execdir action runs your command from the directory that contains the file found.
when $PATH contains relative paths, such as . or anything that doesn't start with /,
-execdir is insecure because a directory where a file is found (or another directory
resolved relative to it) could also contain an executable of the same name as the one
you are trying to run.that potentially untrusted executable would then get
run instead.
This could be deliberately exploited by another user to cause you to run their
program, which might cause harm or breach data security, instead of the program
you are trying to run.Or, less often, it might simply result in the wrong program
inadvertently being run, even without anyone trying to make the problem happen.
If everything in your PATH environment variable is an absolute path, this error
should not occur, even if the directory you're searching and -execdir ing from is
contained in PATH.(I've checked that this works.) if you believe you don't have
any relative directories in $PATH but are still getting this error, please update
your question with details including the output of echo"$PATH".
参考
https://www.aplawrence.com/Unixart/find_execdir.html
https://unix.stackexchange.com/questions/389705/understanding-the-exec-option-of-find
关于find中-exec, -execdir 的风险:
https://www.gnu.org/software/findutils/manual/html_mono/find.html#Top
https://www.gnu.org/software/findutils/manual/html_mono/find.html#Security-Considerations
https://www.gnu.org/software/findutils/manual/html_mono/find.html#Security-Considerations-for-find
https://www.gnu.org/software/findutils/manual/html_mono/find.html#Problems-with-_002dexec-and-filenames
https://www.gnu.org/software/findutils/manual/html_mono/find.html#Race-Conditions-with-_002dexec
https://www.helplib.com/c/mutia_165715
https://www.gnu.org/software/findutils/manual/html_mono/find.html#Single-File