web-dev-qa-db-ja.com

ファイルを分割し、各部分の最初の行を保持する方法は?

指定:「特別な」最初の行(フィールド名など)を持つ1つの大きなテキストデータファイル(CSV形式など)。

Wanted: coreutils split -lコマンド。ただし、元のファイルのヘッダー行が結果の各部分の先頭に表示されるという追加の要件があります。

splitheadを混ぜるとうまくいくと思いますか?

52
Arkady

これはrobhruska'sスクリプトを少しクリーンアップしたものです:

tail -n +2 file.txt | split -l 4 - split_
for file in split_*
do
    head -n 1 file.txt > tmp_file
    cat "$file" >> tmp_file
    mv -f tmp_file "$file"
done

wccutls、およびechoが不要な場所を削除しました。ファイル名の一部を変更して、もう少し意味のあるものにしました。読みやすくするために、複数の行に分けました。

おしゃれにしたい場合は、ハードコードされたファイルを使用する代わりに、mktempまたはtempfileを使用して一時ファイル名を作成できます。

編集

GNU splitを使用すると、これを行うことができます。

split_filter () { { head -n 1 file.txt; cat; } > "$FILE"; }; export -f split_filter; tail -n +2 file.txt | split --lines=4 --filter=split_filter - split_

読みやすくするために分割:

split_filter () { { head -n 1 file.txt; cat; } > "$FILE"; }
export -f split_filter
tail -n +2 file.txt | split --lines=4 --filter=split_filter - split_

いつ --filterを指定すると、splitは各出力ファイルに対してコマンド(この場合はエクスポートする必要がある関数)を実行し、コマンドの環境内の変数FILEをファイル名に設定します。

フィルタースクリプトまたは関数は、出力コンテンツまたはファイル名に必要な操作を行うことができます。後者の例は、変数ディレクトリの固定ファイル名に出力することです:> "$FILE/data.dat" 例えば。

46

GNU coreutils split> = 8.13(2011):で新しい--filter機能を使用できます。

tail -n +2 FILE.in |
split -l 50 - --filter='sh -c "{ head -n1 FILE.in; cat; } > $FILE"'
12
pixelbeat

[mg] awkを使用できます。

awk 'NR==1{
        header=$0; 
        count=1; 
        print header > "x_" count; 
        next 
     } 

     !( (NR-1) % 100){
        count++; 
        print header > "x_" count;
     } 
     {
        print $0 > "x_" count
     }' file

100は、各スライスの行数です。一時ファイルを必要とせず、1行に配置できます。

10
marco

私はBash-fuに関しては初心者ですが、この2つのコマンドの極悪非道を作り上げることができました。もっとエレガントなソリューションがあるはずです。

$> tail -n +2 file.txt | split -l 4
$> for file in `ls xa*`; do echo "`head -1 file.txt`" > tmp; cat $file >> tmp; mv -f tmp $file; done

これは、入力ファイルがfile.txtprefixsplit引数を使用しておらず、splitのデフォルトで始まる他のファイルがないディレクトリで作業しているxa* 出力フォーマット。また、「4」を希望の分割線サイズに置き換えます。

4
Rob Hruska

これにより、大きなcsvが999行の断片に分割され、ヘッダーが各行の上部に表示されます

cat bigFile.csv | parallel --header : --pipe -N999 'cat >file_{#}.csv'

Ole Tangeの回答に基づきます。 (オレの答え:パイプパートでは行カウントを使用できません)

3
Tim Richardson

これは、Denis Williamsonのスクリプトのより堅牢なバージョンです。スクリプトは多くの一時ファイルを作成しますが、実行が不完全だった場合、それらが横になっていると残念です。それでは、シグナルトラップを追加しましょう( http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html を参照してから、 http://tldp.org/ LDP/abs/html/debugging.html )そして一時ファイルを削除します;とにかくこれはベストプラクティスです。

trap 'rm split_* tmp_file ; exit 13' SIGINT SIGTERM SIGQUIT 
tail -n +2 file.txt | split -l 4 - split_
for file in split_*
do
    head -n 1 file.txt > tmp_file
    cat $file >> tmp_file
    mv -f tmp_file $file
done

'13'を必要な戻りコードに置き換えます。ああ、とにかくmktempを使用する必要があります(他の人が既に示唆しているように)、トラップラインのrmから 'tmp_file'を削除します。キャッチするシグナルについては、シグナルのマニュアルページを参照してください。

2
Sam Bisbee

ワンライナーに関する@Arkadyのコメントに触発されました。

  • 定型文を減らすためのMYFILE変数
  • splitはファイル名を表示しませんが、_--additional-suffix_オプションを使用すると、期待する内容を簡単に制御できます
  • _rm $part_を介した中間ファイルの削除(同じサフィックスを持つファイルがないと想定)

MYFILE=mycsv.csv && for part in $(split -n4 --additional-suffix=foo $MYFILE; ls *foo); do cat <(head -n1 $MYFILE) $part > $MYFILE.$part; rm $part; done

証拠:

_-rw-rw-r--  1 ec2-user ec2-user  32040108 Jun  1 23:18 mycsv.csv.xaafoo
-rw-rw-r--  1 ec2-user ec2-user  32040108 Jun  1 23:18 mycsv.csv.xabfoo
-rw-rw-r--  1 ec2-user ec2-user  32040108 Jun  1 23:18 mycsv.csv.xacfoo
-rw-rw-r--  1 ec2-user ec2-user  32040110 Jun  1 23:18 mycsv.csv.xadfoo
_

そしてもちろん_head -2 *foo_はヘッダーが追加されたことを確認します。

1
user1043620

スクリプトを他の人のサイトから直接コピーする規則についてはよくわかりませんが、 Geekology には、動作することを確認するコメントをいくつか付けて、あなたがやりたいことを行うための素晴らしいスクリプトがあります。必ずtail-n+2下部のコメントに記載されています。

1
Mark Rushakoff

マルコのawkバージョンが好きで、これから単純化されたワンライナーを採用しました。そこでは、分割分数を必要なだけ簡単に指定できます。

awk 'NR==1{print $0 > FILENAME ".split1";  print $0 > FILENAME ".split2";} NR>1{if (NR % 10 > 5) print $0 >> FILENAME ".split1"; else print $0 >> FILENAME ".split2"}' file
1
DreamFlasher

ロブとデニスのバージョンがとても好きだったので、改善したいと思いました。

これが私のバージョンです。

in_file=$1
awk '{if (NR!=1) {print}}' $in_file | split -d -a 5 -l 100000 - $in_file"_" # Get all lines except the first, split into 100,000 line chunks
for file in $in_file"_"*
do
    tmp_file=$(mktemp $in_file.XXXXXX) # Create a safer temp file
    head -n 1 $in_file | cat - $file > $tmp_file # Get header from main file, cat that header with split file contents to temp file
    mv -f $tmp_file $file # Overwrite non-header containing file with header-containing file
done

違い:

  1. in_fileは、メンテナンスヘッダーを分割するファイル引数です。
  2. awkはパフォーマンスが優れているため、tailの代わりにawkを使用します。
  3. 4つではなく100,000行のファイルに分割
  4. 分割ファイル名は、入力ファイル名にアンダースコアと数字が追加されます(最大99999-「-d -a 5」分割引数から)
  5. Mktempを使用して一時ファイルを安全に処理する
  6. 2行ではなく、単一のhead | cat行を使用します
1
Garren S

以下は、csvヘッダーを保持するために使用できる4ライナーです(使用:head、split、find、grep、xargs、およびsed)

 
 csvheader = `head -1 bigfile.csv` 
 split -d -l10000 bigfile.csv smallfile _ 
 find。| grep smallfile_ | xargs sed -i "1s/^/$ csvheader\n /"
 sed -i '1d' smallfile_00 
 

説明:

  • csvheaderという名前の変数にヘッダーをキャプチャします
  • Bigfileをいくつかの小さなファイルに分割します(接頭辞smallfile_を使用)
  • Findすべての小さなファイルとxargsおよびsed -iを使用してcsvheaderを最初の行に挿入します。変数を使用するには、「二重引用符」内でsedを使用する必要があることに注意してください。
  • Smallfile_00という名前の最初のファイルには、行1と2に冗長なヘッダーがあります(元のデータとステップ3のsedヘッダー挿入から)。 sed -i '1d'コマンドで冗長ヘッダーを削除できます。
1
Thyag

GNU Parallel:

parallel -a bigfile.csv --header : --pipepart 'cat > {#}'

各部分でコマンドを実行する必要がある場合は、GNU Parallelもこれを行うことができます。

parallel -a bigfile.csv --header : --pipepart my_program_reading_from_stdin
parallel -a bigfile.csv --header : --pipepart --fifo my_program_reading_from_fifo {}
parallel -a bigfile.csv --header : --pipepart --cat my_program_reading_from_a_file {}

CPUコアごとに2つの部分に分割する場合(例:24コア= 48等サイズの部分):

parallel --block -2 -a bigfile.csv --header : --pipepart my_program_reading_from_stdin

10 MBブロックに分割する場合:

parallel --block 10M -a bigfile.csv --header : --pipepart my_program_reading_from_stdin
1
Ole Tange