我的所有解决方案仅根据要求处理文件,并且可以处理所有类型的文件
(即使带有特殊字符)。
ls -S
以正确的方式做:
ls --zero -S | head -z -n5 | xargs -r0 cp -t ./other/dir --
Run Code Online (Sandbox Code Playgroud)
要求最近GNU
coreutils
.
coreutils 9.1-1
这里。
bash
和最近GNU
find
:findutils 4.9.0-4
这里。
基于此:
shopt -s nullglob
cd specific/directory/ || exit
print0 () {
[ "$#" -eq 0 ] || printf '%s\0' "$@"
}
readarray -td '' files < <(
print0 * |
find -files0-from - -maxdepth 0 -type f -printf '%b\t%p\0' |
sort -rzn |
cut -zf2 -
)
cp -av -- "${files[@]:0:5}" "$OLDPWD"/
Run Code Online (Sandbox Code Playgroud)
${files[@]:0:5}
正在扩展到files数组中键大于或等于 0的前 5 个元素。Perl
任何 shellperl -e 'rename($_, "./other/dir/$_") for ((sort { -s $b <=> -s $a } <*>))[0..4]'
Run Code Online (Sandbox Code Playgroud)
使用zsh
您可以避免与解析和排序输出相关的所有陷阱ls
:
cp -n -- specific/directory/*(.DOL[1,5]) ./
Run Code Online (Sandbox Code Playgroud)
或使用 GNU cp
(用于-t
选项):
cp -n -t ./ -- specific/directory/*(.DOL[1,5])
Run Code Online (Sandbox Code Playgroud)
全局限定符在哪里
.
仅匹配普通文件(不匹配目录、符号链接、fifo、套接字。)D
切换 dotglob 选项 - 如果您想排除隐藏文件,请忽略此选项OL[1,5]
按文件长度(大小)对结果进行排序并选择前 5 个该-n
选项可以防止cp
在名称冲突的情况下破坏现有文件。
整合其他答案:
\nTL; DR:请参阅下面的bash
POSIX shell 的可行解决方案。
\n\n为什么管道对第一个项目有效而对其余项目失败?
\n
因为 shell 的行为与您的命令所假设的不同。
\n命令$(ls -S | head)
替换确实被其输出替换,并且确实粘贴在紧邻代码片段右侧的位置cp specific/directory
,但是:
IFS
;后者默认设置为
(单个空格)加 <tab> 加 <newline> 字符,并且 <newline> 恰好是命令用来ls -S | head
分隔每个文件名的内容,因此每个名称最终都是一个单独的独立路径听从您的cp
命令;请注意,在这种情况下,双引号命令替换不会有帮助,因为您可能已经发现specific/directory/
每个名称的片段;(这将是支撑扩展的工作,但在这种情况下要做好它会很棘手);因此,只有第一个这样单独的名称获得目录前缀,因此可以通过 访问cp
,而其他 4 个名称预计会出现在当前目录中,但显然它们不会(即使它们存在,也会出现在当前目录中cp
)然后抱怨它们实际上与目标目录中的文件相同./
)可以让它“工作”吗?原则上是的,但它很脆弱,因为一旦 n 个文件之一包含变量中指定的字符之一,它就会崩溃IFS
;更糟糕的是,eval
如果您无法完全控制specific/directory
. (另外,请参阅下面的注释1)。
bash
POSIX shell的可能解决方案
除了其他答案中提到的使用 GNU coreutils v9.0 及以上版本时可用的解决方案之外,还可以使用coreutils v8.25(大约 2016)及以上版本的GNU安全地ls --zero
完成该操作,它提供了shell 的变体。为此,我们需要使用,因为这实际上是从该选项中受益的唯一方法,该选项确实是设计用于使用的。ls
--quoting-style
eval
ls
eval
像往常一样,eval
如果有的话,需要格外小心地处理。在这里,我们仅将它用于命令ls
,并依赖于ls
根据记录的行为为 shell 正确引用文件名。为了额外的注意,人们可以调用例如提供所需选项的可执行文件的/bin/ls
显式完整路径,而不是冒险使用谁知道哪个恰好在或谁知道故意命名的导出流氓函数(甚至别名)。ls
--quoting-style
ls
$PATH
ls
所以,与bash
:
(\n set -o pipefail \\\n && o="$(/bin/ls -S --quoting-style=shell-escape-always | head -n 5)" \\\n && eval "set -- $o" \\\n && (("$#")) && cp -n -- "${@/#/specific/directory/}" .\n)\n
Run Code Online (Sandbox Code Playgroud)\n您可以通过更改head -n 5
.
请注意,在上面的代码片段中,我添加了额外的安全和错误检查,但实际上,如果您对自己的版本绝对肯定ls
并且没有真正的原因失败或输出杂散,则整个事情可以精简为基本命令人物。
(cd specific/directory && \\\n eval "cp -n -- $(ls -S --quoting-style=shell-escape-always | head -n 5)"\' "$OLDPWD"\')\n
Run Code Online (Sandbox Code Playgroud)\n上述针对 POSIX shell 的解决方案的等效方案也可以安全地工作1,尽管它并不完全理想,因为它需要将命令提供的整个文件列表加载到内存中ls
。由于我们无法在此类列表到达 shell 之前过滤掉该列表,因此源目录不得包含足够多的文件以填充可用内存,否则 shell 将在运行命令之前终止cp
:
(\n set -- && cd specific/directory \\\n && o="$(/bin/ls -rSxw 0 --quoting-style=shell-always)" && eval "set -- $o" \\\n && [ "$#" -gt 0 ] && n="$(($# - 5))" && shift "$(($n > 0 ? $n : 0))" \\\n && cp -n -- "$@" "$OLDPWD"\n)\n
Run Code Online (Sandbox Code Playgroud)\n在这里,您可以通过更改位来更改前 n 个文件的数量$(($# - 5))
。
就像这个版本一样bash
,只要您再次确定所需的先决条件,这个版本也可以稍微精简一下。除了bash
精简版本之外,这个版本还需要至少 n 个文件实际存在于源目录中,否则该shift
命令将失败,导致 shell 过早中止(例如,如果 中的文件少于 5 个specific/directory
,则此命令将失败)精简版不会复制它们)。
(\n set -- && cd specific/directory \\\n && eval "set -- $(ls -rSxw 0 --quoting-style=shell-always)" \\\n && shift "$(($# - 5))" && cp -n -- "$@" "$OLDPWD"\n)\n
Run Code Online (Sandbox Code Playgroud)\n1 \n注意:为了简单和解释,上面的解决方案不检查文件是否实际上只是常规文件(即不是目录或符号链接、套接字、named-fifos、设备文件)。因此,如果您的源目录恰好在前最大的 n 个文件中包含此类“文件”(即使有效计数为 0 字节),则上述解决方案将在最终cp
命令中包含这些名称。这对于符号链接和目录尤其相关,它们的计数总是大于 0,具体取决于其内容,因此可能比ls -S
. 当然,我们可以循环文件名来测试它们的文件类型并丢弃非常规文件,但是它会变得越来越复杂,特别是用下一个替换丢弃的文件。请参阅其他答案来理智地处理这些情况,因为我在这里的解决方案已经扩展了bash
\xc2\xa0 和 POSIX shell 的能力。
归档时间: |
|
查看次数: |
683 次 |
最近记录: |