我正在寻找一种方法来递归获取UNIX中给定目录中所有文件的路径。 (不使用查找)
给出这样的树
lab_assignment:
file1.txt
file2.txt
subdir1
subdir2
./subdir1:
file11.txt
./subdir2:
file21.txt
我需要一个命令,该命令将递归列出lab_assignment中包含的所有文件的路径。
./file1.txt
./file2.txt
./subdir1/file11.txt
./subdir2/file21.txt
我在一个作业中发现了这一点,因此该工具集被有意限制。我知道您可以使用find
命令轻松完成此操作,但是此分配不允许使用find
,因此必须有一种无需使用find
的方法,但我无法提出一个。
老师告诉我们,仅使用ls
,引号,也许使用管道和grep
即可实现这一目标。
在最近的任务中,我遇到了这个问题,尽管这并不是它的主要重点。因此,我设法完全避免了该问题,但后来发现自己对解决该问题的正确方法感到好奇。
此问题的解决方案用于以下任务:
递归输出文件内容,其文件名以.txt结尾
递归计算所有文件以f开头的行数
实用程序like
cat和wc
使用其stdin中提供的文件名,并且没有内置递归功能,因此您必须提供文件路径列表。
我决定尽可能避免出现此问题,并这样做:
cat *.txt */*.txt */*/*.txt
wc -l f* */f* */*/f*`
这有效。老师似乎很不高兴,称这种方法凌乱而丑陋,但他接受了我的报告。我对应该如何做感到好奇。
与老师纠缠了一个多月后,他同意向我展示可以做到这一点的正确方式。
他打了这个字
cat `ls -R $PWD`
这似乎只会导致错误,并且不会创建任何与所需结果类似的东西。
然后他想出了:
cat $PWD/`ls -R`
这件事至少起到了一定作用,但仍然-甚至没有达到要求的结果。
老师然后告诉我,这是他上这门课程的第一年,这是很久以前由uni的不同部门设计的,并且作为UNIX用户,他将使用find
和< strong> 他不知道解决方案
但是他发誓一定在课程的设计文档中某个地方看到了它,或者在某个地方看到了它。
因此,有没有一种方法可以在没有找到的情况下获取文件路径的递归列表? UNIX技巧和心理体操中哪一个巧妙的技巧是关键?
答案 0 :(得分:2)
我需要一个命令,该命令将递归列出所有文件的路径。 [...]
该命令应尽可能简单。
当bash> 4.0且当前目录中至少有一个文件时,可以使用
shopt -s globstar
printf ./%s\\n **
当工作目录可以为空时,使用
shopt -s globstar nullglob
a=(**)
(( ${#a[@]} > 0 )) && printf ./%s\\n "${a[@]}"
并解决明确的任务
以递归方式输出文件内容,其文件名以.txt结尾
shopt -s globstar
cat **/*.txt
递归计算所有文件名以f开头的行数
shopt -s globstar
wc -l **/f*
请注意,**/*
也与工作目录中的文件匹配。展开的列表可能包含也可能不包含带有/
的路径。
老师告诉我们,仅使用ls,引号,也许使用管道和grep即可实现这一目标
我不这么认为,至少不可靠。如果任何文件/目录名称包含换行符,则无法仅使用 提及的机制使其工作。
如果您可以假设»没有路径包含换行符«,甚至是»没有路径包含空格«,那么分配就可以解决。但是,我找不到使用ls
的解决方案,因为ls
从不输出完整路径,并且我们缺少构建工具(例如sed
,递归或循环)输出的完整路径。
grep -RLE '$^'
-R
将grep
递归应用于所有文件。 -E '$^'
是一个永远不会匹配的正则表达式。 -L
打印所有不匹配的文件。
cat $(grep -RLE '$^' | grep -E '\.txt$')
wc -l $(grep -RLE '$^' | grep -E '(^|/)f[^/]*$')
我认为,此作业很糟糕,并不是因为它可能无法解决,而是因为它教给了不良做法(例如,使用假设不正确的工具,...)。
答案 1 :(得分:1)
TL; DR:您只能使用外壳程序,而无需外部工具。在下面您也可以仅使用ls -R
加上一些shell或仅使用工具来完成此操作。看看我的other answer.
我对如何正确地执行此操作非常感兴趣。
“正确”的方式是find
。那是完成这项工作的工具。它是在POSIX中定义的:
find实用程序应从路径指定的每个文件中递归地从目录层次结构中降级,为遇到的每个文件评估由OPERANDS部分中描述的原语组成的布尔表达式。
我会让您的老师从疑惑中受益,并认为这不是一些琐碎的学术活动。我假设该作业具有一些实用性,例如:
“您已经被放入一个损坏的UNIX系统中,该系统已删除了大部分工具集,包括
find
命令。您需要对目录结构进行分类。您所拥有的只是{{1} },ls
和经典的Bourne shell。您知道文件名是常规名称:文件名中没有空格,文件中没有前导破折号,文件中没有控制字符,等等。您将如何做? (1)
(到目前为止还没有实现。我曾经对一个由于错误的grep
指令而丢失的/usr/bin
的系统进行了分类。我只能使用像shell这样的内置程序来诊断和恢复它mount
。)
为此:
echo
首先是“正确”的方式。这是我们的目标输出:
$ tree
.
├── file1.txt
├── file2.txt
├── subdir1
│ ├── file11.txt
│ ├── file12.c
│ └── subdira
│ ├── file1a1.c
│ └── file1a1.txt
├── subdir2
│ └── file21.txt
因此,有没有一种方法可以在没有找到的情况下获取文件路径的递归列表?
是的。我们可以在以下条件下使用内置的shell来解决 :
$ find . -name '*.txt'
./file2.txt
./file1.txt
./subdir1/file11.txt
./subdir1/subdira/file1a1.txt
./subdir2/file21.txt
没有外部程序,只有Shell内置。它很容易扩展:您可以调用$ r() {
d=${1:-.}
for f in *
do
if test -f "$f"; then
case "$f" in *.txt)
echo $d/$f
;;
esac
elif test -d "$f"; then
( cd "$f"; r "$d/$f" )
fi
done
}
$ r
./file1.txt
./file2.txt
./subdir1/file11.txt
./subdir1/subdira/file1a1.txt
./subdir2/file21.txt
之类的程序,而无需回显匹配项。由于全部为外壳,因此您可以跟踪变量以求和,等等。
但是,这几乎没有表现力,并且要排除“怪异”文件名。另外,它与find解决方案也不相同:wc
的输出按inode顺序排列,而我的shell解决方案按语言环境排列。这些可能与我的示例不同。
这也不是进行递归下降的唯一方法,而只是显而易见的方法。有关没有find
的递归下降的替代版本,请参见Rich's POSIX sh tricks。
(1)如果您的讲师认为可以通过包含空格,控制字符,破折号等深奥的文件名来正确完成此操作,那么建议您的讲师阅读David Wheeler的treatise (rant)关于这个问题。
答案 2 :(得分:0)
如果您正在寻找一个纯工具解决方案(与my other answer中的一个纯shell解决方案相对),那么有几个选择:
tar cvf /dev/null . | grep '\.txt$'
du -a | grep '.txt$' | cut -f2
如果您正在寻找工具和外壳的混合解决方案,那么:
ls -R . | while read l; do case $l in *:) d=${l%:};; "") d=;; *.txt) echo "$d/$l";; esac; done
后一个是我能最接近您的讲师给定参数的一个