我正在尝试整理我的照片,由于各种历史原因,这些照片分散在我的系统中。为了让我能够开始这项任务,我一直在尝试使用命令行来构建包含一个或多个 jpg 文件的所有目录的列表。我确信我不必担心寻找其他图像文件格式,但我确实必须允许 jpg 以大小写形式出现。
我希望每个目录名称在最终列表中只出现一次。举个例子,如果我有以下目录,每个目录都包含一个或多个 jpg 或 JPG 文件......
~Mike/Pictures
~Mike/Pictures/London/Olympics
~Mike/Pictures/London
~Mike/Pictures/London/Holiday
~Mike/Photos
~Mike/Family History/Swaine
我希望每个目录只列出一次结果 - 无论它可能包含多少图像文件 - 最好先排序然后写入文件
~Mike/Family History/Swaine
~Mike/Photos
~Mike/Pictures
~Mike/Pictures/London
~Mike/Pictures/London/Holiday
~Mike/Pictures/London/Olympics
我的命令行技能还达不到这个水平!我可以使用许多更简单形式的单个命令,但是一旦它们变得复杂和/或必须通过管道传输,事情往往会出错。
答案1
假设 JPEG 图像文件的后缀为.jpg
或.JPG
:
find "$HOME" -type f \( -name '*.jpg' -o -name '*.JPG' \) \
-exec sh -c 'for d; do dirname "$d"; done' sh {} + | sort -u -o jpeg_dirs.txt
这依赖于您的目录名称中没有包含换行符的时髦目录名称。
使用 GNU find
:
find "$HOME" -type f \( -name '*.jpg' -o -name '*.JPG' \) -printf '%h\n' | sort -u -o jpeg_dirs.txt
这些find
命令将查找您的主目录下的所有 JPEG 图像,并打印找到它们的目录的名称。将sort -u
获取此目录名称列表,对其进行排序并删除重复项。结果将写入jpeg_dirs.txt
当前目录中的文件中。
2021 年初(3.3 年后)回顾这一点,我有点畏缩,因为我上面的解决方案虽然本身并没有错,但有点落后。它还对“好的文件名”(没有换行符)做出了明显的假设。
当你用来find
搜索目录时,不要像我上面那样搜索常规文件;实际上搜索目录。一旦我们有了目录,我们就可以查看每个目录,看看是否有匹配的文件*.jpg
或*.JPG
(其他文件名后缀很容易添加):
find "$HOME" -type d -exec bash -O nullglob -O dotglob -O extglob -c '
for dirpath do
set -- "$dirpath"/*.@(jpg|JPG)
[ "$#" -eq 0 ] || printf "%s\n" "$dirpath"
done' bash {} +
这会从主目录向下查看每个目录,并尝试扩展*.@(jpg|JPG)
每个目录中的通配模式。该模式也可以写为两个单独的模式,*.jpg
并且*.JPG
与我们正在查找的所有文件相匹配。如果一个名称匹配,我们就假设这是一个我们想要输出其名称的目录。这会给仅包含以下内容的目录带来误报子目录带有这些后缀。
我们运行内部脚本的 shell 选项bash
允许我们匹配隐藏名称 ( dotglob
),允许通配模式在不匹配任何内容时完全消失而不是保持未展开状态 ( nullglob
),并允许我们使用ksh
-inspired 扩展通配模式@(...|...)
。
使用zsh
外壳:
typeset -U list=(~/**/*.(jpg|JPG)(.DN:h))
print -rC1 $list
这将创建一个数组变量 ,list
它具有仅存储唯一元素的属性。它被初始化为扩展文件名通配模式的结果。该模式匹配主目录中或主目录下的所有 JPEG 图像文件,:h
最后的 会从生成的路径名中删除实际文件名。使.
模式仅匹配常规文件,并且D
和的作用N
类似于.dotglob
nullglob
bash
答案2
一种简单的方法是列出所有.jpg
文件,然后去掉文件的基本名称(最后斜杠后面的部分),并删除重复项。您可以使用sed
删除最后一个斜杠之后的每行部分。有一个删除重复项的命令,称为uniq
,但它假定输入已排序;如果你无论如何都需要排序,你可以让其sort
进行唯一化。
find ~Mike -iname '*.jpg' | sed 's!/[^/]*$!!' | sort -u >directories_with_jpeg_files.txt
这假设所涉及的目录或文件的名称中没有换行符。带有换行符的文件名在正常情况下不会出现,但请注意文件名是否可能由敌对者选择(例如,如果您正在处理已上传到服务器的文件并且上传者可以选择文件名) 。
如果存在包含大量 JPEG 文件的目录,而没有包含 JPEG 文件的目录不多,则此方法会花费大量时间来报告冗余文件。一旦 find 在目录中找到某些内容,就无法告诉它快捷方式。但是您可以将 find 限制为目录并告诉它在每个目录中搜索 JPEG 文件。然而,这会增加不包含 JPEG 文件的目录的成本,因此如果有许多 JPEGless 目录,性能可能会很差。
find ~Mike -type d -exec sh -c '
for d do
set -- "$d/*.[Jj][Pp][Gg]";
if [ -e "$1" ]; then printf %s\\n "$d"; fi
done
' sh {} + | sort -u >directories_with_jpeg_files.txt
或者,在 zsh 中,您可以使用**
通配符递归遍历目录,(#i)
不区分大小写地匹配后面的路径组件,从而使整个目录树中的模式**/(#i)*.jpg
匹配*.jpg
and *.JPG
(等等)。在 glob 限定符中.Jpg
添加历史修饰符h
以提取目录部分。将其填充到数组变量中,并使用参数扩展标志dirs=(…)
提取该数组的唯一元素。u
set -o extendedglob # for (#i); best in ~/.zshrc
dirs=(~Mike/**/(#i)*.jpg(:h))
print -lr -- ${(u)dirs} >directories_with_jpeg_files.txt
与上面的按目录检查方法等效的是使用e
glob 限定符。
print -lr ~Mike/**/*(/e\''set -- $REPLY/*.(#i)jpg(N[1]); (($# != 0))'\') >directories_with_jpeg_files.txt
答案3
find . -iname '*.jpg' -execdir sh -c 'pwd' _ {} + | sort -u > dirs_with_jpegs.txt
find
假设您实现了支持,应该可以很好地工作-execdir
(可能确实如此)。-execdir
在找到的文件所在的目录中执行命令。在本例中,我们执行命令pwd
,该命令打印目录的名称。我们sh -c
用 strip 参数包装命令。 (一些(全部?)实现find
需要{}
参数替换,这将是当前目录中的 jpeg 文件列表。我们想忽略该列表,只打印目录。)