关于shell:Linux查找命令隐藏基目录

Linux Find Command Hide Base Directory

编者按:这个问题不明确,因为它将两个无关的任务混为一谈:(a)使用-printf操作仅打印文件名(无路径组件),以及(b)在通过{}采取-exec行动的情况下,仅以文件名作为论据。(a)被错误地认为是实施(b)的一种方式。这种混淆导致至少有一个答案集中在(a)。

我试图使用find命令列出某个路径中的所有目录,但在输出中隐藏该路径。-printf"%p"标志应该隐藏/path/to/directory/,但不起作用:

1
2
find /path/to/directory/* -maxdepth 0 -type d -printf"%P
" -exec sudo tar -zcpvf {}.tar.gz {} \;

例如,上面的命令将使用以下内容创建存档:

1
2
3
/path/to/directory/dir1
/path/to/directory/dir2
/path/to/directory/dir3

如何修改命令以输出:

1
2
3
dir1
dir2
dir3

请注意:我知道我可以通过cd/path/to/directory/然后使用find命令来完成上述操作,但重要的是避免使用cd,并使用单个find命令来完成所有操作。


1
find  /path/to/directory/* -maxdepth 0 -type d -exec basename {} \;

查找所有目录find /path/to/directory/* -maxdepth 0 -type d

-exec basename {} \;—使用find中的结果参数执行basename命令


因为您只处理子目录(直接子目录),所以shell循环可能是更简单的解决方案:

1
(cd"/path/to/dir" && for d in */; do sudo tar -zcpvf"${d%/}".tar.gz"$d"; done)

我知道你想避开cd,但是通过将整个命令封闭在(…)中,它在一个子shell中运行,所以当前shell的工作目录。保持不变。

这个答案的其余部分讨论了如何使用gnu find来解决这个问题。

-execdir解决方案也可以与BSD/OSXfind一起使用,实际上在那里会更简单。

至于让find-printf只打印文件名,不带任何目录组件:使用%f格式说明符:

1
2
find /path/to/dir/* -maxdepth 0 -type d -printf"%f
"

这将只打印指定目录中所有直接子目录的名称。

但是,您打印的内容对使用-exec操作时{}扩展到的内容没有影响:{}始终扩展到匹配的路径,该路径始终包含指定为输入的路径组件。

但是,-execdir行动可能给你想要的:

  • 在执行指定的命令之前,它将更改为手头的目录

  • 它将{}扩展到./—也就是说,仅仅是以./作为前缀的文件名(在本例中是目录名),并将其传递给指定的命令。

因此:

1
find /path/to/dir -mindepth 1 -maxdepth 1 -type d -execdir sudo tar -zcpvf {}.tar.gz {} \;

警告:-execdir只对作为输入路径后代的文件进行描述;奇怪的是,对于输入路径本身,{}仍然按原样扩展到输入路径[1].

因此,上面的命令不将globbing(/path/to/dir/*-maxdepth 0一起使用,而是使用/path/to/dir并让find进行包含项的枚举,这些包含项比在级别1上要多,因此-maxdepth 1;由于不应包括输入路径本身,因此必须添加-mindepth 1

注意,这样的行为就有了细微的不同:find总是在枚举中包含隐藏项,而shell的globbing(*)在默认情况下则没有。

如果需要去掉{}扩展中的./前缀,则需要做更多的工作:

1
2
find /path/to/dir -mindepth 1 -maxdepth 1 -type d \
  -execdir sh -c 'd=${1##*/}; sudo tar -zcpvf"$d".tar.gz"$d"' - {} \;

使用shell(sh允许使用shell参数扩展(${1##*/}实际上会除去任何路径组件)来除去./前缀。注意shell分配给$0的伪参数-,我们对此不感兴趣;{}随后变为shell参数$1

[1]如果一个输入路径本身是相对的,那么在./已经预先准备好的情况下;请注意,bsd/osx find并没有表现出这种怪癖:它总是将{}扩展到仅仅一个文件名,而没有任何路径组件。