52

我想在 linux 命令行上回显 find 的文件名部分。我尝试使用以下内容:

find www/*.html -type f -exec sh -c "echo $(basename {})" \;

find www/*.html -type f -exec sh -c "echo `basename {}`" \;

以及一大堆其他组合的转义和引用文本的各个部分。结果是路径没有被剥离:

www/channel.html
www/definition.html
www/empty.html
www/index.html
www/privacypolicy.html

为什么不?

更新:虽然我在下面有一个可行的解决方案,但我仍然对为什么“basename”没有做它应该做的事情感兴趣。

4

4 回答 4

73

您最初尝试的问题:

find www/*.html -type f -exec sh -c "echo $(basename {})" \;

$(basename {})代码在执行命令之前执行一次find。单曲的输出basename{}因为这是{}作为文件名的基本名称。因此,find 执行的命令是:

sh -c "echo {}" 

对于找到的每个文件,但find实际上每次都替换原始(未修改的)文件名,因为{}字符出现在要执行的字符串中。

如果你想让它工作,你可以使用单引号而不是双引号:

find www/*.html -type f -exec sh -c 'echo $(basename {})' \;

但是,将本来会写入标准输出的echo内容重复到标准输出basename有点毫无意义:

find www/*.html -type f -exec sh -c 'basename {}' \;

当然,我们还可以进一步减少:

find www/*.html -type f -exec basename {} \;

您能否在这里解释一下单引号和双引号之间的区别?

这是常规的 shell 行为。让我们使用一个稍微不同的命令(但只是略有不同——文件的名称可以在www目录下的任何位置,而不仅仅是向下一层),并查看单引号 (SQ) 和双引号 (DQ) 版本的命令:

find www -name '*.html' -type f -exec sh -c "echo $(basename {})" \;   # DQ
find www -name '*.html' -type f -exec sh -c 'echo $(basename {})' \;   # SQ

单引号将包含的材料直接传递给命令。因此,在 SQ 命令行中,启动的 shellfind删除了封闭的引号,find命令将其$9参数视为:

echo $(basename {})

因为外壳删除了引号。相比之下,双引号中的材料是由shell处理的。因此,在 DQ 命令行中,shell(启动find的——不是由 启动 find)看到$(basename {})字符串的一部分并执行它,然后返回{},所以它find作为$9参数传递给的字符串是:

echo {}

现在,find它的-exec动作何时执行,在这两种情况下,它都会用{}它刚刚找到的文件名替换 (为了论证,www/pics/index.html)。因此,您会执行两个不同的命令:

sh -c 'echo $(basename www/pics/index.html)'    # SQ
sh -c "echo www/pics/index.html"                # DQ

那里有一个(轻微的)符号作弊 - 这些是您在 shell 中键入的等效命令。在这$2两种情况下,启动的 shell 实际上都没有引号——启动的 shell 看不到任何引号。

如您所见,DQ 命令只是回显文件名;SQ 命令运行basename命令并捕获其输出,然后回显捕获的输出。一点还原论的想法表明,DQ 命令可以写成 as-print而不是 using -exec,而 SQ 命令可以写成-exec basename {} \;.

如果您使用的是 GNU find,它支持格式指令-printf可以遵循的操作,因此无需运行。但是,这仅在 GNU 中可用;此处讨论的其余部分适用于您可能遇到的任何版本。basenamefindfind

于 2013-03-26T01:14:39.037 回答
15

试试这个:

 find www/*.html -type f -printf '%f\n'

如果你想用管道来做(需要更多资源):

find www/*.html -type f -print0 | xargs -0 -n1 basename
于 2013-03-26T00:47:59.563 回答
7

这就是我如何使用 imagick 批量调整文件大小,从源重新分配输出文件名

find . -name header.png -exec sh -c 'convert -geometry 600 {} $(dirname {})/$(basename {} ".png")_mail.png' \;
于 2014-01-17T14:45:35.377 回答
0

我必须完成类似的事情,并发现遵循提到的避免循环 find 的输出并将 find 与 sh 一起使用的做法完全回避了这些{}问题-printf

你可以这样尝试:

find www/*.html -type f -exec sh -c 'echo $(basename $1)' find-sh {} \;

摘要是“不要直接在 sh -c 内部引用 {},而是将其作为参数传递给 sh -c,然后您可以在 sh -c 内部使用数字变量引用它find-sh” dummy 来使用$0,这样做和使用{}for有更多的实用性$1

我假设使用echo确实是为了简化概念和测试功能。正如其他人所提到的,有更简单的方法可以简单地回显,但这种情况的理想用例可能是使用cp,mv或任何更复杂的命令,您希望在命令中多次引用找到的文件名并且您需要获取摆脱路径,例如。当您必须在源和目标中指定文件名或重命名时。

例如,如果您只想将 html 文档复制您的public_html目录(为什么?因为 Example!),那么您可以:

 find www/*.html -type f -exec sh -c 'cp /var/www/$(basename $1) /home/me/public_html/$(basename $1)' find-sh {} \;

在 unix stackexchange 上,用户通配符关于使用 find 循环的答案进入了一些关于-execand使用的伟大宝石sh -c。(你可以在这里找到它:https ://unix.stackexchange.com/questions/321697/why-is-looping-over-finds-output-bad-practice )

于 2016-11-16T18:53:09.807 回答