错误:参数列表太长

错误:参数列表太长

错误:参数列表太长

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一个新目录中:

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

我后续也有同样的问题寻找命令。我使用命名约定来查找并复制与 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 自动将堆栈大小设置为8192arg max 为8388608对于每个新构建。在我的deploy.sh文件中,我ulimit -s 9999999将堆栈大小更改为9999999arg 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大约为findgit diff1200-1500 个文件或更多然后返回:

参数列表太长

此外,只需从我的 shell 脚本运行find或也会返回:cp

参数列表太长

因此,我对这些命令做什么似乎并不重要。更根本的原因是导致该问题的原因,但仅限于输出的文件数git diff超过大约 1200 个文件时。

如何修复我的cpfind命令?


如何重现错误

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

第1步:叉子这个仓库

第 2 步:在终端中运行下面的每一行代码(一次一行)

cd force-app/main/default

创建一个新文件夹“diff”。然后继续下面。

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

添加//comment到底部myclass.clsmyclass.cls-meta.xml文件。保存更改。

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 push --set-upstream origin branch2
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”
git push

cd返回到 sfdx-travisci 文件夹。

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

复杂的复制命令替换:

tar -cf - -C /home/auser/data . | tar xpf - -C /home/auser/data2

答案2

与其尝试一些解决方法,不如了解错误的全部内容。该错误是因为给定的参数超出了最大参数长度调用命令。

这可能会发生,因为

  • shell 扩展产生了太长的参数列表
  • 一个进程(如find)调用了族的函数exec,传递了太长的参数列表。

该命令getconf ARG_MAX告诉您参数的最大长度。

命令

cp $(git diff --name-only branch2 force-app/) ...

意味着git执行该命令,然后将其输出作为参数传递给cp.如果此输出超过参数的最大长度,您会收到上述错误。

命令

find ... -exec cp -t target {} +

没有太大不同,因为命令+的尾部find告诉find我们传递所有找到的文件的名称来代替花括号。

因此,解决方案是找到一种不会导致庞大参数字符串的方式来制定命令。xargs或者命令替换可能是你的朋友。

正如 @AaronD.Marasco 在他们的评论中提到的:

find ... -print0 | xargs -0 cp {} target

xargs负责最大参数长度。请注意,这里的花括号是 的参数xargs,而不是 的参数find,但它们具有相同的含义)。

或者使用流程替换(在这种情况下效率较低):

while IFS= read -d $'\0' -r Filename; do
  # do something with $Filename:
  cp "$Filename" target
done < <(find ... -print0)

有关更多详细信息,请参阅find和的手册页xargs

相关内容