find工具的了解与使用5: -execdir

-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环境变量相关的描述:

  1. 使用-execdir时, PATH环境变量中不能引用"."(即一个点, 表示当前目录)
  2. 使用-execdir时, PATH环境变量中不能有"空" (即两个冒号紧连着, 或者冒号结尾, 或者冒号开头)
  3. 使用-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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值