sea*_*bry 6 zsh parameter string
给定一个包含以下内容的目录:
note 1.txt, 昨天最后修改note 2.txt,上次修改前天note 3.txt,今天最后修改获取数组的最佳方法是什么note 3 note 1 note 2?
为了定义“最佳”,我更关心稳健性(在 macOS 中的 ZSH 上下文中),而不是效率和可移植性。
预期的用例是一个包含数百或数千个纯文本文件的目录,但是——冒着混淆问题的风险——这是我有一个更普遍的问题的一个特定案例,在文件路径上执行字符串操作的最佳实践是什么由命令打印喜欢ls,find,和mdfind。
我一直在使用一个调用此命令的宏来实现上述目的:
ls -t | sed -e 's/.[^.]*$//'
Run Code Online (Sandbox Code Playgroud)
它从来没有失败过,但是:
ls. (解析ls;实践,在“5. 永远不要做这些”下)。sed在参数扩展会做的地方调用效率低下吗?使用find(用 NUL 字符而不是换行符安全地分隔文件路径)和参数扩展来提取基本名称,这会产生一个未排序的列表:
find . -type f -print0 | while IFS= read -d '' -r l ; do print "${${l%.*}##*/}" ; done
Run Code Online (Sandbox Code Playgroud)
但是按修改日期排序似乎需要调用statand sort,因为 macOSfind缺少-printf标志,否则可能会很好地服务。
最后,使用 ZSH 的glob 限定符:
for f in *(om) ; do print "${f%.*}" ; done
Run Code Online (Sandbox Code Playgroud)
虽然不可移植,但最后一种方法对我来说似乎是最健壮和最有效的。这是正确的,find当我实际执行搜索而不是简单地列出目录中的文件时,是否有任何理由不应该使用上述命令的修改版本?
Sté*_*las 11
在zsh,
list=(*(Nom:r))
Run Code Online (Sandbox Code Playgroud)
绝对是最健壮的。
print -rC1 -- *(Nom:r)
Run Code Online (Sandbox Code Playgroud)
每行打印一个,或
print -rNC1 -- *(Nom:r)
Run Code Online (Sandbox Code Playgroud)
作为 NUL 分隔的记录,以便能够对该输出执行任何操作,因为 NUL 是文件路径中唯一不允许的字符。
*(N-om:r)如果您希望在符号链接解析后考虑修改时间(目标的 mtime 而不是像 with 那样的符号链接ls -Lt),请更改为。
:r(对于根名称)是csh删除扩展名的历史修饰符(来自)。请注意,.bashrc如果您启用了该dotglob选项,它会变成空字符串,这只会在此处引起关注。
改为**/*(N-om:t:r)递归进行(:t为尾部(basename),即去除目录组件)。
为任意文件名可靠地执行此操作ls将非常痛苦。
一种方法可能是运行ls -td -- ./*(假设文件名列表符合 arg 列表限制)并解析该输出,依赖于每个文件名以 开头的事实./,并生成一个 NUL 分隔的列表或一个 shell 引用的列表将它传递给外壳,但是除非您求助于perl或 ,否则可移植地执行此操作也非常痛苦python。
但是,如果您可以依赖perl或python在那里,您将能够让它们生成和排序文件列表,并以 NUL 分隔输出(尽管如果您想支持亚秒级精度,可能不那么容易移植)。
Run Code Online (Sandbox Code Playgroud)ls -t | sed -e 's/.[^.]*$//'
对于包含换行符的文件名将无法正常工作(IIRC 某些版本的 macOS 在/etc默认情况下确实带有此类文件名)。对于包含未形成有效字符的字节序列的文件名,它也可能失败,.或者[^.]可能无法匹配它们。不过,它可能不适用于 macOS,可以通过将区域设置设置为C/ POSIXfor来修复sed。
该.应进行转义(s/\.[^.]*$//),因为它是正则表达式运算符的任何字符相匹配否则,原来点较少文件,比如foobar为空字符串。
请注意,要打印字符串raw,它是:
print -r -- "$string"
Run Code Online (Sandbox Code Playgroud)
以print "$string"$string开头的值将失败-,甚至引入命令注入漏洞(例如尝试使用string='-va[$(uname>&2)1]',此处使用无害uname命令)。并且会破坏包含\字符的值。
您的:
Run Code Online (Sandbox Code Playgroud)find . -type f -print0 | while IFS= read -d '' -r l ; do print "${${l%.*}##*/}" ; done
还有一个问题是您.* 在删除目录组件之前删除了 。因此,例如 a./foo.d/bar将变为foo而不是bar并且./foo将变为空字符串。
关于find在各种 shell 中处理输出的安全方法,请参阅为什么循环查找的输出是不好的做法?
| 归档时间: |
|
| 查看次数: |
377 次 |
| 最近记录: |