错误:参数列表太长
sudo cp
和find... -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
命令,我尝试过:
- 格式化命令为
for loop
tar -cf - -C files... | tar xpf - -C target directory...
- 格式化命令为
git diff ... | xargs cp ...
对于find
命令,我尝试过:
find ... | xargs -n 1000 sudo cp ....
- 添加到此命令的任何不同标志也不起作用。
find ... -exec cp ...
- 任何标志或语法更改都会返回相同的错误
更改命令似乎不是解决方案
在本地重现错误后,我发现在与少量文件(~1000 个或更少)交互时,我的命令已经可以正常工作。
但是,当 的输出cp
大约为find
git diff
1200-1500 个文件或更多然后返回:
参数列表太长
此外,只需从我的 shell 脚本运行find
或也会返回:cp
参数列表太长
因此,我对这些命令做什么似乎并不重要。更根本的原因是导致该问题的原因,但仅限于输出的文件数git diff
超过大约 1200 个文件时。
如何修复我的cp
和find
命令?
如何重现错误
以下是根据我的用例重现此错误所需采取的步骤。我使用了 VS Code,所以我建议您也尽可能与我类似地复制它。请注意,您需要将USERNAME
以下代码替换为您的用户名。
第1步:叉子这个仓库。
第 2 步:在终端中运行下面的每一行代码(一次一行)
cd force-app/main/default
创建一个新文件夹“diff”。然后继续下面。
git checkout -b branch2
cd force-app/main/default/classes
添加//comment
到底部myclass.cls和myclass.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
。