Shell如何检查一个目录是否为空?
liuian 2024-11-28 00:43 54 浏览
如何检查一个目录是否为空?如何检查是否存在任何 *.mpg 文件,或者计算有多少个这样的文件?
在 Bash 中,你可以通过使用 nullglob 和 dotglob 选项(这会改变 globbing 的行为),以及一个 array,安全而简便地计算文件数量:
# Bash
shopt -s nullglob dotglob
files=(*)
(( ${#files[*]} )) || echo 目录为空
shopt -u nullglob dotglob
当然,你可以使用任何你喜欢的 glob,而不仅仅是 。比如,.mpg 或者 /my/music/*.mpg 都可以正常工作。
请注意,你需要对目录拥有读取权限,否则它将始终显示为空。
一些人不喜欢 nullglob,因为没有匹配的 glob 会完全消失,这会让像 ls 这样的程序感到困惑。如果将 ls *.zip 错误地输入为 ls *.zpi,可能会导致显示所有文件(对于这种情况,考虑设置 failglob)。在子 shell 中设置 nullglob 可以避免意外地改变其余部分的 shell 设置,但代价是额外的 fork()。如果你想避免设置和取消设置 shell 选项,你可以将所有内容放入一个子 shell:
# Bash
if (shopt -s nullglob dotglob; f=(*); ((! ${#f[@]}))); then
echo "当前目录为空。"
fi
这种方法的另一个缺点(除了额外的 fork())是,当子 shell 退出时,数组会丢失。如果你计划稍后使用这些文件名,那么它们必须再次检索。
这两个示例都会扩展 glob 并将结果文件名存储到一个数组中,然后检查数组中元素的数量是否为 0。如果你实际上想要查看有多少个文件,只需打印数组的大小,而不是检查它是否为 0:
# Bash
shopt -s nullglob dotglob
files=(*)
echo "当前目录包含 ${#files[@]} 个文件。"
如果你不介意在数组中放入一个不存在的文件名(而不是一个空数组),你也可以避免使用 nullglob:
# Bash
shopt -s dotglob
files=(*)
if [[ -e ${files[0]} || -L ${files[0]} ]]; then
echo "当前目录不为空。它包含以下文件:"
printf '%s\n' "${files[@]}"
fi
如果目录中没有文件,则没有 nullglob,这个 glob 将被添加为数组中的唯一元素。由于 * 是一个有效的文件名,我们无法简单地检查数组是否包含一个字面上的 *。因此,我们检查数组中的内容是否作为文件存在。需要注意的是,-L 测试是必需的,因为 -e 如果第一个文件是一个 dangling symlink,将失败。
如果您不关心有多少匹配的文件,并且不想将结果存储在数组中,您可以使用Bash的compgen命令。不幸的是,由于一个bug,您需要使用一个技巧让其识别dotglob:
# Bash
if (shopt -s dotglob; : *; compgen -G '*' >/dev/null); then
echo "当前目录不为空。"
else
echo "当前目录为空。"
fi
或者您可以使用扩展的glob:
# Bash
# 可以通过为整个脚本启用extglob来避免子shell。
# 这样做是安全的。
if (shopt -s extglob; compgen -G '@(*|.[!.]*|..?*)' >/dev/null); then
echo "当前目录不为空。"
else
echo "当前目录为空。"
fi
您还可以使用failglob:
# Bash
if (shopt -s dotglob failglob; : ./*) 2>/dev/null; then
echo "当前目录不为空。"
else
echo "当前目录为空。"
fi
但是,请注意,如果您使用failglob,则需要子shell;下面的代码无法运行,因为failglob会引发一个shell错误,导致Bash停止运行当前命令(包括if命令,任何外部复合命令以及运行此代码的整个函数,如果它是函数的一部分),因此这仅适用于true的情况,else分支永远不会运行:
# 错误的代码!
shopt -s dotglob failglob
if { : ./*; } 2>/dev/null; then
echo "当前目录不为空。"
else
echo "当前目录为空。"
fi
如果您真的想避免使用子shell并且想要全局设置failglob,您可以使用命令eval来“捕获”shell错误,或者您可以编写一个间接展开glob的函数:
shopt -s dotglob failglob
if command eval ': ./*' 2>/dev/null; then
echo "当前目录不为空。"
else
echo "当前目录为空。"
fi
# 或者
shopt -s dotglob failglob
any_match () { local IFS=; { : "$@"; } 2>/dev/null; }
if any_match './*'; then
echo "当前目录不为空。"
else
echo "当前目录为空。"
fi
如果您的脚本需要在各种非Bash shell实现中运行,您可以尝试使用外部程序如Python、Perl或find;或者您可以尝试以下方法。请注意“magic 3 globs”^[1],因为POSIX没有dotglob选项。
# POSIX
# 这会破坏位置参数,所以确保您不需要它们。
set -- * .[!.]* ..?*
for f in "$@"; do
if test -e "$f" || test -L "$f"; then
echo "目录不为空"
break
fi
done
在这个阶段,位置参数已经加载了目录的内容,并且可以用于处理。
如果你只想计算文件数量:
# POSIX
n=0
for f in * .[!.]* ..?*; do
if test -e "$f" || test -L "$f"; then n=$((n+1)); fi
done
printf "共有 %d 个文件。\n" "$n"
在 Bourne shell 中,情况更糟糕,因为没有 test -e 或 test -L?:
# Bourne
# (当然,系统必须有 printf(1)。)
if test "`printf '%s %s %s' .* *`" = '. .. *' && test ! -f '*'
then
echo "目录为空"
fi
当然,如果 *? 存在于普通文件以外的其他内容(如目录或 FIFO),这种方法会失败。缺少 -e? 测试真的很痛苦。
这里还有另一种使用 find? 的解决方案:
# POSIX
# 打印每个文件的一个 `.` 并计算打印的字符数。
# 这个解决方案将递归。如果不想递归,请参见下面的内容。
n=$(find . -type f -exec printf %.0s. {} + | wc -m)
printf "共有 %d 个文件。\n" "$n"
如果你不希望递归,那么你需要告诉 find? 不要递归进入目录。这变得非常棘手和丑陋。GNU find? 有一个 -maxdepth? 选项来实现此功能。对于标准的 POSIX find?,你只能使用 -prune?。这留给读者作为练习。
永远不要尝试解析 ls? 的输出。即使是 ls -A? 的解决方案也可能会出错(例如,在 HP-UX 上,如果你是 root 用户,ls -A? 与非 root 用户的行为正好相反--不,我不能编造出这么愚蠢的东西)。
实际上,你可能希望完全避免直接的 *问题。通常人们想知道一个目录是否为空是因为他们想在其中的文件中进行一些操作等。要看到更大的问题。例如,这些基于 find? 的例子可能是一个合适的解决方案:
# Bourne / POSIX
find "$somedir" -type f -exec echo 发现意外的文件 {} \;
find "$somedir" -prune -empty -exec printf '%s 是空的。\n' {} \; # GNU/BSD
find "$somedir" -type d -empty -exec cp /my/configfile {} \; # GNU/BSD
最常见的情况是只需要像这样:
# Bourne / POSIX
for f in ./*.mpg; do
test -f "$f" || continue
mympgviewer "$f"
done
换句话说,提问者可能认为需要显式的空目录测试来避免出现错误消息,比如 mympgviewer: ./*.mpg: No such file or directory?,当实际上并不需要这样的测试。
对于类似 nullglob 的特性的支持是不一致的。在 ksh93 中,可以通过在模式前加上 ~(N)? 来实现每个模式的基础:
# ksh93
for f in ~(N)*; do
....
done
如果您觉得文章内容对你有一点帮助可以关注我,我在头条平台会持续分享更多实用的shell技巧和最佳实践,如果想系统的快速学习shell的各种高阶用法和生产环境避坑指南可以看看《shell脚本编程最佳实践》专栏,专栏里有更多的实用小技巧和脚本代码分享。
相关推荐
-
- 联想笔记本怎么进入安全模式
-
联想笔记本电脑进入安全模式的方法如下:1、第一步,按下【windows+R】,打开【运行】,输入【msconfig】后,点击【确定】。2、第二步,打开【系统配置】窗口后,点击【引导】。3、第三步,勾选【安全引导】后,选择需要的安全模式,通常...
-
2025-11-10 08:05 liuian
- winxp升级包下载(xp 升级)
-
题主你好,XP系统要升级成WIN7很简单,方法如下:1,下载win7系统iso镜像到本地硬盘,右键使用WinRAR等工具解压出来2,将最大的win7.gho文件和Onekeyghost工具放到同一目...
- windows 7电脑配置要求(windows7电脑配置要求)
-
官方推荐最低配置:处理器:1GHz32位或者64位处理器内存:1GB及以上显卡:支持DirectX9128M及以上(开启AERO效果)硬盘空间:32位16G以上(主分区,NTFS格式)...
- ie主页被360锁定不能修改(ie浏览器首页被锁定360导航怎么取消百度知道)
-
法一、点击开始,运行,键入msconfig点击“确定”,在弹出的窗口中切换到“启动”选项卡,禁用可疑程序启动项。法二、1、打开360安全卫士进入“更多”;2、主页防护;3、在打开的对话框中进行设...
- 镜像文件是干嘛的(镜像文件有什么用)
-
所谓镜像文件其实和ZIP压缩包类似,它将特定的一系列文件按照一定的格式制作成单一的文件,以方便用户下载和使用,例如一个测试版的操作系统、游戏等。镜像文件不仅具有ZIP压缩包的“合成”功能,它最重要的特...
- office免安装版(office免安装版什么意思)
-
1先打开安装程序输入安装序列号KEY,进行安装,在弹出来的界面里我们选择自定义安装;2在【文件位置】选项中选择好文件位置,一般大点的软件我们选择C盘以外的位置安装,可以直接点浏览选择,也可以直接将现有...
- 笔记本可以外接显卡吗(笔记本电脑核显和独显的区别)
-
目前市面上有许多笔记本电脑支持外接显卡,这些笔记本通常具备Thunderbolt3或USB-C接口,这些接口可以通过外接显卡扩展图形性能。例如,戴尔XPS系列、华硕ROG系列、雷蛇Blade系列等都...
- 深度启动盘(深度启动盘安装系统教程)
-
要制作深度系统的启动盘,需要首先下载深度系统的镜像文件,然后选择一个合适的软件进行制作。推荐使用Rufus或Etcher等工具,将镜像文件写入U盘或光盘中,并设置好启动顺序。接着将制作好的启动盘插入电...
- 免费看电影的网站是什么(免费的看电影网站有哪些)
-
优选优酷,不过现在的乐视好的,片源十分高清,如果网速一般就选优酷,播放流畅,片源最多。我个人比较喜欢看电影电视,所以市面上有的播放器都有在用,有一定的经验。如果是下播放器的话,一般网速人又懒就选PP...
- 有wifi电脑却上不了网(有wifi但是电脑连不上网是什么原因经常性的)
-
有几个可能的原因导致电脑无法通过WiFi上网。首先,可能是WiFi信号不稳定或弱,可以尝试靠近路由器或重启路由器来改善信号。其次,可能是电脑的网络设置有问题,可以检查IP地址、DNS设置等是否正确。另...
- 快用苹果助手官方网站(快用苹果助手官方网址)
-
PP助手、爱思助手、当乐助手、海马苹果助手、91苹果助手、同步推手机助手、快用苹果助手、等等都是不错的第三方助手!第三方应用助手是一款智能手机的PC端管理客户端。将应用助手安装到您的PC上,然后可以选...
- win7重置系统后需要用户名和密码
-
如果忘了WINDOS开机密码,有两种最常用的方法:第一种:开机后,按下F8进入”带命令提示符的安全”模式,输入”NETUSER+用户名+123456/ADD”可把某用户的密码强行设置为”12345...
- qq下载2025手机版(qq下载2020手机版)
-
目前来看的话java版的QQ还是可以用的。.我们需要用那种比较老式的塞班系统手机才能下载安装使用,就目前的时代使用它的意义就看我们自己怎么理解了,不过社会在进步,科技也在发展,要想更强时代的潮流,万物...
- 一周热门
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
