【问题标题】:gsutil: Argument list too longgsutil:参数列表太长
【发布时间】:2017-11-30 11:53:31
【问题描述】:

我正在尝试使用以下命令将数千个文件上传到 Google Cloud Storage:

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 本地处理这个问题(我不这么认为,from the documentation),或者有办法在 bash 中处理这个问题,我可以一次列出 10,000 个文件,然后通过管道他们到gsutil 命令?

【问题讨论】:

    标签: bash google-cloud-storage gsutil


    【解决方案1】:

    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 的顶层有十亿个对象。

    【讨论】:

    • 作为一种好的做法,您仍应引用gs://my-bucket1/*。 shell 仍然会将该字符串视为要匹配的模式,尽管它几乎肯定会无法匹配任何内容,但可以设置一个 shell 选项来将不匹配的模式视为错误而不是文字字符串。跨度>
    • 感谢 chepner - 根据您的建议,我在答案中添加了引号。
    • 谢谢,节省了我一些时间!
    【解决方案2】:

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

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

    或者如果你不确定你的名字是否安全并且你的 findxargs 支持它,你可以这样做

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

    【讨论】:

    • 最后一个例子会更简单如find ... -exec gsutil -m cp {} gs://mybucket/mydir \;。 (无论哪种情况,我认为 -m 是不必要的,因为您只将单个文件/URL 传递给 gsutil 的每个实例。)
    • @chepner 执行xargs 然后为每个文件生成gsutil 的新实例,如果参数不在-exec 的末尾,使用\; 而不是+?你的find 命令版本至少是可移植的
    • @chepner 是的,阅读手册页确实如您所说,-I 暗示 -L 1
    • 我认为有一些方法可以使用xargs 来类似地批处理-exec ... +,但我认为这里的问题是gsutil 要么采用模式,要么采用单个文件。我没有看到像使用 GNU cp 那样编写 -exec cp -T src_dir {} + 之类的方法。
    • @chepner 我想出了一个办法,可惜我在发帖前没有刷新页面!
    【解决方案3】:

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

    printf '%s\0' *.json | xargs -0 sh -c 'copy_all () { 
        gsutil -m cp "$@" gs://mybucket/mydir
    }
    copy_all "$@"'
    

    这里我们定义了一个函数,用于将文件参数放在gsutil 命令中的正确位置。整个过程应以处理所有参数所需的最少次数发生,每次都传递尽可能多的文件名参数。

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

    copy_all () { 
        gsutil -m cp "$@" gs://mybucket/mydir
    }
    printf '%s\0' *.json | xargs -0 bash -c 'export -f copy_all; copy_all "$@"'
    

    【讨论】:

      猜你喜欢
      • 2019-06-03
      • 2020-05-09
      • 2014-05-21
      • 2015-04-04
      • 2021-10-26
      • 2014-04-18
      • 2021-10-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多