该答案提供了适用于任何类型文件的解决方案。用换行符或空格。
对于最近的bash,古代bash甚至旧的posix外壳都有解决方案。
该答案[1]下面列出的树用于测试。
选择
select
使用数组很容易:
$ dir='deep/inside/a/dir'
$ arr=( "$dir"/* )
$ select var in "${arr[@]}"; do echo "$var"; break; done
或使用位置参数:
$ set -- "$dir"/*
$ select var; do echo "$var"; break; done
因此,唯一真正的问题是要在数组内或位置参数内获取“文件列表”(正确定界)。继续阅读。
重击
我看不到您用bash报告的问题。Bash能够在给定目录内搜索:
$ dir='deep/inside/a/dir'
$ printf '<%s>\n' "$dir"/*
<deep/inside/a/dir/directory>
<deep/inside/a/dir/file>
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/zz last file>
或者,如果您喜欢循环:
$ set -- "$dir"/*
$ for f; do printf '<%s>\n' "$f"; done
<deep/inside/a/dir/directory>
<deep/inside/a/dir/file>
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/zz last file>
请注意,以上语法可在任何(合理的)shell(至少不是csh)上正常工作。
上面语法的唯一限制是可以进入其他目录。
但是bash可以做到这一点:
$ shopt -s globstar
$ set -- "$dir"/**/*
$ for f; do printf '<%s>\n' "$f"; done
<deep/inside/a/dir/directory>
<deep/inside/a/dir/directory/file>
<deep/inside/a/dir/directory/file name>
<deep/inside/a/dir/directory/file with a
newline>
<deep/inside/a/dir/directory/zz last file>
<deep/inside/a/dir/file>
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/zz last file>
要仅选择一些文件(例如以文件结尾的文件),只需替换*:
$ set -- "$dir"/**/*file
$ printf '<%s>\n' "$@"
<deep/inside/a/dir/directory/file>
<deep/inside/a/dir/directory/zz last file>
<deep/inside/a/dir/file>
<deep/inside/a/dir/zz last file>
强大的
当您在标题中放置“太空安全 ”时,我将假设您的意思是“ 健壮的 ”。
关于空格(或换行符)的最简单方法是拒绝对具有空格(或换行符)的输入的处理。在外壳中执行此操作的一种非常简单的方法是,如果任何文件名使用空格扩展,则退出并显示错误。有几种方法可以做到这一点,但是最紧凑的(和posix)(但仅限于一个目录内容,包括suddirectories名称和避免使用点文件)是:
$ set -- "$dir"/file* # read the directory
$ a="$(printf '%s' "$@" x)" # make it a long string
$ [ "$a" = "${a%% *}" ] || echo "exit on space" # if $a has an space.
$ nl='
' # define a new line in the usual posix way.
$ [ "$a" = "${a%%"$nl"*}" ] || echo "exit on newline" # if $a has a newline.
如果在任何这些项目中使用的解决方案都是可靠的,请删除测试。
在bash中,可以使用上述**立即测试子目录。
包含点文件的方法有两种,Posix解决方案是:
set -- "$dir"/* "$dir"/.[!.]* "$dir"/..?*
找
如果由于某种原因必须使用find,请使用NUL(0x00)替换定界符。
bash 4.4+
$ readarray -t -d '' arr < <(find "$dir" -type f -name file\* -print0)
$ printf '<%s>\n' "${arr[@]}"
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/directory/file name>
<deep/inside/a/dir/directory/file with a
newline>
<deep/inside/a/dir/directory/file>
<deep/inside/a/dir/file>
bash 2.05+
i=1 # lets start on 1 so it works also in zsh.
while IFS='' read -d '' val; do
arr[i++]="$val";
done < <(find "$dir" -type f -name \*file -print0)
printf '<%s>\n' "${arr[@]}"
适当地
为了创建一个有效的POSIX解决方案,其中find没有NUL分隔符,并且没有-d
(nor -a
)可供读取,我们需要一个完全不同的方法。
我们需要使用-exec
find和调用shell 的复合体:
find "$dir" -type f -exec sh -c '
for f do
echo "<$f>"
done
' sh {} +
或者,如果需要选择(select是bash的一部分,而不是sh):
$ find "$dir" -type f -exec bash -c '
select f; do echo "<$f>"; break; done ' bash {} +
1) deep/inside/a/dir/file name
2) deep/inside/a/dir/zz last file
3) deep/inside/a/dir/file with a
newline
4) deep/inside/a/dir/directory/file name
5) deep/inside/a/dir/directory/zz last file
6) deep/inside/a/dir/directory/file with a
newline
7) deep/inside/a/dir/directory/file
8) deep/inside/a/dir/file
#? 3
<deep/inside/a/dir/file with a
newline>
[1]这棵树(\ 012是换行符):
$ tree
.
└── deep
└── inside
└── a
└── dir
├── directory
│ ├── file
│ ├── file name
│ └── file with a \012newline
├── file
├── file name
├── otherfile
├── with a\012newline
└── zz last file
可以使用以下两个命令来构建:
$ mkdir -p deep/inside/a/dir/directory/
$ touch deep/inside/a/dir/{,directory/}{file{,\ {name,with\ a$'\n'newline}},zz\ last\ file}