为了扁平化目录结构,我可以这样做:
find . -type f -exec sh -c 'mv "{}" "./`basename "{}"`"' \;
我想将以下内容存储在我的个人资料中:$FLATTEN
-exec sh -c 'mv "{}" "./`basename "{}"`"' \;
这样我以后就可以执行find . $FLATTEN
我在存储变量时遇到问题,因为它被解释得太早。我希望它存储为字符串文字,并且仅在 shell 上使用时进行解释,而不是在获取时进行解释。
答案1
如果使用 GNU mv
,你应该这样做:
find . -type f -exec mv -t . {} +
和其他mv
人:
find . -type f -exec sh -c 'exec mv "$@" .' sh {} +
您永远不应该嵌入代码{}
中sh
。这是一个命令注入漏洞,因为文件名被解释为 shell 代码(`reboot`
例如尝试使用名为 的文件)。
引用命令替换是个好点,但是因为您使用了古老的形式(`...`
而不是$(...)
),所以您需要转义内部双引号或它不会工作在sh
基于 Bourne shell 或 AT&T ksh 的实现(其中"`basename "foo bar"`"
实际上被视为(在这些 shell 中接受的"`basename "
不匹配的)与and then连接)。`
foo
bar"`"
另外,当你这样做时:
mv foo/bar bar
如果bar
确实存在并且是一个目录,那么它实际上是一个mv foo/bar bar/bar
.mv -t . foo/bar
或者mv foo/bar .
没有这个问题。
现在,要将这几个参数(、、、、、、、)存储到变量-exec
中,您需要一个数组变量。支持数组的 shell 有, , , , , , , 。sh
-c
exec mv "$@" .
sh
{}
+
(t)csh
ksh
bash
zsh
rc
es
yash
fish
为了能够直接使用该变量$FLATTEN
(而不是"${FLATTEN[@]}"
在 ksh/bash/yash 或$FLATTEN:q
中(t)csh
),您需要一个具有合理数组实现的 shell:rc
、es
或fish
。同样zsh
在这里,这些参数都不是空的。
在rc
/ es
/ zsh
:
FLATTEN=(-exec sh -c 'exec mv "$@" .' sh '{}' +)
在fish
:
set FLATTEN -exec sh -c 'exec mv "$@" .' sh '{}' +
然后你可以使用:
find . -type f $FLATTEN
答案2
最好使用 shell 函数来实现此目的,并获得-exec
正确的结果(不要放入{}
子 shell):
flatten () {
( cd "${1:-.}" &&
find . -type f -exec sh -c 'for n; do test -e "${n##*/}" || mv "$n" "${n##*/}"; done' sh {} + )
}
这也不需要调用外部实用程序basename
,也不会尝试覆盖已经存在的东西。
您只需键入即可使用它flatten
,它将作用于当前目录。给它一个目录名将使它执行该目录(将该目录下的所有文件复制到该目录的顶部)。
答案3
函数可能是最好的方法。否则你需要一个数组或eval
:
find_array=(-exec sh -c 'mv "{}" "./`basename "{}"`"' \;)
find . "${find_array[@]}"
或者
FLATTEN="-exec sh -c 'mv \"{}\" \"./`basename \"{}\"`\"' \;"
eval find . $FLATTEN
答案4
一个函数怎么样?
flatten(){
find "$@" -type f -exec sh -c 'mv -- "$0" "${0##*/}"' {} \;
}
用法:
> flatten .
如果您使用zsh
,则可以-g
选择alias
。它允许您定义全局插入的别名,而不仅仅是在命令名称处插入。