使用 zsh 对不同目录中的文件列表进行排序
Sort list of files in different directories with zsh
假设我有以下 directory/file 结构
dirA/1fileAA.zsh
dirA/99fileAB.zsh
dirB/2fileBA.zsh
dirB/50fileBB.zsh
dirB/subdirA/20fileBAA.zsh
我想按文件名开头的数字排序,忽略任何目录,所以我得到
dirA/1fileAA.zsh
dirB/2fileBA.zsh
dirB/subdirA/20fileBAA.zsh
dirA/99fileAB.zsh
dirB/50fileBB.zsh
仅使用内置 zsh
功能。
实现此目标的最佳方法是什么?
我能想到重写字符串排序并写回吗?
或者更好地尝试创建关联数组并按键排序?
我还是zsh
,想避免挖错方向,太多了。
这是一种仅使用 zsh
内置函数即可完成此操作的方法。该函数将文件名添加到每个路径的前面进行排序,然后将其删除:
function sortByFilename {
local -a ary
printf -v ary '%s/%s' ${${argv:t}:^argv}
print -l ${${(n)ary}#*/}
}
使用您的示例目录设置,可以从 dirA
和 dirB
的父目录调用它:
sortByFilename **/*.zsh
正在测试:
sortByFilename \
dirA/1fileAA.zsh \
dirA/99fileAB.zsh \
dirB/2fileBA.zsh \
dirB/50fileBB.zsh \
'/leadslash/42 and spaces' \
dirB/subdirA/20fileBAA.zsh
结果:
dirA/1fileAA.zsh
dirB/2fileBA.zsh
dirB/subdirA/20fileBAA.zsh
/leadslash/42 and spaces
dirB/50fileBB.zsh
dirA/99fileAB.zsh
件数:
printf -v ary <fmt> ...
:使用格式字符串运行 printf,并将结果分配给 ary
数组。格式字符串的每次迭代都将成为数组中的另一个元素。
%s/%s
:格式字符串。这将用斜杠分隔符连接两个字符串。
如果输入中的值多于格式字符串中的说明符,printf
将重复格式模式。所以在这里,它将从输入数组中提取对(filename/pathname)。
${${argv:t}:^argv}
:这将生成一个包含文件名和完整路径的数组,即 (file1 path1 file2 path2 ...)
${ :^ }
:zsh 参数扩展,将压缩两个数组以创建交替文件名和路径。
${argv:t}
:文件名数组。使用 argv
中的函数位置参数和 :t
修饰符构建,returns 数组中每个元素的文件名组件。
argv
: 完整路径数组。
print -l
:在单独的行上打印输入的每个元素。
${${(n)ary}#*/}
:最终排序的路径列表。
${(n)ary}
:Returns数组按数字排序,使用n
参数扩展标志。此时,ary
中的每个元素都是文件名、斜杠和输入路径的串联。
由于文件名模式,n
标志在这里起作用;它将按十进制值排序,而不是在公共/空前缀内按词法排序,例如foo1 foo3 foo12
.
${ #*/}
:从数组中每个元素的前面删除模式 */
。这将删除用于排序的前缀,保留原始路径。
local -a ary
:声明一个数组变量。这用作 printf -v
拆分其输出的指标。
可以通过 (re-/mis-/ab) 使用预先声明的数组 argv
来消除这一行并使函数更短和更隐秘。
function sortByFilename {
printf -v argv %s/%s ${${argv:t}:^argv}
print -l ${${(n)argv}#*/}
}
编辑 - 单行版本:
(){print -l ${"${(n0)$(printf '%s/%s[=15=]' ${${argv:t}:^argv})}"#*/}} **/*.zsh
包括这一点只是因为创建单行线很有趣,而不是因为它被推荐。使用匿名函数、进程替换和附加参数扩展标志,这比上面的函数可读性更差,效率可能更低。
假设我有以下 directory/file 结构
dirA/1fileAA.zsh
dirA/99fileAB.zsh
dirB/2fileBA.zsh
dirB/50fileBB.zsh
dirB/subdirA/20fileBAA.zsh
我想按文件名开头的数字排序,忽略任何目录,所以我得到
dirA/1fileAA.zsh
dirB/2fileBA.zsh
dirB/subdirA/20fileBAA.zsh
dirA/99fileAB.zsh
dirB/50fileBB.zsh
仅使用内置 zsh
功能。
实现此目标的最佳方法是什么?
我能想到重写字符串排序并写回吗? 或者更好地尝试创建关联数组并按键排序?
我还是zsh
,想避免挖错方向,太多了。
这是一种仅使用 zsh
内置函数即可完成此操作的方法。该函数将文件名添加到每个路径的前面进行排序,然后将其删除:
function sortByFilename {
local -a ary
printf -v ary '%s/%s' ${${argv:t}:^argv}
print -l ${${(n)ary}#*/}
}
使用您的示例目录设置,可以从 dirA
和 dirB
的父目录调用它:
sortByFilename **/*.zsh
正在测试:
sortByFilename \
dirA/1fileAA.zsh \
dirA/99fileAB.zsh \
dirB/2fileBA.zsh \
dirB/50fileBB.zsh \
'/leadslash/42 and spaces' \
dirB/subdirA/20fileBAA.zsh
结果:
dirA/1fileAA.zsh
dirB/2fileBA.zsh
dirB/subdirA/20fileBAA.zsh
/leadslash/42 and spaces
dirB/50fileBB.zsh
dirA/99fileAB.zsh
件数:
printf -v ary <fmt> ...
:使用格式字符串运行 printf,并将结果分配给ary
数组。格式字符串的每次迭代都将成为数组中的另一个元素。%s/%s
:格式字符串。这将用斜杠分隔符连接两个字符串。
如果输入中的值多于格式字符串中的说明符,printf
将重复格式模式。所以在这里,它将从输入数组中提取对(filename/pathname)。${${argv:t}:^argv}
:这将生成一个包含文件名和完整路径的数组,即(file1 path1 file2 path2 ...)
${ :^ }
:zsh 参数扩展,将压缩两个数组以创建交替文件名和路径。${argv:t}
:文件名数组。使用argv
中的函数位置参数和:t
修饰符构建,returns 数组中每个元素的文件名组件。argv
: 完整路径数组。
print -l
:在单独的行上打印输入的每个元素。${${(n)ary}#*/}
:最终排序的路径列表。${(n)ary}
:Returns数组按数字排序,使用n
参数扩展标志。此时,ary
中的每个元素都是文件名、斜杠和输入路径的串联。
由于文件名模式,n
标志在这里起作用;它将按十进制值排序,而不是在公共/空前缀内按词法排序,例如foo1 foo3 foo12
.${ #*/}
:从数组中每个元素的前面删除模式*/
。这将删除用于排序的前缀,保留原始路径。
local -a ary
:声明一个数组变量。这用作printf -v
拆分其输出的指标。
可以通过 (re-/mis-/ab) 使用预先声明的数组argv
来消除这一行并使函数更短和更隐秘。function sortByFilename { printf -v argv %s/%s ${${argv:t}:^argv} print -l ${${(n)argv}#*/} }
编辑 - 单行版本:
(){print -l ${"${(n0)$(printf '%s/%s[=15=]' ${${argv:t}:^argv})}"#*/}} **/*.zsh
包括这一点只是因为创建单行线很有趣,而不是因为它被推荐。使用匿名函数、进程替换和附加参数扩展标志,这比上面的函数可读性更差,效率可能更低。