查找和基地名称打得不好

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

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

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

以及逃避和引用文本各个部分的其他一系列组合。 结果是path不被剥离:

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

为什么不?

更新:虽然我有一个工作的解决scheme下面,我仍然感兴趣,为什么“基本名称”不应该做它应该做的。

你原来的尝试的麻烦:

 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命令行中,启动find的shell删除封闭的引号, find命令将其$9参数看作:

 echo $(basename {}) 

因为shell删除了引号。 相比之下,双引号中的内容由外壳处理。 因此,在DQ命令行中,shell(启动find (不是find所启动的那个))看到字符串的$(basename {})部分并执行它,返回{} ,所以它传递给它的字符串因为其$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中输入的等效命令。 在任何情况下,启动的shell的$2实际上都没有引号 – 启动的shell没有看到任何引号。

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

如果您使用的是GNU find ,则它支持-printf操作, 格式指令后面可以跟随-printf操作,从而不需要运行basename 。 但是,这只在GNU find可用; 这里的其余讨论适用于你可能遇到的任何版本的find

试试这个:

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

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

 find www/*.html -type f -print0 | xargs -0 -n1 basename 

多数民众赞成我如何使用imagick批量调整文件大小,从源代码输出文件名

 find . -name header.png -exec sh -c 'convert -geometry 600 {} $(dirname {})/$(basename {} ".png")_mail.png' \; 

我不得不完成类似的事情,并且找到了遵循上面提到的避免循环查找输出的做法,并且使用find来完全避免这些问题{}-printf

你可以尝试这样的:

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

总结是“不要在sh -c中直接引用{},而是将它作为参数传递给sh -c,那么可以在sh -c中使用数字变量引用它。” find-sh只是作为一个假币来占用$0 ,这样做的效果就更好了,用$1 {} $1 {}

我假设使用echo真的是为了简化概念和测试功能。 有一些简单的方法可以像其他人一样简单地回显,但是对于这种情况,一个理想的用例可能是使用cpmv或者更复杂的命令,在命令中多次引用找到的文件名,而且您需要摆脱路径,例如。 当你必须在源和目的地中指定文件名或者你正在重命名的东西。

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

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

在unix stackexchange上,用循环查找的用户通配符的答案在使用-execsh -c会变成一些很好的宝石。 (你可以在这里找到它: https : //unix.stackexchange.com/questions/321697/why-is-looping-over-finds-output-bad-practice )