问题
- 我一直在努力编写一个 Bash 命令,该命令能够递归搜索目录,然后返回包含唯一隐藏文件和/或隐藏目录的每个子目录(达到某个最大深度)的路径。
视觉解释
- 考虑以下文件系统摘录:
+--- Root_Dir
| +--- Dir_A
| | +--- abc.txt
| | +--- 123.txt
| | +--- .hiddenfile
| | +--- .hidden_dir
| | | +--- normal_sub_file_1.txt
| | | +--- .hidden_sub_file_1.txt
| |
| +--- Dir_B
| | +--- abc.txt
| | +--- .hidden_dir
| | | +--- normal_sub_file_2.txt
| | | +--- .hidden_sub_file_2.txt
| |
| +--- Dir_C
| | +--- 123.txt
| | +--- program.c
| | +--- a.out
| | +--- .hiddenfile
| |
| +--- Dir_D
| | +--- .hiddenfile
| | +--- .another_hiddenfile
| |
| +--- Dir_E
| | +--- .hiddenfile
| | +--- .hidden_dir
| | | +--- normal_sub_file_3.txt # This is OK because its within a hidden directory, aka won't be checked
| | | +--- .hidden_sub_file_3.txt
| |
| +--- Dir_F
| | +--- .hidden_dir
| | | +--- normal_sub_file_4.txt
| | | +--- .hidden_sub_file_4.txt
所需输出
- 我正在寻找的命令会输出
./Root_Dir/Dir_D ./Root_Dir/Dir_E ./Root_Dir/Dir_F
Dir_D
因为它只包含隐藏文件。Dir_E
因为它只包含我正在搜索的级别的隐藏文件和隐藏目录。Dir_F
因为它只包含我正在搜索的级别的隐藏目录。
尝试
- 我试图使用该
find
命令来获取我正在寻找的结果,但我似乎无法弄清楚我需要将输出传输到哪个其他命令或我应该使用哪些其他选项。 - 我认为有效的命令看起来像这样:
bob@linux:/$ find ./Root_Dir -mindepth 2 -maxdepth 2 -type d -name "*." -type -f -name "*." | command to see if these are the only files in that directory
答案1
我对此有一个不太漂亮的解决方案。
以脚本形式:
#!/bin/bash
# Echo whatever is passed to fail, and then exit with a status of 1
fail() {
echo >&2 "$@"
exit 1
}
# If the number of arguments are less or more than what
# we expect, throw help syntax and exit.
if [ $# -gt 2 ] || [ $# -lt 2 ]
then
fail "
$0 check for directories that only contain hidden listings
Usage: $0 <Directory to search from> <Depth to check>
"
fi
# Assign parameters
root_dir=$1
depth=$2
# Find a list of directories that contain files OR subdirectories
# that are hidden
readarray -t dirs < <(export LC_ALL=C
find "$root_dir" -mindepth "$depth" -maxdepth "$depth" \
-name ".*" \( -type d -o -type f \) -execdir pwd \; | sort -u
)
# Of the dirs we found, do any of them return any listings with a
# default ls execution? If so, that directory cannot only contain
# hidden listings.
final=()
for dir in "${dirs[@]}"
do
notExclusive="$(ls -- "$dir")"
if [ "$notExclusive" = "" ]
then
final+=("$dir")
fi
done
# The array final contains the directories who only contain hidden
# files/subdirectories.
printf '%s\n' "${final[@]}"
基本上,我们只是找到包含隐藏列表的目录(按问题指定的深度为 2),将它们加载到数组中,如果ls
没有标志返回任何内容,我们可以得出结论,该目录中只包含隐藏列表,满足我们的标准。
解释一下为什么你只需要find
根据 OP 上的评论调用一次。该命令find
具有用于基本逻辑的运算符,-a 是连接两个表达式逻辑 AND 时的默认行为,!如逻辑“非”,-o 如逻辑“或”。
此方法假设目录路径不包含换行符,如果包含换行符,readarray
则会错误地分隔每个目录路径。
丑陋的“一行”:
readarray -t dirs < <(export LC_ALL=C; find ./Root_Dir -mindepth 2 -maxdepth 2 -name '.*' \( -type d -o -type f \) -execdir pwd \; | sort -u); final=(); for dir in "${dirs[@]}"; do notExclusive="$(ls -- "$dir")"; if [ "$notExclusive" = "" ]; then final+=("$dir"); fi; done; printf '%s\n' "${final[@]}"
答案2
使用find
和bash
,以下命令查找所有目录。然后,它用于bash
测试找到的任何目录是否包含任何非隐藏名称。它通过扩展*
每个目录中的 glob 模式并计算由此产生的名称数量来实现此目的。如果数量为零,则输出目录的路径名。默认情况下,全局模式*
不会扩展到隐藏名称。
find . -type d -exec bash -O nullglob -c '
for dirpath do
set -- "$dirpath"/*
[[ $# -eq 0 ]] && printf "%s\n" "$dirpath"
done' bash {} +
我nullglob
在内联bash -c
脚本中设置 shell 选项,以便在没有匹配项时删除模式本身。
使用 时/bin/sh
,您无权访问nullglob
shell 选项,因此您必须测试模式扩展为的单个名称是否实际存在。如果没有,则该目录仅包含隐藏名称。
find . -type d -exec sh -c '
for dirpath do
set -- "$dirpath"/*
[ "$#" -eq 1 ] && [ ! -e "$1" ] && printf "%s\n" "$dirpath"
done' sh {} +
如果你想避免发现空的目录,在主目录! -empty
之前添加(假设您的实现通过常见测试支持此非标准)。-exec
find
find
答案3
和zsh
:
print -rC1 -- **/*(NDF^e['()(($#)) $REPLY/*(NY1)'])
要排除超过一定深度的目录,可以使用~
排除运算符排除它们:
set -o extendedglob
print -rC1 -- **/*~*/*/*(NDF^e['()(($#)) $REPLY/*(NY1)'])
但请注意,它不会停止zsh
首先下降到它们中。
如果你只想要深度为 2 的,你可以这样做:
print -rC1 -- */*(NDF^e['()(($#)) $REPLY/*(NY1)'])
(尽管要注意它遵循深度 1 的符号链接)
print -rC1
在柱上打印r
aw1
C
**/*
递归通配符(...)
全局限定符N
:N
ullglob: 如果没有匹配,不要抱怨D
:D
otglob: 不要忽略隐藏文件F
:仅空(除和F
之外至少有一个条目)目录。.
..
^
: 否定以下限定符e['code']
:e
评估code
是否选择该文件。()(($#)) args
: 如果传递至少一个参数则返回 true$REPLY
,在 中code
,保存当前正在考虑的文件。$REPLY/*
中的非隐藏文件$REPLY
Y1
:在第一个找到的文件后停止。