2009-09-11 40 views
37

鑑於:帶有「特殊」第一行(例如字段名稱)的一個大文本數據文件(例如CSV格式)。如何分割一個文件,並保持每一塊的第一行?

尋求:所述的coreutils split -l命令,但與從原始文件中的標題行出現在每個所得到的片的開始處的附加要求的等效。

我猜一些splithead的混合會做詭計嗎?

+8

這似乎是合理的,有人要補充一點,作爲一個內置的功能'split' ,不是嗎? – 2009-09-11 16:49:09

+1

對於這種成爲內置的最大因素*可能是您通常通過執行「cat a b c>重建」來重建分割文件。文件中的多餘行意味着正常重建方法不會重現原始文件。 – 2009-09-11 18:23:29

+2

這就是即將推出的(* not *)「'unsplit --remove-header'」工具!但嚴重的是,'split',如果它有一個「repeat-header」選項,仍然應該默認爲它的當前行爲。如果你真的想要它,你只能使用標題。 – 2009-09-11 19:00:31

回答

32

robhruska的腳本清理了一下:

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 

我刪除了在他們不必要的地方wccutlsecho。我改變了一些文件名,使它們更有意義。我把它分解成多行,只是爲了方便閱讀。

如果你想變得很花哨,你可以使用mktemptempfile創建一個臨時文件名,而不是使用硬編碼的文件名。

編輯

使用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_ 

當指定--filtersplit運行命令(功能在這種情況下,必須導出)爲每個輸出文件並設置v在命令的環境中,可變的FILE到文件名。

過濾器腳本或函數可以對輸出內容甚至文件名進行任何操作。後者的一個例子可能是輸出到變量目錄中的固定文件名:例如> "$FILE/data.dat"

+0

這肯定會奏效。我只是希望有一些像''for $ part in(split -l 1000 myfile)這樣的簡單單線程; cat <(head -n1 myfile)$ part> myfile。$ part; done' – Arkady 2009-09-11 19:09:00

+0

這不能工作,因爲必須的'split'不會在'stdout'上輸出。 – 2009-09-11 19:19:47

+0

'split * *可以*輸出文件的*名稱*到標準輸出,但(只要我們正在討論什麼'split' *應該*做:-) – Arkady 2009-09-11 19:23:08

4

我是新手,當談到巴什福,但我能夠調製這個雙命令怪物。我確信有更優雅的解決方案。

$> 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.txt,你不使用prefix參數split,和你在一個目錄中工作,不具有與split的默認啓動的任何其他文件輸出格式爲xa*。另外,用你想要的分割線尺寸替換'4'。

1

我從來不確定直接從其他人的網站複製腳本的規則,但Geekology有一個很好的腳本來執行您想要的操作,並有幾條註釋確認它可以正常工作。一定要做tail-n+2正如註釋底部附近的註釋。

2

這是一個更穩健的版本丹尼斯威廉姆森的腳本。該腳本創建了大量臨時文件,如果運行不完整,它們仍然處於左右躺臥的狀態將會是一種恥辱。因此,我們添加信號陷阱(請參閱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(因爲有些人已經建議了),所以繼續,從trap中刪除'tmp_file'。看到信號手冊頁以獲取更多的信號。

8

You可以使用[毫克] 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是每個片的行數 它不需要臨時文件,可以在一行內把

+0

Upvoting教給我一些新東西,但是如果我要編寫一個小腳本,我不妨在Perl或Python中執行它:-) – Arkady 2009-09-13 00:50:23

5

你可以使用新的。 - GNU coreutils split中的過濾功能split = 8.13(2011):

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

我喜歡單線版本。只是爲了讓它對bash更通用,我做了:'tail -n +2 FILE.in | split -d --lines 50 - --filter ='bash -c「{head -n1 $ {FILE%。*}; cat;}> $ FILE」'FILE.in.x' – KullDox 2017-05-04 21:30:45

1

我很喜歡馬可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 
+0

我喜歡這個解決方案,但是它僅限於兩個拆分文件 – Bas 2016-05-09 09:47:16

+0

如果你喜歡它,那麼它就有upvote功能;)它可以很容易地調整到更多的文件,但是它不像拆分那樣靈活-l – DreamFlasher 2016-05-09 10:06:00

+0

「one liner」... pshh – Pandem1c 2017-01-19 01:12:36

1

我真的很喜歡Rob和丹尼斯的版本,以至於我想改善它們。

這裏是我的版本:

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. 分成100,000行文件而不是4
  4. 分割的文件名會用下劃線和數字輸入附加文件名(最多99999 - 從「-d -a 5」分論點)
  5. 使用mktemp的安全地處理臨時文件
  6. 使用單一head | cat線而不是兩行
0

使用GNU並行:

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

如果你需要在每個部件的運行命令,然後GNU並行可以幫助做到這一點,太:

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內核分成兩部分(例如,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 
相關問題