Answers:
cp *.prj ../prjshp/是正确的命令,但是您遇到一种罕见的情况,即遇到大小限制。您尝试的第二个命令没有任何意义。
一种方法是对cp文件进行分块运行。该find命令知道如何执行此操作:
find -maxdepth 1 -name '*.prj' -exec mv -t ../prjshp {} +
find 递归遍历当前目录及其下的目录。-maxdepth 1 表示停在1的深度,即不要递归到子目录中。-name '*.prj'表示仅对名称与指定模式匹配的文件起作用。请注意模式周围的引号:它将由find命令而不是shell解释。-exec … {} +表示对所有文件执行指定的命令。如有必要,它将多次调用命令,请注意不要超过命令行限制。mv -t ../prjshp将指定的文件移动到中../prjshp。在此-t使用该选项是因为find命令的局限性:找到的文件(由标记{})作为命令的最后一个参数传递,您不能在其后添加目标。另一种方法是使用rsync。
rsync -r --include='*.prj' --exclude='*' . ../prjshp
rsync -r … . ../prjshp../prjshp递归复制当前目录。--include='*.prj' --exclude='*'表示复制匹配的文件*.prj并排除其他所有文件(包括子目录,因此.prj将找不到子目录中的文件)。cp * | grep '\.prj$' ../prjshp/ 没有任何意义,但是如果*扩展到文件列表,最后一个是目录(aka cp SOURCE1 SOURCE2....DEST),则在语法上是有效的。当然,管道没有任何意义,但就shell而言,它dup()在语法上仍然有效-它将使文件描述符很好,只是管道的读取器端将不会获取任何数据,因为cp不会写入任何数据。
该命令一个接一个地复制文件,即使它们太多而*无法扩展为一个cp命令,该文件也将起作用:
for i in *; do cp "$i" ../prjshp/; done
面对Argument list too long错误时,请牢记3个关键点:
命令行参数的长度受到ARG_MAX变量的限制,根据POSIX的定义,变量是“ ... [m] 包含环境数据的exec函数的参数的最大长度”(加重)。即,当shell执行非-built-it命令,它必须调用其中之一exec()来产生该命令的过程,而这正是ARG_MAX发挥作用的地方,此外,命令本身的名称或路径(例如/bin/echo)也起作用。
Shell内置命令由Shell执行,这意味着Shell不使用exec()函数族,因此不受ARG_MAX变量影响。
某些命令(例如xargs和)find知道ARG_MAX变量,并在该限制下重复执行操作
从以上几点可以看出,如库萨兰南达对相关问题的出色回答所示,Argument list too long环境大时也会发生这种情况。因此,考虑到每个用户的环境可能会有所不同,并且参数大小(以字节为单位)是相关的,因此很难提出单个文件/参数的数目。
关键是不关注文件的数量,而是关注您将要使用的命令是否涉及exec()函数族,并且切向涉及堆栈空间。
使用shell内置
如前所述,shell内置程序ARG_MAX不受限制,也就是说,诸如for循环,while循环,内置echo和内置之类的东西printf-所有这些都将表现良好。
for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done
在相关的问题有关删除文件,有一个解决方案,例如:
printf '%s\0' *.jpg | xargs -0 rm --
请注意,这使用了shell的内置功能printf。如果我们正在调用external printf,则将涉及exec(),因此将失败,并带有大量参数:
$ /usr/bin/printf "%s\0" {1..7000000}> /dev/null
bash: /usr/bin/printf: Argument list too long
bash数组
根据jlliagre 的回答,bash它没有对数组施加限制,因此也可以完成文件名数组的构建以及每次循环迭代使用切片,如danjpreron的回答所示:
files=( /path/to/old_dir/*.prj )
for((I=0;I<${#files[*]};I+=1000)); do
cp -t /path/to/new_dir/ "${files[@]:I:1000}"
done
但是,这具有特定于bash和非POSIX的局限性。
增加堆栈空间
有时候,你可以看到人们建议增加堆栈空间使用ulimit -s <NUM>; 在Linux上,每个程序的ARG_MAX值是堆栈空间的1/4,这意味着按比例增加堆栈空间会增加自变量的空间。
# getconf reports value in bytes, ulimit -s in kilobytes
$ getconf ARG_MAX
2097152
$ echo $(( $(getconf ARG_MAX)*4 ))
8388608
$ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none
8388608
# Increasing stack space results in increated ARG_MAX value
$ ulimit -s 16384
$ getconf ARG_MAX
4194304
根据引用Linux Journal 的Franck Dernoncourt的回答,人们还可以重新编译Linux参数更大的Linux内核,以获取最大的内存页参数,但是,这比必要的工作更多,并且有被引用的Linux Journal文章所述的潜力。
避免壳
另一种方法是在Ubuntu上默认使用python或python3。下面的python + here-doc示例是我个人用来在40,000项范围内复制大型文件目录的内容:
$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
> if os.path.isfile(f):
> shutil.copy(f,'./newdir/')
> EOF
对于递归遍历,可以使用os.walk。
恕我直言,用于处理大量文件的最佳工具是find和xargs。请参阅man find。请参阅man xargs。find凭借其-print0开关,产生一个NUL文件名-分隔列表(文件名可能包含任何字符execpt NUL或/)该xargs理解,使用该-0开关。xargs然后构建允许的最长命令(文件名最多,末尾没有半文件名)并执行它。xargs重复此操作,直到find不再提供文件名。运行xargs --show-limits </dev/null以查看限制。
为了解决您的问题,(并在man cp找到后进行查找--target-directory=):
find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/