Bash gsutil:参数列表太长

Bash gsutil:参数列表太长,bash,google-cloud-storage,gsutil,Bash,Google Cloud Storage,Gsutil,我正在尝试使用以下命令将数千个文件上载到Google云存储: gsutil -m cp *.json gs://mybucket/mydir 但我得到了这个错误: -bash: Argument list too long 处理这个问题的最佳方法是什么?我显然可以编写一个bash脚本来迭代不同的数字: gsutil -m cp 92*.json gs://mybucket/mydir gsutil -m cp 93*.json gs://mybucket/mydir gsutil -m cp

我正在尝试使用以下命令将数千个文件上载到Google云存储:

gsutil -m cp *.json gs://mybucket/mydir
但我得到了这个错误:

-bash: Argument list too long
处理这个问题的最佳方法是什么?我显然可以编写一个bash脚本来迭代不同的数字:

gsutil -m cp 92*.json gs://mybucket/mydir
gsutil -m cp 93*.json gs://mybucket/mydir
gsutil -m cp ...*.json gs://mybucket/mydir
但问题是,我事先不知道我的文件名将是什么,所以编写该命令并不简单


有没有一种方法可以用
gsutil
本机处理这个问题(我不这么认为),或者有没有一种方法可以在bash中处理这个问题,我可以一次列出10000个文件,然后通过管道将它们传输到
gsutil
命令?

如果您的文件名不受换行符的影响,您可以使用
gsutil cp
的读取
stdin
的功能,如

find . -maxdepth 1 -type f -name '*.json' | gsutil -m cp -I gs://mybucket/mydir
或者,如果您不确定您的姓名是否安全,并且您的
find
xargs
支持它,您可以这样做

find . -maxdepth 1 -type f -name '*.json' -print0 | xargs -0 -I {} gsutil -m cp {} gs://mybucket/mydir

Eric的答案应该有效,但另一种选择是通过引用通配符表达式依赖gsutil的内置通配符:

gsutil -m cp "*.json" gs://mybucket/mydir
进一步解释:“参数列表太长”错误来自shell,它为扩展通配符提供了有限大小的缓冲区。通过引用通配符,可以防止shell扩展通配符,相反,shell会将该文本字符串传递给gsutil。然后,gsutil以流方式扩展通配符,即在执行操作时扩展通配符,因此它永远不需要缓冲无限量的扩展文本。因此,您可以在任意大的表达式上使用gsutil通配符。在对象名称上使用gsutil通配符时也是如此,因此,例如,这将起作用:

gsutil -m cp "gs://my-bucket1/*" gs://my-bucket2

即使在gs://my-bucket1的顶层有十亿个对象。

这里有一种方法可以做到这一点,使用
xargs
限制一次传递到
gsutil
的文件数。空字节用于防止文件名中的空格或换行出现问题

printf '%s\0' *.json | xargs -0 sh -c 'copy_all () { 
    gsutil -m cp "$@" gs://mybucket/mydir
}
copy_all "$@"'
这里我们定义了一个函数,用于将文件参数放在
gsutil
命令的正确位置。整个过程应以处理所有参数所需的最小次数进行,每次传递可能的最大文件名参数数

或者,您可以单独定义函数,然后导出它(这是特定于bash的):


作为良好的实践,您仍然应该引用
gs://my-bucket1/*
。shell仍然会将该字符串视为要匹配的模式,尽管它几乎肯定无法匹配任何内容,但可以设置一个shell选项,将不匹配的模式视为错误,而不是文字字符串。谢谢chepner-我根据您的建议在我的答案中添加了引号。谢谢,为我节省了一些时间!最后一个示例将更简单,因为
find-exec gsutil-m cp{}gs://mybucket/mydir\。(在任何一种情况下,我认为
-m
都是不必要的,因为您只向
gsutil
的每个实例传递一个文件/URL)@chepner确实
xargs
然后为每个文件生成一个
gsutil
的新实例,如果参数不是在
-exec
使用
\而不是
+
?您的
find
命令版本至少可以移植though@chepner是的,阅读手册页确实证实了您所说的,
-I
意味着
-l1
我认为有一些方法可以使用
xargs
类似于批处理
-exec+
,但我认为这里的问题是,
gsutil
要么采用一个模式,要么采用一个文件。我没有找到一种像GNU
cp
@chepner那样编写类似
-exec cp-t src_dir{}+
的东西的方法,我想出了一种方法,可惜我在发布之前没有刷新页面!
copy_all () { 
    gsutil -m cp "$@" gs://mybucket/mydir
}
printf '%s\0' *.json | xargs -0 bash -c 'export -f copy_all; copy_all "$@"'