我有一个根文件夹,其中有许多目录和文件。我需要保存每个子目录中名为的内容列表list.txt
。
假设我有
A
|
-----B
| |---Z
| |---a.txt
| |---b.jpg
|
|
|----C
| |--a.txt
| |--b.txt
运行该命令它应该list.txt
在每个子目录中给出一个用逗号分隔的内容。
我已经#评论了应该包含的内容...
A
|
-----B
| |---Z
| |---a.txt
| |---b.jpg
| |---list.txt # Z,a.txt,b.jpg
|
|
|----C
| |--a.txt
| |--b.txt
| |--list.txt # a.txt,b.txt
我能列出的最接近的文件是
find . -maxdepth n -type f -printf '%f\n'
但我不知道如何单独保存内容。
请提出一些建议。
答案1
下面的脚本将添加一个列表到所有子以递归方式从目录中获取目录:
#!/usr/bin/env python3
import os
import sys
for root, dirs, files in os.walk(sys.argv[1]):
for dr in dirs:
dr = os.path.join(root, dr)
open(os.path.join(dr, "list.txt"), "wt").write(
",".join(f for f in os.listdir(dr) if f != "list.txt")
)
使用
- 将脚本复制到一个空文件中,另存为
dirlists.py
使用主目录作为参数运行它:
python3 /path/to/dirlists.py /path/to/maindirectory
笔记
如上所述,脚本将 list.txt 添加到所有子目录. 如果你也需要或想要在主要的请提及您的目录的 (root)-dir。
解释
递归列出(遍历)目录内的所有目录:
for root, dirs, files in os.walk(sys.argv[1]): for dr in dirs: dr = os.path.join(root, dr)
为每个内容创建一个内容列表:
os.listdir(dr)
打开(如有必要,创建)文本文件并写入列出的内容,排除可能的早期文件名为
list.txt
:open(os.path.join(dr, "list.txt"), "wt").write( ",".join(f for f in os.listdir(dr) if f != "list.txt") )
编辑
根据评论的要求:
如果您需要list.txt
以逗号结尾的行,只需替换:
",".join(f for f in os.listdir(dr) if f != "list.txt")
经过:
",".join(f for f in os.listdir(dr) if f != "list.txt")+","
注意缩进,将替换项放在完全相同的位置
答案2
为了使其递归,首先打开globstar
shopt -s globstar
然后,在父目录中(A
在您的结构中),您可以运行:
for d in **; do [[ -d "$d" ]] && (find "$d" -mindepth 1 -maxdepth 1 \( -not -name "list.txt" \) -printf '%f,' | sed 's/,$/\n/') |tee "$d"/list.txt ; done
或者稍微更易读一些
for d in **; do
[[ -d "$d" ]] &&
(find "$d" -mindepth 1 -maxdepth 1 \( -not -name "list.txt" \) -printf '%f,' | sed 's/,$/\n/') | tee "$d"/list.txt
done
如果目录a
包含
├── a
│ ├── 1.txt
│ ├── 2.txt
│ ├── 3.txt
│ ├── a badly named file &
│ └── Z
a
将在目录中创建一个如下所示的列表:
2.txt,1.txt,3.txt,Z,a badly named file &
find
不会产生有序的输出,所以如果这是个问题,我必须想出一个更好的方法。表达式\( -not -name "list.txt" \)
中的find
是为了防止列表包含自身,而表达式sed
纯粹是为了删除尾随的逗号。可惜这些额外的字节太多了。
globstar
完成后你可能想关闭
shopt -u globstar
答案3
单行版本
首先使用find
来获取目录,然后 shell 为您完成工作:
$ tree
.
├── a_directory
│ ├── a_file
│ ├── a_subdir
│ └── mv-files.py
└── another_dir
├── {file}1
└── {file}2
3 directories, 4 files
$ find -type d -exec bash -c 'cd $1; find -maxdepth 1 -not -name "." -not -name "list.txt" -printf "%f," | awk "{print substr(\$0,0,length(\$0)-1)}" > list.txt' bash "{}" \;
$ cat a_directory/list.txt
mv-files.py,a_file,list.txt,a_subdir
其运作方式如下:
- 我们使用
find
命令来-type d
过滤掉所有目录 -exec
带有终止符的语句\;
允许我们为每个参数运行指定的命令find
-exec
我们使用标志运行,并将参数传递给它,bash
参数是外部所在的目录-c
$0
bash
$1
find
bash
将进入给定的目录并使用find
参数- maxdepth 1
将该命令限制在该子目录中。-not -name "."
将排除.
目录链接,即对自身的引用。- 接下来,我们将文本传递给
awk
,它的作用只是删除,
给出的最后一个,find
以便我们得到一个有效的 CSV 列表。请注意使用双引号和\$
。这是为了简化引用并防止 bash 将其解释$0
为其自己的位置参数,而是解释为awk
命令。 - 内部获得的所有项目列表都将通过重定向
find
发送。list.txt
>
-not -name "list.txt"
对此的额外改进可能是在内部命令中使用find
以排除列表文件本身(因为由于>
始终首先创建要写入的文件,list.txt
因此也会出现在列表中)。
就我个人而言,如果我为自己做这件事,我会用分隔符写文件列表\0
以避免处理困难的文件名,但这也需要记住list.txt
格式\0
并为其编写解析器函数。
完整脚本版本
为了便于阅读,这里提供完整的脚本版本,而不是单行版本。
脚本:
#!/bin/bash
# note : this assumes you run the script from top-most directory
find -type d | while IFS= read -r directory;
do
cd "$directory"
find -maxdepth 1 -not -name "." -not -name "list.txt" -printf "%f," |
awk "{print substr(\$0,0,length(\$0)-1)}" > list.txt
cd - > /dev/null
done
请注意,此脚本从最顶层目录运行。如果脚本存储在同一目录中,它还会将自身包含在列表中。如果您将其放置到~/bin
例如(或属于$PATH
变量的任何其他目录)并运行它,则脚本名称不会出现在列表中。
测试 :
$ tree
.
├── a_directory
│ ├── a_file
│ ├── a_subdir
│ └── mv-files.py
├── another_dir
│ ├── {file}1
│ └── {file}2
└── make_lists.sh
3 directories, 5 files
$ ./make_lists.sh
$ tree
.
├── a_directory
│ ├── a_file
│ ├── a_subdir
│ │ └── list.txt
│ ├── list.txt
│ └── mv-files.py
├── another_dir
│ ├── {file}1
│ ├── {file}2
│ └── list.txt
├── list.txt
└── make_lists.sh
3 directories, 9 files
$ cat a_directory/list.txt
mv-files.py,a_file,a_subdir