我一直看到答案引用此链接,并明确声明“请勿解析ls
!”。这使我感到烦恼有两个原因:
似乎可以毫无疑问地完全接受了该链接中的信息,尽管我可以从随意阅读中至少挑出一些错误。
似乎该链接中提到的问题似乎没有激发寻求解决方案的欲望。
从第一段开始:
...当您请求
[ls]
文件列表时,存在一个巨大的问题:Unix允许文件名中的几乎所有字符,包括空格,换行符,逗号,管道符号以及几乎您尝试用作的任何其他内容。除NUL外的定界符。...ls
用换行符分隔文件名。除非您的文件名中包含换行符,否则这没问题。而且由于我不知道有任何实现ls
允许您使用NUL字符而不是换行符来终止文件名,因此我们无法使用来安全地获取文件名列表ls
。
兄弟,对吗?如何以往我们可以处理一个换行符终止可能包含换行符数据集上市?好吧,如果在这个网站上回答问题的人们每天都没有做这种事情,我可能会觉得我们遇到了麻烦。
事实是,尽管大多数ls
实现实际上提供了一个非常简单的api来解析其输出,我们一直都在做,甚至都没有意识到。您不仅可以以null结尾的文件名,还可以以null或可能需要的任何其他任意字符串开头的文件名。此外,您可以为每个文件类型分配这些任意字符串。请考虑:
LS_COLORS='lc=\0:rc=:ec=\0\0\0:fi=:di=:' ls -l --color=always | cat -A
total 4$
drwxr-xr-x 1 mikeserv mikeserv 0 Jul 10 01:05 ^@^@^@^@dir^@^@^@/$
-rw-r--r-- 1 mikeserv mikeserv 4 Jul 10 02:18 ^@file1^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 01:08 ^@file2^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 02:27 ^@new$
line$
file^@^@^@$
^@
见这更多。
现在,这是让我真正了解的下一部分:
$ ls -l
total 8
-rw-r----- 1 lhunath lhunath 19 Mar 27 10:47 a
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a?newline
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a space
问题是,从的输出来看
ls
,您或计算机都无法分辨出文件名的哪些部分。是每个字吗?不,是每行吗?不可以。除了:您无法分辨,这个问题没有正确答案。还请注意,
ls
有时文件名数据有时会出现乱码(在我们的例子中,这将\n
单词“ a”和 “ newline”之间的字符变成了一个问号 ......
如果只想遍历当前目录中的所有文件,请使用
for
循环和glob:
for f in *; do
[[ -e $f ]] || continue
...
done
当返回包含shell glob 的文件名列表时,作者称其为garbling filenames ,然后建议使用shell glob来检索文件列表!ls
考虑以下:
printf 'touch ./"%b"\n' "file\nname" "f i l e n a m e" |
. /dev/stdin
ls -1q
f i l e n a m e
file?name
IFS="
" ; printf "'%s'\n" $(ls -1q)
'f i l e n a m e'
'file
name'
POSIX定义的-1
和-q
ls
操作数等等:
-q
-强制将每个不可打印文件名字符和<tab>
s的实例写为问号('?'
)字符。如果输出是到终端设备的,则默认情况下,实现可以提供此选项。
-1
- (数字一位。)强制输出为每行一个条目。
混响并非没有其自身的问题- ?
匹配任何字符,因此?
列表中的多个匹配结果将多次匹配同一文件。这很容易处理。
尽管怎么做并不是重点-毕竟它不需要做很多事,并在下面进行了证明-我对为什么不做感兴趣。我认为,对该问题的最佳答案已被接受。我建议您尝试将重点更多地放在告诉人们他们可以做什么而不是他们不能做什么上。我认为,至少被证明是错误的可能性要小得多。
但是,为什么还要尝试呢?诚然,我的主要动机是别人不断告诉我我做不到。我非常清楚,ls
只要您知道要查找的内容,输出就可以像您希望的那样定期且可预测。错误的信息使我比做大多数事情更受困扰。
事实是,尽管帕特里克和Wumpus Q. Wumbley的答案都值得注意(尽管后者的用法很棒),但我认为答案中的大多数信息都是正确的-Shell Glob 都更易于使用并且通常在搜索当前目录方面比解析更有效ls
。但是,至少在我看来,它们并没有足够的理由来证明传播以上文章中引用的错误信息,也不是可以接受的“ 从不解析ls
”的理由。
请注意,帕特里克答案的不一致结果主要是他使用zsh
then 的结果bash
。zsh
-默认情况下-不以可移植的方式$(
用单词拆分命令替换)
结果。因此,当他问其余文件放在哪里时?这个问题的答案是您的贝壳吃掉了它们。这就是为什么SH_WORD_SPLIT
在使用zsh
和处理可移植Shell代码时需要设置变量的原因。我认为他没有在答复中指出这一点是严重的误导。
Wumpus的答案并不适合我-在列表上下文中,?
角色是一个外壳问题。我不知道该怎么说。
为了处理多个结果,您需要限制全局的贪婪性。以下内容将创建一个糟糕的文件名的测试库,并为您显示:
{ printf %b $(printf \\%04o `seq 0 127`) |
sed "/[^[-b]*/s///g
s/\(.\)\(.\)/touch '?\v\2' '\1\t\2' '\1\n\2'\n/g" |
. /dev/stdin
echo '`ls` ?QUOTED `-m` COMMA,SEP'
ls -qm
echo ; echo 'NOW LITERAL - COMMA,SEP'
ls -m | cat
( set -- * ; printf "\nFILE COUNT: %s\n" $# )
}
输出值
`ls` ?QUOTED `-m` COMMA,SEP
??\, ??^, ??`, ??b, [?\, [?\, ]?^, ]?^, _?`, _?`, a?b, a?b
NOW LITERAL - COMMA,SEP
?
\, ?
^, ?
`, ?
b, [ \, [
\, ] ^, ]
^, _ `, _
`, a b, a
b
FILE COUNT: 12
现在我要安全的每一个字符不是一个/slash
,-dash
,:colon
或字母数字在shell水珠则字符sort -u
的列表中唯一的结果。这是安全的,因为ls
已经为我们保护了所有不可打印的字符。看:
for f in $(
ls -1q |
sed 's|[^-:/[:alnum:]]|[!-\\:[:alnum:]]|g' |
sort -u | {
echo 'PRE-GLOB:' >&2
tee /dev/fd/2
printf '\nPOST-GLOB:\n' >&2
}
) ; do
printf "FILE #$((i=i+1)): '%s'\n" "$f"
done
输出:
PRE-GLOB:
[!-\:[:alnum:]][!-\:[:alnum:]][!-\:[:alnum:]]
[!-\:[:alnum:]][!-\:[:alnum:]]b
a[!-\:[:alnum:]]b
POST-GLOB:
FILE #1: '?
\'
FILE #2: '?
^'
FILE #3: '?
`'
FILE #4: '[ \'
FILE #5: '[
\'
FILE #6: '] ^'
FILE #7: ']
^'
FILE #8: '_ `'
FILE #9: '_
`'
FILE #10: '?
b'
FILE #11: 'a b'
FILE #12: 'a
b'
在下面,我再次解决该问题,但是使用了不同的方法。请记住,除了\0
null 以外,/
ASCII字符是路径名中唯一禁止的字节。我在这里放下了glob,而是结合使用POSIX指定的-d
选项ls
和POSIX指定的-exec $cmd {} +
构造find
。由于find
只会自然地/
依次发出一个文件,因此以下文件很容易获得一个递归且可靠定界的文件列表,其中包括每个条目的所有牙科信息。试想一下您可能会用以下方法做什么:
#v#note: to do this fully portably substitute an actual newline \#v#
#v#for 'n' for the first sed invocation#v#
cd ..
find ././ -exec ls -1ldin {} + |
sed -e '\| *\./\./|{s||\n.///|;i///' -e \} |
sed 'N;s|\(\n\)///|///\1|;$s|$|///|;P;D'
###OUTPUT
152398 drwxr-xr-x 1 1000 1000 72 Jun 24 14:49
.///testls///
152399 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
\///
152402 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
^///
152405 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
`///
...
ls -i
可能非常有用-特别是在结果唯一性有问题时。
ls -1iq |
sed '/ .*/s///;s/^/-inum /;$!s/$/ -o /' |
tr -d '\n' |
xargs find
这些只是我能想到的最可移植的方式。使用GNU,ls
您可以执行以下操作:
ls --quoting-style=WORD
最后,这是一种更简单的解析ls
方法,当需要inode编号时,我碰巧经常使用它:
ls -1iq | grep -o '^ *[0-9]*'
那只是返回inode号-这是另一个方便的POSIX指定的选项。
stat
在答案中使用的原因,因为它实际上检查每个文件是否存在。您对sed
事物的了解不大。
ls
首先解析相比,如何轻松解决问题所描述的所有困难呢?什么你所描述的是非常困难的。我需要解构它才能理解所有内容,而且我是一个相对称职的用户。您不可能期望普通的Joe能够处理这样的事情。
ls
输出错误的所有原因在原始链接(以及很多其他地方)中都得到了很好的阐述。如果OP寻求帮助来理解它,那么这个问题就很合理了,但是OP只是试图证明自己的错误用法是可以的。
parsing ls is bad
。这样做for something in $(command)
并依托字拆分得到准确的结果是不好的绝大多数command's
不具有简单的输出。
time bash -c 'for i in {1..1000}; do ls -R &>/dev/null; done'
= 3.18s vstime bash -c 'for i in {1..1000}; do echo **/* >/dev/null; done'
= 1.28s