Bash 递归列出不带ls、find或extendedglob的隐藏文件

Bash 递归列出不带ls、find或extendedglob的隐藏文件,bash,recursion,hidden-files,built-in,Bash,Recursion,Hidden Files,Built In,作为练习,我为自己设置了使用bash内置函数递归列出文件的任务。我特别不想使用ls或find,我更不想使用setopt extendedglob。下面的代码似乎有效,但我看不到如何使用/*扩展它以列出隐藏文件。有简单的解决方法吗 g() { for k in "$1"/*; do # loop through directory [[ -f "$k" ]] && { echo "$k"; continue; }; # echo file path [[ -d "$k" ]] &a

作为练习,我为自己设置了使用bash内置函数递归列出文件的任务。我特别不想使用ls或find,我更不想使用setopt extendedglob。下面的代码似乎有效,但我看不到如何使用/*扩展它以列出隐藏文件。有简单的解决方法吗

g() { for k in "$1"/*; do # loop through directory
[[ -f "$k" ]] && { echo "$k"; continue; }; # echo file path
[[ -d "$k" ]] && { [[ -L "$k" ]] && { echo "$k"; continue; }; # echo symlinks but don't follow
g "$k"; }; # start over with new directory
done; }; g "/Users/neville/Desktop" # original directory
后来添加:抱歉-我应该说:“OSX上的bash-3.2”

更改

for k in "$1"/*; do

第二个glob匹配名称以点开头并后跟除点以外的任何内容的所有文件,而第三个glob匹配名称以两点开头并后跟其他内容的所有文件。在这两者之间,它们将匹配除条目
之外的所有隐藏文件。

不幸的是,除非设置了shell选项
nullglob
,否则这些(像第一个glob)可能会保持原样,如果没有名称匹配的文件(在第三个glob的情况下极有可能),因此有必要验证名称实际上是一个文件

另一种方法是使用更简单的glob
“$1”/.
,它将始终匹配
目录项,因此将始终被替换。在这种情况下,有必要从列表中删除两个条目:

for k in "$1"/* "$1"/.*; do
  if ! [[ $k =~ /\.\.?$ ]]; then
    # ...
  fi
done

(不过,
“$1”/*
仍有可能保留在列表中。因此这可能没有多大帮助。)

您可以为
指定多个参数:

“$1”/*”$1”/.*中的k的
;做
但是如果您确实在目录中搜索
*
,您应该知道它还提供
文件。如果glob匹配,
“$1”/*
glob,您也可能会得到一个不存在的文件,所以我也会检查一下

考虑到这一点,我将这样纠正循环:

g(){
局部k细分
对于“$1”/*”$1”/.*;中的k,请在目录中循环
[[-e“$k”]| |继续#跳过丢失的文件(不匹配的全局)
细分利率=${k##*/}
[[“$subdir”=.]| |[[“$subdir”=.]]和&continue#跳过伪目录“.”和“.”
如果[-f“$k”]|【[-L“$k”]];则
printf%s\\n“$k”#回显文件和符号链接的路径
elif[-d“$k”];然后
g“$k”#用新目录重新开始
fi
完成
}
g~内维尔/桌面
在这里,外观时髦的
${k###*/}
只是获取文件基名的一种快速方法,而
本地
被放入其中,这样变量就不会修改shell中的任何现有变量

我还将
echo“$k”
更改为
printf%s\\n“$k”
,因为
echo
在参数处理方面存在无法弥补的缺陷,应该避免用于回显未知变量。(有关如何操作的解释,请参见;它归结为-n和-e在工作中使用扳手。)


顺便说一下,这不会打印套接字或FIFO-这是故意的吗?

将GLOBIGNORE文件设置为排除
,这会隐式打开“shopt-u dotglob”。然后,您的原始代码可以正常工作,而无需进行其他更改

user@host [/home/user/dir]
$ touch file
user@host [/home/user/dir]
$ touch .dotfile
user@host [/home/user/dir]
$ echo *
file
user@host [/home/user/dir]
$ GLOBIGNORE=".:.."
user@host [/home/user/dir]
$ echo *
.dotfile file

注意,这是特定于bash的。特别是,它在ksh中不起作用。

bash
4.1开始,您可以在
==
的参数中使用扩展模式=extglob
。事实上,我比我更喜欢这个答案(我个人就是这样做的),因为它可以在shell而不是Bash中工作。但是由于OP确实明确地说了“bash”,我继续提供了GLOBIGNORE解决方案,它自动跳过
:)这两种方法都很有效,我可以看到它们在做什么,但我不明白为什么。没有它们,使用“$1”/.*会慢慢生成许多目录:/Users/neville/Desktop/test dir//-为什么会这样?点文件是否像指向目录的符号链接?如果是,为什么它不被我的符号链接陷阱阻止?如果我很乐意列出所有的UNIX点和双点文件,那么应该如何修改它?
user@host [/home/user/dir]
$ touch file
user@host [/home/user/dir]
$ touch .dotfile
user@host [/home/user/dir]
$ echo *
file
user@host [/home/user/dir]
$ GLOBIGNORE=".:.."
user@host [/home/user/dir]
$ echo *
.dotfile file