我正在尝试将五个最大的文件从某个目录复制到我的pwd
. 使用cp specific/directory$(ls -S specific/directory | head -n) ./
复制第一个文件,然后继续cannot stat
为列表中的其余文件产生错误。
为什么管道对第一个项目有效而对其余项目无效?
我正在尝试将五个最大的文件从某个目录复制到我的pwd
. 使用cp specific/directory$(ls -S specific/directory | head -n) ./
复制第一个文件,然后继续cannot stat
为列表中的其余文件产生错误。
为什么管道对第一个项目有效而对其余项目无效?
我所有的解决方案都只处理文件,按要求处理,并且可以处理所有类型的文件
(即使有特殊字符)。
如果你想使用
ls -S
以正确的方式做:
要求最近
GNU
coreutils
。coreutils 9.1-1
这里。另一种方式,使用
bash
和最近GNU
find
:findutils 4.9.0-4
这里。基于此:
${files[@]:0:5}
扩展到files数组的前 5 个元素,其键大于或等于 0。对于旧工具,通过
Perl
任何 shell使用
zsh
您可以避免与解析和排序输出相关的所有陷阱ls
:或使用 GNU
cp
(用于-t
选项):glob 限定符在哪里
.
仅匹配普通文件(不匹配目录、符号链接、fifos、套接字。)D
切换 dotglob 选项 - 如果您想排除隐藏文件,请忽略此选项OL[1,5]
按文件长度(大小)对结果进行排序并选择前 5 个并且该
-n
选项可以防止cp
在名称冲突的情况下破坏现有文件。编辑:新答案,更完整:
原始失败的原因是目录名称仅添加到第一个结果,因此剩余的结果不存在于当前目录中,导致没有此类文件的错误。
一种不使用的方法
find
是利用选项-F
tols
,它包括指示 inode 类型的尾随字符。以下是一个不完整的答案,它通过 ; 从列表中删除了目录grep
;更完整的答案将删除应排除的其他 inode 类型。这些sed
命令删除了*
添加到可执行文件的-F
.原始答案:
假设最大的文件是一、二、三和四。问题中的命令最终是
由于 2、3 和 4 未在 . 中退出,因此命令失败。类似于
会做的。
警告:如果任何文件名中有任何换行符(或者即使
ls
不打印到终端时您的文件名也被破坏),这也会中断。整合其他答案:
TL; DR:请参阅下文了解
bash
POSIX shell 的可行解决方案。因为 shell 的行为并不像您的命令所假定的那样。
Command
$(ls -S | head)
Substitution 确实被它的输出所取代,并且确实粘贴在紧邻代码片段的右侧cp specific/directory
,但是:IFS
;后者默认设置为ls -S | head
分隔每个文件名的命令,因此每个名称最终都是一个单独的独立路径听从您的cp
命令;请注意,在这种情况下,双引号命令替换将无济于事,因为您可能已经发现specific/directory/
为每个名称复制代码段;(这将是 Brace Expansion 的工作,但在这种情况下很难做到正确);因此,只有第一个如此独立的名称获得目录前缀,因此可以通过 访问cp
,而其他 4 个名称预计会出现在当前目录中,但显然它们不是(即使它们是,也会cp
有然后抱怨它们实际上与目标目录中的文件相同./
)它可以“工作”吗?原则上是的,但它很脆弱,因为一旦其中一个 n 文件包含变量中指定的字符之一,它就会崩溃
IFS
;更糟糕的是,如果与一个不受控制的结合,eval
它可以用于最经典的命令注入,如果你不能完全控制specific/directory
. (另外,请参见下面的注释1)。bash
和 POSIX shell的可能解决方案除了其他答案中提到的使用 GNU coreutils v9.0 及以上版本时可用的解决方案外,还可以使用 coreutils v8.25(大约 2016 年)及以上版本的GNU
ls --zero
安全地完成操作1 ,它提供了shell 的变体。为此,我们需要使用,因为这实际上是从该选项中获益的唯一方法,该选项确实旨在与一起使用。ls
--quoting-style
eval
ls
eval
像往常一样,
eval
需要格外小心处理,如果有的话。在这里,我们仅将它专门用于ls
命令,并依赖于ls
根据记录的行为为 shell 正确引用文件名。对于额外的注意,可以调用例如提供所需选项的可执行文件/bin/ls
的显式完整路径,而不是冒险去使用谁知道哪个恰好在或者谁知道导出的流氓函数(甚至别名)有目的地命名。ls
--quoting-style
ls
$PATH
ls
所以,与
bash
:您可以通过更改
head -n 5
.请注意,在上面的代码片段中,我添加了额外的安全和错误检查,但实际上,如果您对您的版本绝对肯定
ls
并且没有真正的理由失败或输出杂散字符,则可以将整个事情精简为基本命令.为 POSIX shell 制作的上述解决方案的等价物也可以安全地工作1尽管它并不完全理想,因为它需要将命令提供的整个文件列表加载到内存中
ls
。由于我们无法在它到达 shell 之前过滤掉这样的列表,源目录不能包含足够多的文件来填充可用内存,否则 shell 将在运行命令之前死掉cp
:在这里,您通过更改位来更改前 n 个文件的数量
$(($# - 5))
。与
bash
版本一样,只要您再次对所需的先决条件持肯定态度,这个版本也可以稍微精简。这个,除了bash
精简版本,还需要至少 n 个文件实际存在于源目录中,否则shift
命令将失败使 shell 过早中止(例如,如果你的文件少于 5 个specific/directory
,这精简版不会复制它们)。1 注意:为了简单和解释,上面的解决方案不检查文件实际上只是常规文件(即不是目录或符号链接、套接字、命名 fifos、设备文件)。因此,如果您的源目录确实碰巧在最大的 n 个文件中有那些类型的“文件”(即使有效计数为 0 字节),上述解决方案将在最终
cp
命令中包含这些名称。这对于总是计数大于 0 的符号链接和目录特别相关,具体取决于它们的内容,因此可能在ls -S
. 自然地,我们可以遍历文件名来测试它们的文件类型并丢弃非常规文件,但它会变得越来越复杂,尤其是用下一个级别替换丢弃的文件。请查看其他答案以理智地处理这些情况,因为我在这里的解决方案已经扩展了bash
POSIX shell 的能力。