Linux Find 和 basename 不能很好地播放

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15627446/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 22:33:47  来源:igfitidea点击:

Find and basename not playing nicely

linuxfind

提问by Matt Parkins

I want to echo out the filename portion of a find on the linux commandline. I've tried to use the following:

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

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

and

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

and a whole host of other combinations of escaping and quoting various parts of the text. The result is that the path isn't stripped:

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

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

Why not?

为什么不?

Update: While I have a working solution below, I'm still interested in why "basename" doesn't do what it should do.

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

采纳答案by Jonathan Leffler

The trouble with your original attempt:

您最初尝试的问题:

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

is that the $(basename {})code is executed once, before the findcommand is executed. The output of the single basenameis {}since that is the basename of {}as a filename. So, the command that is executed by find is:

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

sh -c "echo {}" 

for each file found, but findactually substitutes the original (unmodified) file name each time because the {}characters appear in the string to be executed.

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

If you wanted it to work, you could use single quotes instead of double quotes:

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

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

However, making echorepeat to standard output what basenamewould have written to standard output anyway is a little pointless:

但是,echo对标准输出重复basename写入无论如何都会写入标准输出的内容有点毫无意义:

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

and we can reduce that still further, of course, to:

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

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


Could you also explain the difference between single quotes and double quotes here?

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

This is routine shell behaviour. Let's take a slightly different command (but only slightly — the names of the files could be anywhere under the wwwdirectory, not just one level down), and look at the single-quote (SQ) and double-quote (DQ) versions of the command:

这是常规的 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

The single quotes pass the material enclosed direct to the command. Thus, in the SQ command line, the shell that launches findremoves the enclosing quotes and the findcommand sees its $9argument as:

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

echo $(basename {})

because the shell removes the quotes. By comparison, the material in the double quotes is processed by the shell. Thus, in the DQ command line, the shell (that launches find— not the one launched byfind) sees the $(basename {})part of the string and executes it, getting back {}, so the string it passes to findas its $9argument is:

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

echo {}

Now, when finddoes its -execaction, in both cases it replaces the {}by the filename that it just found (for sake of argument, www/pics/index.html). Thus, you get two different commands being executed:

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

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

There's a (slight) notational cheat going on there — those are the equivalent commands that you'd type at the shell. The $2of the shell that is launched actually has no quotes in it in either case — the launched shell does not see any quotes.

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

As you can see, the DQ command simply echoes the file name; the SQ command runs the basenamecommand and captures its output, and then echoes the captured output. A little bit of reductionist thinking shows that the DQ command could be written as -printinstead of using -exec, and the SQ command could be written as -exec basename {} \;.

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

If you're using GNU find, it supports the -printfaction which can be followed by Format Directivessuch that running basenameis unnecessary. However, that is only available in GNU find; the rest of the discussion here applies to any version of findyou're likely to encounter.

如果您使用的是 GNU find,它支持-printf可以跟在格式指令之后的操作,这样basename就不需要运行了。但是,这仅在 GNU 中可用find;此处的其余讨论适用于find您可能遇到的任何版本。

回答by Gilles Quenot

Try this instead :

试试这个:

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

If you want to do it with a pipe (more resources needed) :

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

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

回答by maosmurf

Thats how I batch resize files with imagick, rediving output filename from source

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

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

回答by Sheldon

I had to accomplish something similar, and found following the practices mentioned for avoiding looping over find's output and using find with sh sidestepped these problems with {}and -printfentirely.

我必须要完成类似的东西,发现以下避免遍历查找的输出,并使用find与SH提到的做法回避了这些问题{},并-printf完全。

You can try it like this:

你可以这样试试:

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

The summary is "Don't reference {} directly inside of a sh -c but instead pass it to sh -c as an argument, then you can reference it with a number variable inside of sh -c" the find-shis just there as a dummy to take up the $0, there is more utility in doing it that way and using {}for $1.

总结是“不要直接在 sh -c 内部引用 {},而是将它作为参数传递给 sh -c,然后你可以用 sh -c 内部的数字变量引用它”,find-sh它只是作为一个虚拟占用的$0,有做它的方式和使用更多实用{}$1

I'm assuming the use of echois really to simplify the concept and test function. There are easier ways to simply echo as others have mentioned, But an ideal use case for this scenario might be using cp, mv, or any more complex commands where you want to reference the found file names more than once in the command and you need to get rid of the path, eg. when you have to specify filename in both source and destination or if you are renaming things.

我假设使用echo真的是为了简化概念和测试功能。有简单地附和如其他人所说的更简单的方法,但是,一个理想的使用情况下,这种情况可能会使用cpmv或要引用找到的文件名不止一次在命令中的任何更复杂的命令,你需要得到摆脱路径,例如。当您必须在源和目标中指定文件名时,或者要重命名内容时。

So for instance, if you wanted to copy onlythe html documents to your public_htmldirectory (Why? because Example!) then you could:

因此,举例来说,如果你想复制在HTML文档到你的public_html目录(为什么?因为实例?!),那么你可以:

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

Over on unix stackexchange, user wildcard's answer on looping with find goes into some great gems on usage of -execand sh -c. (You can find it here: https://unix.stackexchange.com/questions/321697/why-is-looping-over-finds-output-bad-practice)

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