从 1,000,000 个 .json 文件(不包括任何目录)创建一个 .tar.gz 文件的终端命令
Terminal command to create a .tar.gz files from 1,000,000 .json files (without including any directory)
我有一个包含 1,000,000 个 .json
文件的目录,并使用以下命令仅从 json 文件(不包括 /Library/WebServer/a/a/e/j/
路径)构建 j.tar.gz
:
cd /Library/WebServer/a/a/e/j && tar -zcvf j.tar.gz *.json
发生此错误:...Argument list too long
。你会建议一个更好的命令来完成这个任务吗?谢谢。
您的系统设置了限制。您可以查看
$ getconf ARG_MAX
我的returns
131072
或者,您可以为 tar
创建一个文件列表并使用 -T
、--files-from F
选项来获取名称而不是 globbing,这会达到最大参数限制。
尝试:
find . -type f -name "*.json" > ./include_file && tar -zcvf j.tar.gz --files-from ./include_file
注意:这已在 CentOS/RedHat 6.7.
上成功测试
最初的警告:tar
不是标准定义的工具(POSIX 存档器是 pax
),因此它的行为可能因平台而异,没有任何最低保证基线。您的里程可能会有所不同。
因为这是为 bash
标记的,您可以使用 <()
-- 进程替换 -- 生成一个文件名,当读取时,将发出子进程的输出而不需要临时文件。 (如果您的操作系统支持它们,这通常会实现为 /dev/fd
名称,否则会实现为命名管道)。
如果您只想将 cd
应用于 tar
命令,您可以按如下方式进行,将其放入子 shell 并使用 exec
让 subshell 用 tar
命令替换自己,避免 subshell 否则造成的 fork 惩罚:
dir=/Library/WebServer/a/a/e/j
(cd "$dir" && exec tar --null -zcvf j.tar.gz -T <(printf '%s[=10=]' *.json) )
或者,如果您的 tar
支持它,您可以使用 --include
告诉 tar
自己过滤名称:
tar -C "$dir" --include='*.json' -cvzf "$dir/j.tar.gz" .
注意事项:
printf '%s\n' *.json
不受此影响,因为 printf
是一个 shell 内置;因此,glob 结果不会放入 execv
-family 系统调用的参数中,因此 ARG_MAX
不适用。
- 在
find
上使用 --null
,在 printf
上使用 '%s[=30=]'
(如果您使用 find
生成姓名列表,则使用 -print0
) 可以防止恶意生成的带有文字换行符的名称能够将任意名称注入到您的流中。想想如果有人运行 mkdir -p $'hello/\n/etc/passwd\n.json'
会发生什么——你不希望 /etc/passwd
进入你的 tarball。
怎么样:
> cd /Library/WebServer/a/a/e/j
> find . -name '*.json' -maxdepth 1 | xargs tar -czvf j.tar.gz --add-file
它不需要临时文件,也不需要在 shell 中执行 *.json
这会失败。
在 Ubuntu 上查看过 Mac 手头没有。
我有一个包含 1,000,000 个 .json
文件的目录,并使用以下命令仅从 json 文件(不包括 /Library/WebServer/a/a/e/j/
路径)构建 j.tar.gz
:
cd /Library/WebServer/a/a/e/j && tar -zcvf j.tar.gz *.json
发生此错误:...Argument list too long
。你会建议一个更好的命令来完成这个任务吗?谢谢。
您的系统设置了限制。您可以查看
$ getconf ARG_MAX
我的returns
131072
或者,您可以为 tar
创建一个文件列表并使用 -T
、--files-from F
选项来获取名称而不是 globbing,这会达到最大参数限制。
尝试:
find . -type f -name "*.json" > ./include_file && tar -zcvf j.tar.gz --files-from ./include_file
注意:这已在 CentOS/RedHat 6.7.
上成功测试最初的警告:tar
不是标准定义的工具(POSIX 存档器是 pax
),因此它的行为可能因平台而异,没有任何最低保证基线。您的里程可能会有所不同。
因为这是为 bash
标记的,您可以使用 <()
-- 进程替换 -- 生成一个文件名,当读取时,将发出子进程的输出而不需要临时文件。 (如果您的操作系统支持它们,这通常会实现为 /dev/fd
名称,否则会实现为命名管道)。
如果您只想将 cd
应用于 tar
命令,您可以按如下方式进行,将其放入子 shell 并使用 exec
让 subshell 用 tar
命令替换自己,避免 subshell 否则造成的 fork 惩罚:
dir=/Library/WebServer/a/a/e/j
(cd "$dir" && exec tar --null -zcvf j.tar.gz -T <(printf '%s[=10=]' *.json) )
或者,如果您的 tar
支持它,您可以使用 --include
告诉 tar
自己过滤名称:
tar -C "$dir" --include='*.json' -cvzf "$dir/j.tar.gz" .
注意事项:
printf '%s\n' *.json
不受此影响,因为printf
是一个 shell 内置;因此,glob 结果不会放入execv
-family 系统调用的参数中,因此ARG_MAX
不适用。- 在
find
上使用--null
,在printf
上使用'%s[=30=]'
(如果您使用find
生成姓名列表,则使用-print0
) 可以防止恶意生成的带有文字换行符的名称能够将任意名称注入到您的流中。想想如果有人运行mkdir -p $'hello/\n/etc/passwd\n.json'
会发生什么——你不希望/etc/passwd
进入你的 tarball。
怎么样:
> cd /Library/WebServer/a/a/e/j
> find . -name '*.json' -maxdepth 1 | xargs tar -czvf j.tar.gz --add-file
它不需要临时文件,也不需要在 shell 中执行 *.json
这会失败。
在 Ubuntu 上查看过 Mac 手头没有。