如何将字符串文字存储到变量而不进行替换或解释?

如何将字符串文字存储到变量而不进行替换或解释?

为了扁平化目录结构,我可以这样做:

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连接)。`foobar"`"

另外,当你这样做时:

mv foo/bar bar

如果bar确实存在并且是一个目录,那么它实际上是一个mv foo/bar bar/bar.mv -t . foo/bar或者mv foo/bar .没有这个问题。

现在,要将这几个参数(、、、、、、、)存储到变量-exec中,您需要一个数组变量。支持数组的 shell 有, , , , , , , 。sh-cexec mv "$@" .sh{}+(t)cshkshbashzshrcesyashfish

为了能够直接使用该变量$FLATTEN(而不是"${FLATTEN[@]}"在 ksh/bash/yash 或$FLATTEN:q(t)csh),您需要一个具有合理数组实现的 shell:rcesfish。同样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。它允许您定义全局插入的别名,而不仅仅是在命令名称处插入。

相关内容