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/