我需要编写一个一次性实用程序,它对给定目录中的所有文件(但预定义列表中的文件列表)执行一些操作。由于给定的列表是预定义的,我将把它硬编码为数组。
话虽如此,如何获取不在给定数组中的所有文件的名称?这可以是任何标准的 unix 脚本(bash、awk、perl)。
答案1
有了bash
,你可以这样做:
all=(*)
except=(file1 file2 notme.txt)
only=()
IFS=/
for file in "${all[@]}"; do
case "/${except[*]}/" in
(*"/$file/"*) ;; # do nothing (exclude)
(*) only+=("$file") # add to the array
esac
done
ls -ld -- "${only[@]}"
(这适用于当前目录中的文件,但对于像all=(*/*) except=(foo/bar)
我们用来/
连接数组元素以进行查找的全局变量来说并不可靠)。
它基于以下事实:"${array[*]}"
将数组的元素与$IFS
(此处选择的第一个字符连接起来,/
因为它不会出现在文件中姓名; NUL是文件中不能出现的字符小路,但不幸的是bash
(与 相反zsh
)它的变量中不能有这样的字符)。因此,对于其中的每个文件$all
(此处以$file
beingfoo
为例),我们都会case "/file1/file2/notme.txt/" in (*"/foo/"*)
检查是否$file
要排除。
答案2
使用以下方法更容易zsh
:
except=(file1 file2 notme.txt)
all=(*)
only=(${all:|except})
ls -ld -- $only
助记符${all:|except}
:元素$all
酒吧那些$except
.
您还可以检查文件是否在数组中$except
作为全局限定符:
ls -ld -- *.txt(^e:'((except[(Ie)$REPLY]))':)
或者使用一个函数:
in_except() ((except[(Ie)${1-$REPLY}]))
ls -ld -- *.txt(^+in_except)
答案3
如果文件名足够简单,您可以使用bash的GLOBIGNORE
变量:
shell
GLOBIGNORE
变量可用于限制与模式匹配的文件名集。如果GLOBIGNORE
设置,GLOBIGNORE
则将从匹配列表中删除也与其中模式之一匹配的每个匹配文件名。如果nocaseglob
设置了该选项,则执行与 中的模式的匹配GLOBIGNORE
,而不考虑大小写。当设置且不为空时,文件名.
和..
始终被忽略 。GLOBIGNORE
但是,设置GLOBIGNORE
为非空值具有启用dotglob
shell 选项的效果,因此以 ' ' 开头的所有其他文件名.
都将匹配。要获得忽略以 ' ' 开头的文件名的旧行为.
,请将 '.*
' 设为GLOBIGNORE
.未设置时该dotglob
选项将被禁用。GLOBIGNORE
$ echo *
bin boot dev etc home lib lib64 lost+found mnt opt proc root run sbin srv sys tmp usr var
$ except=(etc lib lib64 tmp sbin)
$ GLOBIGNORE=$(IFS=:; printf "%s" "${except[*]}")
$ echo *
bin boot dev home lost+found mnt opt proc root run srv sys usr var
当然,如果您正在创建数组,那么您可以直接设置变量GLOBIGNORE
:
GLOBIGNORE=etc:lib:lib64:tmp:sbin
您还可以利用列表中可能存在的任何适合 bash 通配符的模式:
GLOBIGNORE=etc:lib*:tmp:sbin