非常に大きなcsvファイルが約50個あり、数千行あります。
そして、私はそれらのそれぞれの最初の200行を保持したいだけです-生成されたファイルが元のファイルを上書きする場合は大丈夫です。
これを行うにはどのコマンドを使用すればよいですか?
現在のディレクトリにすべてのCSVファイルが含まれ、それらすべてに.csv
ファイル名サフィックスがあると仮定します。
for file in ./*.csv; do
head -n 200 "$file" >"$file.200"
done
これにより、各CSVファイルの最初の200行が、head
とリダイレクトを使用して新しいファイルに出力されます。新しいファイルの名前は古いファイルと同じですが、名前の最後に.200
が追加されています。新しいファイル名が既に存在するかどうかを確認するチェックはありません。
オリジナルを置き換える場合:
for file in ./*.csv; do
head -n 200 "$file" >"$file.200" &&
mv "$file.200" "$file"
done
head
コマンドの最後の&&
は、mv
の実行に問題があった場合にhead
が実行されないようにします。
CSVファイルが現在のディレクトリの下のサブディレクトリに分散している場合は、shopt -s globstar
を使用し、ループ内のパターン./*.csv
を./**/*.csv
に置き換えます。これにより、現在のディレクトリ以下のCSVファイルが検索され、それぞれに対して操作が実行されます。 **
グロビングパターンは、サブディレクトリに「再帰的に」一致しますが、globstar
Shellオプションが設定されている場合のみです。
改行が埋め込まれたデータを含むCSVファイルの場合、レコードが切り捨てられる可能性があるため、上記は正しく機能しません。代わりに、CSV対応のツールを使用してジョブを実行する必要があります。
以下では、CSVファイルを解析および一般的に操作するためのコマンドラインツールのセットであるCSVkitと、JSONファイルを操作するためのツールであるjq
を使用します。
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の別のツールであるcsvpy
を使用してこの変換を直接実行することはおそらく可能ですが、私のPythonスキルが存在しないため、解決策を思い付くことはしませんそれはそれを行います。
以前の回答では、データをコピーしてファイルを上書きしていました。この手法では、同じiノードを保持し、コピーを行わず、全体的に高速で実行する必要があります。各ファイルについて:
(a)最初の200行を読み取って、各ファイルの長さを見つけます。
(b)GNU coreutilsからtruncate
を使用するか、一部のBSDシステムで見つかったtruncate
を使用して、ファイルをその長さに切り詰めます。
SZ="$( head -n 200 -- "${file}" | wc -c )"
truncate -s "${SZ}" -- "${file}"
シェルグロビングでsedを使用する:
sed -ni '1,200p' *.csv
Globbing/sed/parallelの使用:
printf '%s\n' *.csv | parallel -- sed -ni '1,200p' {}
これはすべてを検索します.csv
ファイルを現在のディレクトリに入れ、GNU parallelにフィードします。これにより、sedコマンドが実行され、最初の200行のみが保持されます。これにより、所定の場所にファイル。
またはパラレルでヘッドを使用する:
printf '%s\n' *.csv | parallel -- head -n 200 {} ">" {}.out
これにより、.out
サフィックス。
Ksh93とPOSIX準拠のhead
実装(カーソルが出力する最後の行の直後のstdin内にカーソルを置くもの)では、次のことができます。
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
が$PATH
内のhead
コマンドを含むディレクトリの前にある場合に有効)は、このインスタンスではPOSIXlyで動作しません。 (他のほとんどのhead
実装と同様に)入力をチャンクで読み取りますが、seekingを呼び出して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レコードに複数の行が含まれる可能性があるため("..."
引用フィールドに埋め込まれるため)、ループでCSVを読み取るように特別に設計されたksh93
のread -S
を使用できます。
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
私は比較的新しいので、優しくしてください。私が提案しているソリューションが最適でない場合は、建設的なフィードバックをいただければ幸いです。
1から4までの番号が付けられた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
コマンドを使用して、出力を別のファイルにリダイレクトできます。
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