为什么会出现“ usr / bin / cp(&find):参数列表太长”的信息?

时间:2020-08-04 18:37:42

标签: linux bash shell ubuntu travis-ci

错误:参数列表过长

sudo cpfind... -exec sudo cp产量

/usr/bin/sudo(or find): Argument list too long

我在Travis CI构建输出中遇到此错误。我的.travis.yml文件使用shell脚本(deploy.sh)对branch2的force-app文件夹运行git diff,输出(数千个文件)是cp进入新目录:< / p>

sudo cp --parents $(git diff --name-only branch2 force-app/) $DEPLOYDIRECTORY;

错误为两行:

./deploy.sh: line 122: /usr/bin/git: Argument list too long
./deploy.sh: line 122: /usr/bin/sudo: Argument list too long

后续的 find 命令遇到相同的问题。我使用命名约定从git diff查找并复制与每个文件相对应的文件:

for FILE in $GIT_DIFF_FILES; do
    if [[ $FILE == *Test.cls ]]; then
        find $classPath -maxdepth1 -samefile "$FILE-meta.xml" -exec sudo cp --parents {} $DEPLOY_DIRECTORY +
    fi;
done;

再次,我得到了错误:./deploy.sh: line 142: /usr/bin/find: Argument list too long

注意:Travis CI正在为此版本运行Ubuntu Linux(Xenial)虚拟环境。


我尝试过的

1。重置堆栈大小

对于每个新版本,

Travis CI自动将堆栈大小设置为 8192 ,将arg max设置为 8388608 。在我的deploy.sh文件中,我ulimit -s 9999999将每个新版本的堆栈大小更改为 9999999 ,并将arg max更改为 2559999744

2。命令变化

对于第一个sudo cp命令,我尝试过:

  1. 将命令格式化为for loop
  2. tar -cf - -C files... | tar xpf - -C target directory...
  3. 将命令格式化为git diff ... | xargs cp ...

对于find命令,我已经尝试过:

  1. find ... | xargs -n 1000 sudo cp ....
    • 添加到此命令的任何其他标志也不起作用。
  2. find ... -exec cp ...
    • 任何标志或语法更改都会返回相同的错误

更改命令似乎不是解决方案

在本地重现错误后,我发现与少量文件(〜1000个或更少)交互时,我的命令已经可以正常工作。

但是,当cp的输出大约为 1200-1500个文件或更多时,运行findgit diff命令,然后返回:

参数列表太长

此外,仅从我的shell脚本中运行findcp也会返回:

参数列表太长

因此,我对这些命令执行的操作似乎无关紧要。更根本的原因是导致此问题的原因,但是仅当git diff输出的文件数超过大约1200个文件时。

如何修复cpfind命令?


如何重现错误

以下是根据我的用例重现此错误的步骤。我使用了VS Code,所以我建议您也尽可能地复制它。请注意,您需要将以下代码中的USERNAME替换为用户名。

第1步:叉this repo

第2步:在终端中按照以下步骤操作

cd force-app/main/default
mkdir diff
git checkout -b branch2
cd force-app/main/default/classes

在{strong> myclass.cls 和 myclass.cls-meta.xml 文件的底部添加//comment。保存更改。

for n in {001..1500}; do cp myclass.cls myclass$n.cls; done
for n in {001..1500}; do cp myclass.cls-meta.xml myclass$n.cls-meta.xml; done
git add .
git commit -m “first commit”
git checkout master
for n in {001..1500}; do cp myclass.cls myclass$n.cls; done
for n in {001..1500}; do cp myclass.cls-meta.xml myclass$n.cls-meta.xml; done
git add .
git commit -m “second commit”
cd .. #back to the sfdx-travisci folder
sudo cp -p $(git diff --name-only branch2 /Users/USERNAME/sfdx-travisci/force-app/main/default/classes) /Users/USERNAME/sfdx-travisci/force-app/main/default/diff
for file in $(sudo cp -p $(git diff --name-only branch2 /Users/USERNAME/sfdx-travisci/force-app/main/default/classes) /Users/USERNAME/sfdx-travisci/force-app/main/default/diff); do if [[ $file == *.cls ]]; then find /Users/USERNAME/sfdx-travisci/force-app/main/default/classes -samefile “$file-meta.xml” -exec sudo cp -p {} /Users/USERNAME/sfdx-travisci/force-app/main/default/diff +; fi; done;

1 个答案:

答案 0 :(得分:1)

在大多数操作系统上,命令行长度是有限制的。通常的解决方案不是使用命令行,而是使用管道,最后使用xargs来获取一批文件作为参数。

例如,您可以使用:

find . | xargs -n 1000 sudo cp ....

在这种情况下,xargs会一次将参数拆分为1000个文件,并在每个批次中执行sudo cp。这样,您应该不会有问题(但是在非常长的目录和文件名上)。

如果只想执行一次sudo,则可能需要移动sudo内的内容(警告:您可能以root用户的身份访问更多文件,并且应注意管道和重定向是在sudo内部完成,而不是在调用shell中完成。

注意:您还应该将diff更改为git diff

通常我们在-print0中使用find,在-0中使用xargs,这样文件名中的特殊字符就不会出现问题。 (在POSIX兼容系统中,文件名中禁止使用\U+0000

注意:如果可行,您可能希望将带有补丁的错误报告发送给创建原始文件的组(如果该文件不是由您创建的)。我们经常发现这类错误:在特殊情况下失败的命令。