bashを使用してディレクトリ内のすべてのcsvファイルの最初の200行を保持する方法は?

bashを使用してディレクトリ内のすべてのcsvファイルの最初の200行を保持する方法は?

何千もの行を含む約50の非常に大きなcsvファイルがあります。

各ファイルの最初の200行だけを維持したいと思います。生成されたファイルが元のファイルを上書きしても問題はありません。

これを行うにはどのコマンドを使用する必要がありますか?

答え1

現在、ディレクトリにすべての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ことを意味します。mvhead

CSVファイルが現在のディレクトリ内のサブディレクトリに散在している場合は、shopt -s globstarループのパターンをに置き換えます。これにより、現在のディレクトリ内または下にあるすべてのCSVファイルが見つかり、各ファイルに対して操作が実行されます。ワイルドカードパターンは「再帰的」サブディレクトリと一致しますが、シェルオプションが設定されている場合にのみ適用されます。./*.csv./**/*.csv**globstar


改行を含むデータを含むCSVファイルの場合、レコードが切り捨てられる可能性があるため、上記の方法は正しく機能しません。代わりに、一部のCSV認識ツールを使用して作業を実行する必要があります。

以下は、CSVファイルを解析して通常処理するための一連のコマンドラインツールであるCSVkitとjqJSONファイルを処理するためのツールを使用しています。

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にフォーマットします。

CSVkitの他のツールを使用してこの変換を直接実行することは可能かもしれませんが、csvpyPythonテクノロジがないため、これを達成するためのソリューションを見つけることはありません。

答え2

以前の答えはデータをコピーしてファイルを上書きします。このテクニックは同じinodeを維持してコピーせず、はるかに速く実行する必要があります。各ファイルに対して、次の操作を行います。

(a)最初の200行を読み、各ファイルの長さを取得します。

truncatetruncate(b)GNU coreutilsまたは一部のBSDシステムのコマンドを使用してファイルをこの長さに切り捨てます。

SZ="$( head -n 200 -- "${file}" | wc -c )"
truncate -s "${SZ}" -- "${file}"

答え3

シェルワイルドカードでsedを使用してください。

sed -ni '1,200p' *.csv

globbing/sed/パラレルを使用してください。

printf '%s\n' *.csv | parallel -- sed -ni '1,200p' {}

.csvそのディレクトリ内のすべてのファイルを探します。現在のディレクトリGNU Parallelに供給すると、最初の200行だけを維持するようにsedされます。これにより、現在の場所のファイルが上書きされます。

または、パラレルヘッダを使用します。

printf '%s\n' *.csv | parallel -- head -n 200 {} ">" {}.out

.outこれにより、サフィックスを含む新しいファイルが作成されます。

答え4

私は比較的初心者で、優しくしてください。私が提案したソリューションが最適でない場合は、建設的なフィードバックを送っていただきありがとうございます。

たとえば、1から4まで番号が付けられた4つのサンプルファイルを作成しました。touch {1..4}各ファイルには、最初のファイルの11行から20行までの10行のサンプル行が含まれています。

ファイル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

関連情報