2014-03-24 40 views
0

请找到下面的代码来显示当前文件夹中的目录。

  1. 什么是${arg##/*/}在shell脚本中的含义。 (arg#*arg##/*/给出相同的输出。)
  2. {} \;在for循环语句中的含义是什么。
for arg in `find . -type d -exec ls -d {} \;` 
do 
    echo "Output 1" ${arg##/*/} 
    echo "Output 2" ${arg#*} 
done 
+0

您应该添加周围的代码段反引号(见我的编辑),或者你的问题与格式(包括未显示的字符) –

+0

'$ {如果arg没有以'/'开始,并且/或者包含至少2个'/'(即如果/ * /'不匹配,arg#} *'只会匹配'$ {arg ##/* /}字符串) – BroSlow

+0

你明显从来没有因为shell脚本而感到沮丧,在凌晨3点不起作用。 –

回答

6

添加到@常总的有用的答案:

${arg#*}根本毫无意义扩张,其结果总是相同$arg本身,因为它剔除了最短前缀匹配任何字符(*),并且匹配任何字符的最短前缀是空字符串。

${arg##/*/} - 剥离最长前缀匹配图案/*/ - 是无用在此上下文中,因为输出路径将是./ -prefixed由于使用find .,所以不会有前缀开始/。相反,${arg##*/}将工作并剥离父路径(仅保留文件夹名称组件)。

除了它是ill-advised to parse command output in a for loop,作为@JoSo指出, 在OP的find命令过于复杂和低效 (顺便说一句,我只想澄清,该find命令列出当前文件夹的子树的所有文件夹,而不仅仅是眼前的子文件夹):

find . -type d -exec ls -d {} \; 

可以简化为:

find . -type d 

这两个命令也是这样做的:-exec ls -d {} \;只是简单地做了默认的find(暗示-print)。

如果我们把它放在一起,我们得到:

find . -mindepth 1 -type d | while read -r arg 
do 
    echo "Folder name: ${arg##*/}" 
    echo "Parent path: ${arg%/*}" 
done 

请注意,我用${arg%/*}作为第二输出项目,这条最短后缀匹配/*,从而返回路径;此外,我已经添加-mindepth 1使find还不匹配.

@JoSo,在注释中,演示了一个解决方案,它更简单,更高效;它采用-exec处理一个shell命令行和+到尽可能多的路径在一次传递尽可能:

find . -mindepth 1 -type d -exec /bin/sh -c \ 
    'for arg; do echo "Folder name: ${arg##*/}"; echo "Parent: ${arg%/*}"; done' \ 
    -- {} + 

最后,如果你有GNU find,事情变得更容易,因为你可以利用的-printf初级,它支持的占位符之类的文件名和父路径:

find . -type d -printf 'Folder name: %f\nParen path: %h\n' 

下面是基于通配(路径扩展)一个的bash-唯一的解决方案,礼貌@AdrianFrühwirth的:

买者:这需要bash 4+,与外壳选项globstar开启(shopt -s globstar) - 这是默认关闭。

shopt -s globstar # bash 4+ only: turn on support for ** 
for arg in **/ # process all directories in the entire subtree 
do 
    echo "Folder name: $(basename "$arg")" 
    echo "Parent path: $(dirname "$arg")" 
done 

请注意,我使用basenamedirname这里解析,因为他们方便忽略终止/的水珠**/不约而同地增加了它的匹配。


的再思考再处理find的输出在while循环:上关的机会,你的文件名包含嵌入\n字符,你可以分析如下,使用空字符。以单独项目(见为什么-d $'\0'而非-d ''评论时):

find . -type d -print0 | while read -d $'\0' -r arg; ... 
+0

'-d $'\ 0''并不存在。尽管'read'是内置的,它具有传递参数的'argv'(C语言)语义(就像外部程序一样,这是一件好事)。 C字符串不能有NUL字符。它和'-d'''(一个长度为零的字符串)真的是一样的,它恰好因为特定的bash实现而发挥作用。 –

+0

你真正想要什么(如果有的话)是'find -type d -exec/bin/sh -c'arg = $ 1;回声“文件夹:$ {arg ## * /}”; echo“Parent:$ {arg ##%/ *}”' - {} \;''这很复杂 –

+0

或者,对于arg中的“$ @ find -type d -exec/bin/sh -c' “;做回声“文件夹:$ {arg ## * /}”;回声家长:$ {arg ##%/ *};完成'' - {} +' –

2
  1. ${arg##/*/}是 “参数扩展” 的应用。 (在shell的手册中搜索这个术语,例如在linux shell中键入man bash)。它扩展为arg,而没有与/*/匹配的arg的最长前缀作为全局模式。例如。如果arg/foo/bar/doo,则它扩展到doo

  2. 这是不好的shell代码(类似于Bash Pitfalls上的项目#1)。 {} \;与shell没有多大关系,但更多的是find命令期望的-exec子命令。 {}被替换为当前文件名,例如,这导致find执行命令ls -d FILENAME,FILENAME将其替换为找到的每个文件。 \;充当-exec参数的终止符。参见手册页find,例如在linux shell上键入man find,然后查找字符串-exec以查找说明。

+1

你说得对,以为我误解了它,删除了评论。 – BroSlow