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
Find and basename not playing nicely
提问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 find
command is executed. The output of the single basename
is {}
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 find
actually 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 echo
repeat to standard output what basename
would 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 www
directory, 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 find
removes the enclosing quotes and the find
command sees its $9
argument 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 find
as its $9
argument is:
因为外壳删除了引号。相比之下,双引号中的内容是经过shell处理的。因此,在 DQ 命令行中,shell(启动find
- 而不是由 启动的find
)看到$(basename {})
字符串的一部分并执行它,返回{}
,因此它find
作为$9
参数传递给的字符串是:
echo {}
Now, when find
does its -exec
action, 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 $2
of 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 basename
command 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 -print
instead 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 -printf
action which can be followed by Format Directivessuch that running basename
is unnecessary. However, that is only available in GNU find
; the rest of the discussion here applies to any version of find
you'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 -printf
entirely.
我必须要完成类似的东西,发现以下避免遍历查找的输出,并使用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-sh
is 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 echo
is 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
真的是为了简化概念和测试功能。有简单地附和如其他人所说的更简单的方法,但是,一个理想的使用情况下,这种情况可能会使用cp
,mv
或要引用找到的文件名不止一次在命令中的任何更复杂的命令,你需要得到摆脱路径,例如。当您必须在源和目标中指定文件名时,或者要重命名内容时。
So for instance, if you wanted to copy onlythe html documents to your public_html
directory (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 -exec
and sh -c
. (You can find it here: https://unix.stackexchange.com/questions/321697/why-is-looping-over-finds-output-bad-practice)
在 unix stackexchange 上,用户通配符关于使用 find 循环的答案进入了一些关于-exec
and使用的伟大宝石sh -c
。(你可以在这里找到它:https: //unix.stackexchange.com/questions/321697/why-is-looping-over-finds-output-bad-practice)