AGamePlayer Asked: 2020-01-12 08:18:00 +0800 CST2020-01-12 08:18:00 +0800 CST 2020-01-12 08:18:00 +0800 CST 如何使用 bash 将所有 csv 文件的前 200 行保留在目录中? 772 我有大约 50 个非常大的 csv 文件,它们有数千行。 而且我只想为它们中的每一个保留前 200 行 - 如果生成的文件覆盖原始文件,我可以。 我应该使用什么命令来执行此操作? bash files 6 个回答 Voted Best Answer Kusalananda 2020-01-12T08:22:02+08:002020-01-12T08:22:02+08:00 假设当前目录包含所有 CSV 文件并且它们都有一个.csv文件名后缀: for file in ./*.csv; do head -n 200 "$file" >"$file.200" done head这会使用重定向将每个 CSV 文件的前 200 行输出到一个新文件。新文件的名称与旧文件的名称相同,但.200附加在名称的末尾。没有检查新文件名是否已经存在。 如果要更换原件: for file in ./*.csv; do head -n 200 "$file" >"$file.200" && mv "$file.200" "$file" done &&命令末尾的使得head如果mvrunning 出现问题,则不会运行head。 如果您的 CSV 文件分散在当前目录下的子目录中,请使用shopt -s globstar然后将./*.csv循环中的模式替换为./**/*.csv. 这将找到当前目录中或下方的任何 CSV 文件,并对每个文件执行操作。globbing 模式“递归”**匹配到子目录中,但前提globstar是设置了 shell 选项。 对于包含嵌入换行符的数据的 CSV 文件,上述方法将无法正常工作,因为您可能会截断记录。相反,您必须使用一些支持 CSV 的工具来为您完成这项工作。 下面使用 CSVkit,一组用于解析和处理 CSV 文件的命令行工具,以及jq处理 JSON 文件的工具。 CSV 工具包中没有工具可以在特定点截断 CSV 文件,但我们可以将 CSV 文件转换为 JSON 并用于jq仅输出前 200 条记录: for file in ./*.csv; do csvjson -H "$file" | jq -r '.[:200][] | map(values) | @csv' >"$file.200" && mv "$file.200" "$file" done 给定一些 CSV 文件,如下面的简短示例, a,b,c 1,2,3 "hello, world",2 3,4 "hello there","my good man",nice weather for ducks 该csvjson命令将产生 [ { "a": "a", "b": "b", "c": "c" }, { "a": "1", "b": "2", "c": "3" }, { "a": "hello, world", "b": "2 3", "c": "4" }, { "a": "hello\nthere", "b": "my good\nman", "c": "nice weather for ducks" } ] 然后,该jq工具将获取此信息,并针对数组中的每个对象(仅限于前 200 个对象),将值提取为数组并将其格式化为 CSV。 可能可以直接使用csvpyCSVkit 中的另一个工具进行此转换,但由于我的 Python 技能不存在,因此我不会尝试提出解决方案。 Paul_Pedant 2020-01-12T10:11:28+08:002020-01-12T10:11:28+08:00 以前的答案复制数据并覆盖文件。这种技术应该保持相同的 inode,不进行复制,并且运行得更快。对于每个文件: (a) 通过读取前 200 行找出每个文件的长度。 truncate(b) 使用GNU coreutils 或truncate在某些 BSD 系统上找到的将文件截断为该长度: SZ="$( head -n 200 -- "${file}" | wc -c )" truncate -s "${SZ}" -- "${file}" jesse_b 2020-01-12T08:54:33+08:002020-01-12T08:54:33+08:00 将 sed 与 shell globbing 一起使用: sed -ni '1,200p' *.csv 使用 globbing/sed/parallel: printf '%s\n' *.csv | parallel -- sed -ni '1,200p' {} 这将找到当前目录.csv中的所有文件并将它们提供给 GNU 并行,后者将对它们执行 sed 命令以仅保留前 200 行。请注意,这将覆盖到位的文件。 或使用并行头: printf '%s\n' *.csv | parallel -- head -n 200 {} ">" {}.out 这将创建带有.out后缀的新文件。 Stéphane Chazelas 2020-01-13T23:59:22+08:002020-01-13T23:59:22+08:00 使用 ksh93 和符合 POSIX 的head实现(在输出的最后一行之后将光标留在标准输入中),您可以执行以下操作: for file in ~(N)./*; do [ -f "$file" ] || continue # skip non-regular files head -n 200 0<>; "$file" > /dev/null done <>;重定向运算符是标准运算符的变体,它<>在重定向命令返回后截断文件,前提是命令返回成功退出状态。 在这里,我们丢弃head的输出,我们只对它在第 200 行之后离开光标的能力感兴趣。 不幸的是,ksh93 的内置函数head(如果您发出builtin head或者如果/opt/ast/bin在任何带有 in 的head命令的目录之前启用$PATH)在这种情况下不会表现得 POSIXly。它以块的形式读取输入(与大多数其他head实现一样),但在以这种方式调用时不会费心回溯到第 200 行的末尾。为了强制它进行回溯,我们需要执行一个外部命令,这首先破坏了内置函数的目的head: builtin head # enable ksh93's head builtin { head -n 200 && /bin/true; } 0<>; file > /dev/null 另一种不涉及调用外部实用程序的工作方法是在head返回后执行显式 0 偏移搜索: builtin head # enable ksh93's head builtin for file in ~(N)./*; do [ -f "$file" ] || continue # skip non-regular files { head -n 200 && exec <#((CUR)); } 0<>; "$file" > /dev/null done 特别是对于 CSV 输入,并保留前 200 条 CSV 记录(与行相反,因为 CSV 记录可以包含多行(嵌入在"..."引用字段中),您可以使用ksh93'sread -S专门设计用于循环读取 CSV: for file in ~(N)./*.csv; do [ -f "$file" ] || continue # skip non-regular files for ((i=0;i<200;i++)); do IFS=, read -rSA discard done 0<>; "$file" done Ryan 2020-01-13T23:36:15+08:002020-01-13T23:36:15+08:00 我比较新,所以请温柔。如果我提出的解决方案不是最佳的,我将不胜感激建设性的反馈。 我创建了 4 个示例文件,编号为 1 到 4,例如touch {1..4},每个文件包含 10 个示例行,例如第一个文件中的示例行和下一个文件中的第 11 到 20 行,依此类推。 文件 1 Line 1 Line 2 Line 3 Line 4 Line 5 Line 6 Line 7 Line 8 Line 9 Line 10 文件 2 Line 11 Line 12 Line 13 Line 14 Line 15 Line 16 Line 17 Line 18 Line 19 Line 20 以提取前 2 行为例(可以外推到 200),命令head -n 2 {1..4}返回输出; ==> 1 <== Line 1 Line 2 ==> 2 <== Line 11 Line 12 ==> 3 <== Line 21 Line 22 ==> 4 <== Line 31 Line 32 该命令可以使用该命令将输出重定向到另一个文件head -n 2 {1..4} > ExtractedOutput chepner 2020-01-14T08:18:41+08:002020-01-14T08:18:41+08:00 用于ed截断每个文件。 for f in *.csv; do printf '201,$d\nwq\n' | ed "$f" done 如果要保存备份,则可能更容易使用ex。(无论如何,您也可能会考虑ex使用更简单;只需放弃w!%.bak|先跳过备份。) for f in *.csv; do ex -c 'w!%.bak|201,$d|wq' "$f" done
假设当前目录包含所有 CSV 文件并且它们都有一个
.csv
文件名后缀:head
这会使用重定向将每个 CSV 文件的前 200 行输出到一个新文件。新文件的名称与旧文件的名称相同,但.200
附加在名称的末尾。没有检查新文件名是否已经存在。如果要更换原件:
&&
命令末尾的使得head
如果mv
running 出现问题,则不会运行head
。如果您的 CSV 文件分散在当前目录下的子目录中,请使用
shopt -s globstar
然后将./*.csv
循环中的模式替换为./**/*.csv
. 这将找到当前目录中或下方的任何 CSV 文件,并对每个文件执行操作。globbing 模式“递归”**
匹配到子目录中,但前提globstar
是设置了 shell 选项。对于包含嵌入换行符的数据的 CSV 文件,上述方法将无法正常工作,因为您可能会截断记录。相反,您必须使用一些支持 CSV 的工具来为您完成这项工作。
下面使用 CSVkit,一组用于解析和处理 CSV 文件的命令行工具,以及
jq
处理 JSON 文件的工具。CSV 工具包中没有工具可以在特定点截断 CSV 文件,但我们可以将 CSV 文件转换为 JSON 并用于
jq
仅输出前 200 条记录:给定一些 CSV 文件,如下面的简短示例,
该
csvjson
命令将产生然后,该
jq
工具将获取此信息,并针对数组中的每个对象(仅限于前 200 个对象),将值提取为数组并将其格式化为 CSV。可能可以直接使用
csvpy
CSVkit 中的另一个工具进行此转换,但由于我的 Python 技能不存在,因此我不会尝试提出解决方案。以前的答案复制数据并覆盖文件。这种技术应该保持相同的 inode,不进行复制,并且运行得更快。对于每个文件:
(a) 通过读取前 200 行找出每个文件的长度。
truncate
(b) 使用GNU coreutils 或truncate
在某些 BSD 系统上找到的将文件截断为该长度:将 sed 与 shell globbing 一起使用:
使用 globbing/sed/parallel:
这将找到当前目录
.csv
中的所有文件并将它们提供给 GNU 并行,后者将对它们执行 sed 命令以仅保留前 200 行。请注意,这将覆盖到位的文件。或使用并行头:
这将创建带有
.out
后缀的新文件。使用 ksh93 和符合 POSIX 的
head
实现(在输出的最后一行之后将光标留在标准输入中),您可以执行以下操作:<>;
重定向运算符是标准运算符的变体,它<>
在重定向命令返回后截断文件,前提是命令返回成功退出状态。在这里,我们丢弃
head
的输出,我们只对它在第 200 行之后离开光标的能力感兴趣。不幸的是,ksh93 的内置函数
head
(如果您发出builtin head
或者如果/opt/ast/bin
在任何带有 in 的head
命令的目录之前启用$PATH
)在这种情况下不会表现得 POSIXly。它以块的形式读取输入(与大多数其他head
实现一样),但在以这种方式调用时不会费心回溯到第 200 行的末尾。为了强制它进行回溯,我们需要执行一个外部命令,这首先破坏了内置函数的目的head
:另一种不涉及调用外部实用程序的工作方法是在
head
返回后执行显式 0 偏移搜索:特别是对于 CSV 输入,并保留前 200 条 CSV 记录(与行相反,因为 CSV 记录可以包含多行(嵌入在
"..."
引用字段中),您可以使用ksh93
'sread -S
专门设计用于循环读取 CSV:我比较新,所以请温柔。如果我提出的解决方案不是最佳的,我将不胜感激建设性的反馈。
我创建了 4 个示例文件,编号为 1 到 4,例如
touch {1..4}
,每个文件包含 10 个示例行,例如第一个文件中的示例行和下一个文件中的第 11 到 20 行,依此类推。文件 1
文件 2
以提取前 2 行为例(可以外推到 200),命令
head -n 2 {1..4}
返回输出;该命令可以使用该命令将输出重定向到另一个文件
head -n 2 {1..4} > ExtractedOutput
用于
ed
截断每个文件。如果要保存备份,则可能更容易使用
ex
。(无论如何,您也可能会考虑ex
使用更简单;只需放弃w!%.bak|
先跳过备份。)