ls
!”这使我感到困扰,原因有两个:似乎可以毫无疑问地接受该链接中的信息,尽管我可以从随意阅读中至少挑出一些错误。
似乎该链接中提到的问题似乎没有引发寻求解决方案的愿望。
从第一段开始:
。 ..当您问
[ls]
文件列表时,存在一个巨大的问题:Unix允许文件名中的几乎所有字符,包括空格,换行符,逗号,管道符号和除了NUL之外,您还可以尝试使用其他任何东西作为定界符。 ...
ls
用换行符分隔文件名。除非您有一个名称中带有换行符的文件,否则这很好。而且由于我不知道ls
的任何实现方式是否允许您使用NUL字符而不是换行符来终止文件名,这使我们
无法通过
ls
安全地获取文件名列表。好吧,对吧?我们如何处理可能包含换行符的数据以换行符终止的列出数据集?好吧,如果在这个网站上回答问题的人们每天都没有做这种事情,我可能会觉得我们遇到了麻烦。
事实是,大多数
ls
实现实际上提供了一个非常简单的api来解析其输出,我们一直都在做,甚至都没有意识到。您不仅可以以null结尾的文件名,还可以以null或可能需要的任何其他任意字符串开头的文件名。此外,您可以为每种文件类型分配这些任意字符串。请考虑:LS_COLORS='lc=$ 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
:rc=:ec=for f in *; do
[[ -e $f ]] || continue
...
done
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'
{ printf %b $(printf \%04o `seq 0 127`) |
sed "/[^[-b]*/s///g
s/\(.\)\(.\)/touch '?\v' '\t' '\n'\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" $# )
}
: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` ?QUOTED `-m` COMMA,SEP
??\, ??^, ??`, ??b, [?\, [?\, ]?^, ]?^, _?`, _?`, a?b, a?b
NOW LITERAL - COMMA,SEP
?
\, ?
^, ?
`, ?
b, [ \, [
\, ] ^, ]
^, _ `, _
`, a b, a
b
FILE COUNT: 12
问题在于,从
ls
的输出中,您或计算机都无法分辨出它的哪些部分构成了文件名。是每个
单词吗?不,是每行吗?否。除以下问题外,没有正确答案:
还请注意
ls
有时会混淆文件名数据(在我们的情况下,将单词“ a”和
“换行符”之间的
\n
字符变成一个问号... ...
如果您只想要遍历当前目录中的所有文件,请使用
for
循环和glob: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
作者称其为乱码文件名当
ls
返回包含Shell Glob的文件名列表时,然后建议使用Shell Glob来检索文件列表!请考虑以下内容:
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'
POSIX将
-1
和-q
ls
操作数定义为:-q
-强制将每个不可打印的文件名字符和<tab>
的每个实例写入问号('?'
)字符。实现可以提供如果输出到终端设备,则默认为option。
-1
-(数字1。)强制输出为每行一个条目。globbing并非没有其自身的问题-
?
匹配任何字符,因此列表中的多个匹配?
结果将多次匹配同一文件。这很容易处理。虽然这不是重点-毕竟并不需要做很多事情,并且在下面说明-我对为什么不感兴趣。在我看来,对该问题的最佳答案已被接受。我建议您尝试将重点更多地放在告诉人们他们可以做什么而不是他们不能做什么上。我认为,至少被证明是错误的可能性要小得多。
但是,为什么还要尝试呢?诚然,我的主要动机是别人不断告诉我我做不到。我非常了解
ls
输出只要您知道要查找的内容,就可以按照您希望的那样定期且可预测。错误的信息使我比做大多数事情更受困扰。事实是,除了帕特里克(Patrick)和伍普斯(Wumpus)问。Wumbley的答案很明显(尽管后者的用法很棒),我认为大部分信息都是如此。在此处的答案中,大多数都是正确的-与解析
ls
相比,shell glob在搜索当前目录时更易于使用,而且通常更有效。但是,至少就我个人而言,它们并没有足够的理由来证明以上文章中引用的错误信息的传播,也不是“从不解析ls
”的可接受理由。请注意,帕特里克的回答是结果不一致主要是他使用
zsh
然后使用bash
导致的。 zsh
-默认情况下-不以单词形式拆分$(
命令,以可移植的方式替换)
结果。因此,当他问其余文件放在哪里时?这个问题的答案是您的贝壳吃掉了它们。这就是为什么在使用SH_WORD_SPLIT
并处理可移植的shell代码时需要设置zsh
变量的原因。我认为他没有在答案中指出这一点是极具误导性的。Wumpus的答案对我而言并不重要-在列表上下文中,
?
字符是一个外壳问题。我不知道该怎么说。 为了处理多个结果,您需要限制全局的贪婪性。以下内容将创建一个糟糕的文件名的测试基础并为您显示:
#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\)///|///|;$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 -1iq |
sed '/ .*/s///;s/^/-inum /;$!s/$/ -o /' |
tr -d '\n' |
xargs find
现在,我将保护不在外壳程序glob中的不是
/slash
,-dash
,:colon
或字母数字字符的每个字符,然后将sort -u
列为唯一结果。这是安全的,因为ls
已经为我们保护了所有不可打印的字符。观看:ls --quoting-style=WORD
输出:
ls -1iq | grep -o '^ *[0-9]*'
下面我再次解决该问题,但是我使用了不同的方法。请记住,除了
/
null之外,-d
ASCII字符是路径名中唯一禁止的字节。我在这里放下了glob,而是将posix指定的ls
选项和-exec $cmd {} +
的POSIX指定的find
构造组合在一起。由于find
只会自然地顺序发出一个/
,因此以下代码很容易获得一个递归且可靠定界的文件列表,其中包括每个条目的所有牙科信息。试想一下您可能会用这样的方法做什么:q4312078q
ls -i
可能非常有用-尤其是当结果唯一性有问题时。q4312078q
这些只是我能想到的最便携式的方式。使用GNU
ls
,您可以执行以下操作:q4312078q
最后,这是一种解析
ls
的简单方法,当我需要inode编号时,我碰巧经常使用它:q4312078q
这仅返回inode编号-这是另一个方便的POSIX指定的选项。
#1 楼
我一点都不相信这一点,但是为了争辩,让我们假设,如果您准备好进行充分的努力,即使面对“对手”,也可以可靠地解析ls
的输出。谁知道您编写的代码,并故意选择旨在破坏该文件名的文件名。即使您可以做到,但这仍然不是一个好主意。
Bourne shell是不是一门好语言。除非极端的可移植性比任何其他因素(例如
autoconf
)都重要,否则它不应该用于任何复杂的事情。我声称,如果您遇到解析
ls
的输出似乎存在的问题就像shell脚本的阻力最小的路径一样,这有力地表明您所做的一切对于shell来说都太复杂了,您应该用Perl或Python重写整个过程。这是您使用Python编写的最后一个程序: import os, sys
for subdir, dirs, files in os.walk("."):
for f in dirs + files:
ino = os.lstat(os.path.join(subdir, f)).st_ino
sys.stdout.write("%d %s %s\n" % (ino, subdir, f))
此文件名中的不寻常字符没有任何问题-输出的模棱两可与
ls
的输出模棱两可的方式相同,但是在“真实”程序(与此类演示相对)中无关紧要同样重要,与您所写的内容形成鲜明对比的是,从现在起六个月后,它仍然有意义,并且在需要时可以轻松进行修改,以完全使用os.path.join(subdir, f)
的结果。做一些稍微不同的事情。举例说明,假设您发现需要排除点文件和编辑器备份,并按字母顺序按基名称处理所有内容: import os, sys
filelist = []
for subdir, dirs, files in os.walk("."):
for f in dirs + files:
if f[0] == '.' or f[-1] == '~': continue
lstat = os.lstat(os.path.join(subdir, f))
filelist.append((f, subdir, lstat.st_ino))
filelist.sort(key = lambda x: x[0])
for f, subdir, ino in filelist:
sys.stdout.write("%d %s %s\n" % (ino, subdir, f))
评论
很好那是在|说到递归?我不确定。即使它不能超过一个,对吗?到目前为止,这是唯一对我有意义的答案。
–mikeserv
2014年5月12日22:58
没有递归,只是嵌套的for循环。 os.walk在后台进行了一些繁重的工作,但您不必担心,而不必担心ls或在内部找到工作。
– zwol
2014年5月12日23:04
从技术上讲,os.walk返回一个生成器对象。生成器是Python的惰性列表版本。每次外部for循环迭代时,都会调用生成器并“屈服”另一个子目录的内容。如果有帮助,Perl中的等效功能是File :: Find。
– zwol
2014年5月12日23:12
您应该意识到,我100%同意您批评的文档以及Patrick和Terdon的回答。我的回答旨在提供一个额外的独立原因,以避免解析ls输出。
– zwol
14年5月13日在17:02
这是非常误导的。 Shell不是一种好的编程语言,只是因为它不是一种编程语言。这是一种脚本语言。这是一种很好的脚本语言。
–Miles Rout
14年5月13日在21:38
#2 楼
对该链接的引用很多,因为信息完全准确,而且已经存在很长时间了。ls
用glob字符替换了不可打印的字符是,但是这些字符不在实际的文件名中。为什么这么重要? 2个原因:如果将该文件名传递给程序,则该文件名实际上不存在。它必须扩展glob才能获取真实的文件名。
文件glob可能匹配多个文件。
例如:
$ touch a$'\t'b
$ touch a$'\n'b
$ ls -1
a?b
a?b
请注意,我们有2个看起来完全一样的文件。如果将它们都表示为
a?b
,您将如何区分它们?当ls返回包含shell glob的文件名列表时,作者称其为garbling文件名。使用Shell Glob检索文件列表!
这里有区别。如图所示,当您恢复一个glob时,该glob可能会匹配多个文件。但是,当遍历与glob匹配的结果时,您将获得确切的文件,而不是glob。例如,
$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6109 62 a.b
0000000: 610a 62 a.b
请注意,
xxd
输出如何显示$file
包含原始字符\t
和\n
,而不是?
。如果使用
ls
,则会得到以下内容:for file in $(ls -1q); do printf '%s' "$file" | xxd; done
0000000: 613f 62 a?b
0000000: 613f 62 a?b
“无论如何我都要进行迭代,为什么不使用
ls
?” 您给出的示例实际上没有用。看起来好像可以,但是不起作用。
我指的是:
for f in $(ls -1q | tr " " "?") ; do [ -f "$f" ] && echo "./$f" ; done
我创建了目录带有一堆文件名:
$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6120 62 a b
0000000: 6120 2062 a b
0000000: 61e2 8082 62 a...b
0000000: 61e2 8083 62 a...b
0000000: 6109 62 a.b
0000000: 610a 62 a.b
运行我的代码时,我得到的是:
$ for f in $(ls -1q | tr " " "?") ; do [ -f "$f" ] && echo "./$f" ; done
./a b
./a b
其余文件放在哪里?
让我们试试看吧:
$ for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f"; done
stat: cannot stat ‘./a?b’: No such file or directory
stat: cannot stat ‘./a??b’: No such file or directory
./a b
./a b
stat: cannot stat ‘./a?b’: No such file or directory
stat: cannot stat ‘./a?b’: No such file or directory
现在让我们使用实际的glob:
$ for f in *; do stat --format='%n' "./$f"; done
./a b
./a b
./a b
./a b
./a b
./a
b
用bash
上面的示例是我的普通shell zsh。当我用bash重复该过程时,您的示例得到另一组完全不同的结果:
同一组文件:
$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6120 62 a b
0000000: 6120 2062 a b
0000000: 61e2 8082 62 a...b
0000000: 61e2 8083 62 a...b
0000000: 6109 62 a.b
0000000: 610a 62 a.b
与您的代码截然不同的结果:
for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f"; done
./a b
./a b
./a b
./a b
./a
b
./a b
./a b
./a b
./a b
./a b
./a b
./a b
./a
b
./a b
./a b
./a b
./a b
./a
b
使用Shell Glob,它可以很好地工作:
$ for f in *; do stat --format='%n' "./$f"; done
./a b
./a b
./a b
./a b
./a b
./a
b
bash如此行为的原因可以追溯到我在回答开始时提出的观点之一:“文件glob可能匹配多个文件”。
ls
返回的是同一个glob( a?b
)用于几个文件,因此每次扩展此glob时,我们都会获得与之匹配的每个文件。如何重新创建我使用的文件列表:
touch 'a b' 'a b' a$'\xe2\x80\x82'b a$'\xe2\x80\x83'b a$'\t'b a$'\n'b
十六进制代码是UTF-8 NBSP字符。
评论
@mikeserv实际上他的解决方案没有返回glob。我只是更新了我的答案以澄清这一点。
–随钻
2014年5月12日在2:02
“没有休息”吗?这是不一致的行为,并且结果出乎意料,这不是原因吗?
–随钻
2014年5月12日下午2:32
@mikeserv您没有看到我对您的问题的评论吗? Shell球的速度比ls快2.5倍。我还要求您测试您的代码,因为它不起作用。 zsh与这一切有什么关系?
–随钻
2014年5月12日下午4:29
@mikeserv不,这一切甚至都适用于bash。虽然我已经解决了这个问题,因为您没有听我在说什么。
–随钻
2014年5月12日下午5:37
您知道吗,我想我会赞成这个答案,并在我的声明中澄清我同意它所说的一切。 ;-)
– zwol
14年5月13日在17:03
#3 楼
ls -q
的输出根本不是glob。它使用?
表示“此处存在无法直接显示的字符”。 glob使用?
表示“此处允许任何字符”。glob具有其他特殊字符(至少
*
和[]
,并且在[]
对内还有更多特殊字符)。 ls -q
不能逃脱这些错误。$ touch x '[x]'
$ ls -1q
[x]
x
如果处理
ls -1q
输出,则会有一组glob并对其进行扩展,不仅会得到两次x
,而且会完全错过[x]
的。作为全局变量,它不匹配字符串。ls -q
的目的是使您的眼睛和/或终端免于疯狂的字符,而不是产生可反馈给外壳的内容。 #4 楼
让我们尝试简化一下:$ touch a$'\n'b a$'\t'b 'a b'
$ ls
a b a?b a?b
$ IFS="
"
$ set -- $(ls -1q | uniq)
$ echo "Total files in shell array: $#"
Total files in shell array: 4
看到了吗?那里已经错了。有3个文件,但bash报告了4个文件。这是因为给
set
提供了ls
生成的glob,这些glob在传递给set
之前由外壳程序进行了扩展。这就是为什么您得到以下内容的原因:$ for x ; do
> printf 'File #%d: %s\n' $((i=$i+1)) "$x"
> done
File #1: a b
File #2: a b
File #3: a b
File #4: a
b
或者,如果您愿意:
$ printf ./%s\0 "$@" |
> od -A n -c -w1 |
> sed -n '/ \{1,3\}/s///;H
> /\0/{g;s///;s/\n//gp;s/.*//;h}'
./a b
./a b
./a\tb
./a\nb
以上已运行在
bash 4.2.45
上。 评论
我对此表示赞同。很高兴看到您自己的代码咬住了您。但是,仅仅因为我做错了并不意味着就不能做对。我今天早上用ls -1qRi向您展示了一种非常简单的方法| grep -o'^ * [0-9] *'-这是对ls输出的解析,伙计,这是我知道获取索引节点编号列表的最快,最好的方法。
–mikeserv
2014年5月12日22:56
@mikeserv:如果您有时间和耐心,可以做对。但是事实是,它天生就是容易出错的。你自己弄错了。在争论其优点时!即使是为之奋斗的人也未能正确做到这一点,这对它是巨大的打击。而且很有可能,在正确之前,您可能会花费更多的时间来弄错它。我对您不屑一顾,但是大多数人比他们花更多的时间做事而不是花很多时间使用相同的代码行。
– cHao
14年5月13日在1:06
@ cHao-我没有争论它的优点-我抗议它的宣传。
–mikeserv
14年5月13日在1:47
@mikeserv:反对它的论点是有根据的,是当之无愧的。即使您已经证明它们是真实的。
– cHao
14年5月13日在1:50
@cHao-我不同意。口头禅和智慧之间的界限不太细。
–mikeserv
14年5月13日在1:51
#5 楼
答案很简单:ls
的特殊情况必须要胜过任何可能的好处。如果不解析ls
输出,则可以避免这些特殊情况。这里的口头禅是从不信任用户文件系统(相当于从不信任用户输入)。如果有一种方法始终可以100%确定性地起作用,那么即使
ls
所做的相同但不确定性较低,它也应该是您首选的方法。我将不涉及技术细节,因为这些细节已被terdon和Patrick广泛涵盖。我知道,由于在重要的(可能是昂贵的)交易中使用ls
的风险,而我的工作/声望很高,因此,如果可以避免的话,我会优先选择那些没有不确定性的解决方案。我知道有些人比确定性更喜欢冒险,但是我已经提交了错误报告。
#6 楼
人们说从不做某事的原因不一定是因为绝对不能正确地做某事。我们也许可以这样做,但是它在空间或时间上都可能更复杂,效率更低。例如,最好说“永远不要在x86组装中构建大型的电子商务后端”。现在解决当前的问题:正如您所展示的,您可以创建一个解决方案,解析ls并给出正确的结果-因此正确性不是问题。
更复杂吗?是的,但是我们可以将其隐藏在辅助函数的后面。
所以现在要提高效率:
空间效率:您的解决方案依靠
uniq
来过滤出重复项,因此我们不能懒惰地生成结果。因此,O(1)
与O(n)
或两者都具有O(n)
。时间效率:最好的情况uniq
使用哈希图方法,因此在采购的元素数量上仍然有O(n)
算法,尽管它是O(n log n)
。现在是真正的问题:虽然您的算法看起来还不错,但我还是非常谨慎地使用采购的元素而不是n的元素。因为那确实有很大的不同。假设您有一个文件
\n\n
,它将导致??
出现问题,因此请匹配清单中的每2个字符文件。有趣的是,如果您还有另一个文件\n\r
,该文件也将导致??
,并且还返回所有2个字符文件。指数而不是线性行为肯定会被视为“更糟糕的运行时行为”。这是一种实用算法与您在理论CS期刊上撰写论文的算法之间的区别。每个人都喜欢示例吗?开始了。创建一个名为“ test”的文件夹,并在该文件夹所在的目录中使用此python脚本。
#!/usr/bin/env python3
import itertools
dir = "test/"
filename_length = 3
options = "\a\b\t\n\v\f\r"
for filename in itertools.product(options, repeat=filename_length):
open(dir + ''.join(filename), "a").close()
唯一要做的就是为7个字符生成所有长度为3的乘积。高中数学告诉我们,应该是343个文件。那么应该很快就能打印出来,所以让我们来看一下:
time for f in *; do stat --format='%n' "./$f" >/dev/null; done
real 0m0.508s
user 0m0.051s
sys 0m0.480s
现在让我们尝试您的第一个解决方案,因为我真的不能得到这个
eval set -- $(ls -1qrR ././ | tr ' ' '?' |
sed -e '\|^\(\.\{,1\}\)/\.\(/.*\):|{' -e \
's///;\|/$|!s|.*|&/|;h;s/.*//;b}' -e \
'/..*/!d;G;s/\(.*\)\n\(.*\)//' -e \
"s/'/'\\''/g;s/.*/'&'/;s/?/'[\"?$IFS\"]'/g" |
uniq)
这里的东西可以在Linux Mint 16上工作(我认为这种方法的实用性足以说明问题)。
无论如何,因为上述内容非常多对结果进行过滤后,较早的解决方案至少应与较晚的解决方案一样快(该解决方案中没有inode技巧,但这些技巧不可靠,因此您会放弃正确性)。
现在
time for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f" >/dev/null; done
需要多长时间?好吧,我真的不知道,检查343 ^ 343文件名需要一些时间-在宇宙热死之后,我会告诉您。
评论
当然,正如在另一个答案下的注释中所提到的那样,语句“ ...您已经证明可以创建一个解析ls并给出正确结果的解决方案...”实际上是不正确的。
–通配符
16 Jan 10 '16 at 11:12
#7 楼
OP的陈述的意图已解决序言和原始答案的依据†于2015-05-18
mikeserv(OP)在对他的问题的最新更新中表示:“我确实认为遗憾的是,我首先问了这个问题,指出了错误信息的根源,但不幸的是,这里最受推崇的答案在很大程度上是误导。”
我感到很遗憾,因为我花了很多时间试图弄清楚如何解释我的意思,而在重新阅读问题时才发现这一点。这个问题最终导致“ [引起讨论,而不是回答””‡,最终导致大约18K的文本(仅就问题而言,就是要清楚),即使是博客帖子也是如此。
但是StackExchange不是您的肥皂盒,也不是您的博客。但是,实际上,您至少已将其用作两者。人们最终花费大量时间回答您的“ To-Point-Out”,而不是回答人们的实际问题。在这一点上,我将标记该问题不适合我们的格式,因为OP明确表示它根本不打算成为一个问题。
此时,我不确定我的答案是否正确。可能不是,但这是针对您的一些问题,也许对其他人可能是一个有用的答案;初学者会振作起来,一旦经验丰富,其中一些“不做”就会变成“有时做”。 :)
作为一般规则...
请原谅剩余的粗糙边缘;我已经在此上花费了太多时间...而不是直接引用OP(如最初的意图),我将尝试进行总结和解释。
[考虑到我的原始回答后,大部分都在重做]经考虑后,我认为我误解了OP对我回答的问题的重视;但是,提出的要点已经提出,并且我留下的答案基本上是完整的,因为我相信这些答案是对的,并且可以解决我在其他情况下以及针对初学者的建议中提出的问题。 br />
原始帖子以几种方式询问为什么各种文章都提供了建议,例如“不要解析
ls
输出”或“您绝对不要解析ls
输出”,等等。 我对这个问题的建议解决方法是,这种陈述的实例只是一个成语的例子,用稍微不同的方式表达,其中绝对量词与命令式(例如,«don 't [ever] X»,«[您应该]永远是Y»,«[一个人应该永远不要Z»]来形成旨在用作一般规则或准则的陈述,特别是当给那些刚接触主题的陈述或准则时,这些陈述的表述形式显然不是绝对的真理。
当您开始学习新的主题时,除非您对为什么可能需要做其他事情有很好的了解,否则,明智的做法是,毫无例外地简单地遵循公认的一般规则,除非在经验丰富的人的指导下进行。随着技能和经验的提高,您将能够进一步确定规则何时以及是否适用于任何特定情况。一旦您掌握了相当多的经验,就可能首先了解通用规则背后的原因,然后,您可以开始使用自己的判断来决定该规则背后的原因是否适用以及适用于何种水平这种情况,以及是否存在最重要的问题。
那时候专家也许会选择违反“规则”来做事。但这并不会使它们成为“规则”。
因此,对于当前的话题:在我看来,仅仅是因为专家可能会完全违反此规则ack贬不一,我看不出有什么办法可以证明对初学者来说“有时”可以解析
ls
输出,因为:事实并非如此。或者,至少对于初学者而言,这样做当然是不合适的。您总是将棋子放在中间;开场一招,一招;尽早进行城堡建造;主教前的骑士;边缘的骑士是冷酷的;并始终确保您可以完整了解计算结果! (哇,对不起,累了,这是为了国际象棋StackExchange。)
规则,是要打破的吗?
在阅读有关主题的文章时,或可能被初学者阅读,通常您会看到以下内容:
“您永远不要做X。”
“永远不要做Q!”
“别做Z。”
“一个人应该总是做Y!”
“ C,无论如何。”
这些语句似乎在说明绝对和永恒的规则,不是。相反,这是一种陈述一般规则的方式[a.k.a. “指南”,“经验法则”,“基础知识”等]至少可以说是为可能正在阅读这些文章的初学者陈述它们的一种合适方法。但是,仅因为这些规则被说成是绝对的,所以这些规则当然不会约束专业人士和专家[他们很可能首先总结了这些规则,以此作为记录和传递在处理重复出现时所获得的知识的方式特定工艺中的问题。]
这些规则当然不会揭示专家如何处理一个复杂或细微的问题,例如,这些规则相互冲突。或首先导致该规则的担忧根本不适用。专家不害怕(或不应该害怕!)只是打破他们碰巧知道在特定情况下没有道理的规则。专家们一直在努力平衡其工作中的各种风险和担忧,并且必须经常使用自己的判断力来选择打破那些规则,必须权衡各种因素,而不能仅仅依靠规则表来遵循。以
Goto
为例:关于它们是否有害,已经有很长一段时间反复发生的争论。 (是的,永远不要使用gotos。; D)模态命题
一个奇怪的特征,至少在英语中,我想在许多其他语言中,一般规则是,它们以与情态命题相同的形式陈述,但是领域内的专家愿意为情况给出一般规则,同时知道他们会在适当的时候违反规则。因此,显然,这些语句并不等同于模态逻辑中的相同语句。
这就是为什么我说它们必须只是惯用语。这些规则不是真正地成为“从不”或“永远”的情况,而通常是用来编纂一般指导方针,这些指导方针往往适用于各种情况,并且当初学者盲目地遵循它们时,很可能会导致比初学者无缘无故地选择与他们对抗效果更好。有时,他们将规则编纂成规则,只是导致不合格的结果,而不是违反规则时伴随错误选择而导致的彻底失败。
因此,一般规则并不是表面上出现的绝对模态命题,而是在隐含标准样板的情况下给出规则的一种简便方法,如下所示:
除非您有能够在特定情况下判断该准则是错误的,并向自己证明自己是对的,然后使用$ {RULE}
当然可以在其中替换“从不解析
ls
输出” $ {RULE}的位置。 :) 哦,是的!那么解析
ls
输出呢?那么,鉴于所有这些……我认为,很明显,这条规则是一个好规则。首先,如上所述,真正的规则必须理解为习惯用法... 但是,不仅如此,您不仅必须非常擅长使用shell脚本来了解它是否可以在某些特定情况下会损坏。同样,当您试图在测试中打破它时,要告诉您它弄错了,也需要同样多的技巧!而且,我有信心地说,此类文章的绝大多数潜在读者(提供诸如“不要解析
ls
的输出!”之类的建议)不能做那些事情,而那些具备这种技巧的人可能会意识到他们自己搞定了,仍然无视规则。但是……只是看这个问题,甚至那些可能确实有这种技能的人怎么也认为这样做是一个错误的选择;以及问题的作者花了多少精力才了解当前最好的例子!我向您保证这个问题很难解决,那里有99%的人会弄错它,而且结果可能非常糟糕!即使确定的方法被证明是一个好方法,直到它(或另一个)解析想法被IT /开发人员整体采纳,经受了很多测试(尤其是时间的考验),最后设法升级为“通用技术”状态时,很可能很多人可能会尝试它,并把它弄错……带来灾难性的后果。
所以,我最后一次重申……。尤其是在这种情况下,这就是为什么“永不解析
ls
输出!” [更新2014-05-18:阐明了回答OP意见的答案的理由(以上);以下是响应OP对昨天问题的补充]。
[UPDATE 2014-11-10:添加了标题和重组/重构的内容;并且:重新格式化,改写,澄清和确定...“简明扼要” ...我本来只是为了进行清理,尽管这样做确实有点重做。我把它放在一个遗憾的状态,所以我主要是试图给它一些命令。我确实觉得重要的是要保持第一部分的完整。因此,这里只有两个较小的更改,多余的“但是”被删除了,并强调了“那个”。]
†我原本打算仅以此作为对原始内容的澄清;但在反思后决定了其他补充内容
‡有关帖子的指南,请参见https://unix.stackexchange.com/tour
评论
从来都不是惯用的。这不能解决任何问题。
–mikeserv
2014年5月17日17:52
嗯好吧,我不知道这个答案是否令人满意,但是我绝对不希望它引起争议。而且,我(并非要)认为“从不”本身是惯用的。但是“永远不要X!”是惯用语。我看到两个可以说明“从不/不解析ls!”的一般情况。正确的建议是:1.证明(令您满意)每个可能解析输出的用例都有另一种可用的解决方案,该解决方案在某种程度上是卓越的,而没有这样做。 2.证明在引用的情况下,该陈述不是文字陈述。
– shelleybutterfly
2014年5月18日下午6:50
我应该收拾那个职位。不过,永远不是正确的措辞方式。人们以为自己有资格告诉别人从不或不告诉别人,这有点荒谬。只是告诉他们,你不认为这会起作用,为什么,但是你知道什么会起作用,为什么。 ls是计算机实用程序-您可以解析计算机输出。
–mikeserv
2014年5月18日14:28
好吧,我撤回了我的不赞成票,因为至少,您对那起举报不对。生病尝试今晚或明天清理。我的想法是,我将大多数代码示例移至我猜得到的答案。但就我而言,它仍然没有为经常引用的博客文章中的不正确之处辩解。我希望人们不再完全引用bash手册-至少在引用POSIX规范后才开始引用...
–mikeserv
2014年5月18日在17:44
这个精彩的评论概括地解释了这个对话。我要引用一句话:为什么您认为他们说“不要低头看枪筒”,而不是“除非是空的就不要看枪筒”?还是“不要试图将手指伸入电源插座”,而不是“不要试图将手指伸入电源插座,除非它们太大而不能插入,或者除非您已关闭电源”?等等
–通配符
19年5月22日在5:35
#8 楼
在某些情况下是否可以解析ls
的输出?当然。从目录中提取索引节点编号列表的想法是一个很好的例子-如果您知道实现的ls
支持-q
,因此每个文件将只产生一行输出,而您所需的就是索引节点编号,并将其解析ls -Rai1q
输出中肯定不是一个可行的解决方案。当然,如果作者以前从未听过诸如“从不解析ls的输出”之类的建议,他可能不会考虑其中包含换行符的文件名,因此可能会忽略“ q”,因此在这种情况下,代码可能会被巧妙地破坏-因此,即使在解析ls
的输出是合理的情况下,此建议仍然有用。广义来说,当新手使用shell脚本时试图让脚本找出(例如)目录中最大的文件是什么,或者目录中最近修改的文件是什么,他的第一个直觉是解析
ls
的输出-可以理解,因为ls
是第一个文件之一命令新手学习。不幸的是,这种本能是错误的,并且这种方法被打破了。更不幸的是,它被巧妙地破坏了-在大多数情况下都可以使用,但是在某些情况下可能会失败,而这种情况可能会被那些了解代码的人利用。
新手可能会认为
ls -s | sort -n | tail -n 1 | awk '{print }'
是一个获取目录中最大文件的方法。它可以正常工作,直到您的文件名中带有空格为止。确定,那么
ls -s | sort -n | tail -n 1 | sed 's/[^ ]* *[0-9]* *//'
怎么样?正常工作,直到您的文件名中包含换行符为止。当文件名中有换行符时,将
-q
添加到ls
的参数是否有帮助?看起来似乎确实如此,直到您在文件名的同一位置有2个不同的文件中都包含不可打印的字符,然后ls
的输出无法让您区分出哪一个最大。更糟糕的是,为了扩展“?”,他可能会求助于外壳程序的eval
-如果他命中了一个名为例如foo`/tmp/malicious_script`bar
的文件,则会引起问题。
--quoting-style=shell
有帮助吗(如果您的ls
甚至支持)?不,仍然显示?对于不可打印的字符,因此仍然不清楚多场比赛中哪一场最大。 --quoting-style=literal
?不,一样。如果只需要清楚地打印最大文件的名称,则--quoting-style=locale
或--quoting-style=c
可能会有所帮助,但是如果以后需要对文件进行某些操作,则可能不会帮忙-撤销引用并返回到实际目录将是一堆代码文件名,这样您就可以将其传递给gzip。在所有工作结束时,即使他拥有的文件名对所有可能的文件名都是安全且正确的,也不可读且无法维护,并且可以用python或perl或ruby更加轻松,安全且可读地完成。
甚至使用其他shell工具-浮现在脑海,我认为这应该可以解决问题:
find . -type f -printf "%s %fq4312078q" | sort -nz | awk 'BEGIN{RS="q4312078q"} END{sub(/[0-9]* /, "", q4312078q); print}'
并且至少应与
--quoting-style
一样轻便。评论
哦,关于尺寸,我确实可以-如果尝试过,我可能可以做到-我应该吗?我有点累或整件事-我喜欢您的回答,因为您没有说不能或不做,但是实际上给出了为什么不这样做的示例,以及其他类似示例-谢谢。
–mikeserv
14年5月16日在16:44
我认为,如果您尝试过,就会发现它比您想象的要难得多。所以,是的,我建议您尝试一下。只要我能想到,我将很乐意继续为您提供不会损坏的文件名。 :)
– godlygeek
2014年5月16日16:50
评论不作进一步讨论;此对话已移至聊天。
– terdon♦
2014年8月23日在10:24
@mikeserv和godlygeek,我已将此评论线程移至聊天位置。请在评论中不要进行像这样的长时间讨论,这就是聊天的目的。
– terdon♦
2014年8月23日在10:28
#9 楼
我“不解析ls
”是因为:文件名可以包含除
/
和NUL
(0x00
)以外的任何ASCII字符。 ls
输出奇怪字符的多字符表示形式。在将文件名传递到另一个程序之前,必须将其反转(撤消)。ls
输出SPACE
(“
”),NewLine(^J
)和其他“表单控件”字符从字面上的文件名。在后续处理中必须格外小心。所有变量必须加引号。在一定的时间长度后,
ls
的日期表示形式从3个字段(“ mmm dd HH:MM
”)更改为1个字段(“ yyyy
”),并且所有随后的字段将重新编号。#1原因不是通过“解析
ls
”获取有关文件的信息:还有一种更好的方法!find
命令可以是用于选择文件,并带有-print0
选项,生成一个文件名列表(完整的奇怪和表单控制字符),由NUL 0x00
字节分隔。带有“
xargs
”选项的-0
命令将消耗NUL
的列表文件名,并将它们(再次完整无缺)传递给在command
命令行上指定的xargs
。 command
甚至可以是bash
脚本。给定文件名列表的
stat
命令可以以您可以指定的格式输出任何文件信息。请阅读
man find xargs stat
。对于傻笑,阅读
man ls
并尝试查看如何保证可解析性
评论
@mikeserv好吧,我做到了。 Shell glob的速度提高了2.48倍。 time bash -c'for {1..1000}中的i;做ls -R&> / dev / null;完成时间= 3.18秒vs时间bash -c'对于{1..1000}中的i;做回声** / *> / dev / null;已完成'= 1.28秒关于您的最新更新,请停止依赖视觉输出来确定您的代码是否有效。将您的输出传递到实际程序,并让程序尝试对文件执行操作。这就是为什么我在回答中使用stat的原因,因为它实际上检查每个文件是否存在。您无法使用sed东西在底部进行操作。
你不能当真与不首先解析ls相比,如何比您简单地解析问题更容易或更简单,或者以任何方式更好?您要描述的内容很难。我需要解构它才能理解所有内容,并且我是一个相对称职的用户。您不可能期望普通的Joe能够处理这样的事情。
-1用于使用问题选择参数。解析ls输出错误的所有原因在原始链接中(以及许多其他地方)都得到了很好的说明。如果OP寻求帮助以了解它,那么这个问题就很合理了,但是OP只是试图证明自己的错误用法是可以的。
@mikeserv不仅仅是解析ls是不好的。对于大多数没有简单输出的命令,在$(command)中执行某些操作并依靠分词来获取准确的结果是不利的。