Ketika datang untuk membagi file teks menjadi beberapa file di Linux, kebanyakan orang menggunakan perintah split. Tidak ada yang salah dengan perintah split kecuali bergantung pada ukuran byte atau ukuran baris untuk memisahkan file.
Ini tidak nyaman dalam situasi di mana Anda perlu membagi file berdasarkan kontennya, bukan ukurannya. Biarkan saya memberi Anda sebuah contoh.
Saya mengelola tweet terjadwal saya menggunakan file YAML. File tweet biasa berisi beberapa tweet, dipisahkan oleh empat tanda hubung:
----
event:
repeat: { days: 180 }
status: |
I think I use the `sed` command daily. And you?
https://www.yesik.it/EP07
#Shell #Linux #Sed #YesIKnowIT
----
status: |
Print the first column of a space-separated data file:
awk '{print $1}' data.txt # Print out just the first column
For some unknown reason, I find that easier to remember than:
cut -f1 data.txt
#Linux #AWK #Cut
----
status: |
For the #shell #beginners :
[...]
Saat mengimpornya ke sistem saya, saya perlu menulis setiap tweet ke filenya sendiri. Saya melakukannya untuk menghindari pendaftaran tweet duplikat.
Tetapi bagaimana cara membagi file menjadi beberapa bagian berdasarkan isinya? Yah, mungkin Anda bisa mendapatkan sesuatu yang meyakinkan menggunakan perintah awk:
sh$ awk < tweets.yaml '
> /----/ { OUTPUT="tweet." (N++) ".yaml" }
> { print > OUTPUT }
> '
Namun, meskipun relatif sederhana, solusi seperti itu tidak terlalu kuat:misalnya, saya tidak menutup berbagai file output dengan benar, jadi ini mungkin mencapai batas file terbuka. Atau bagaimana jika saya lupa pemisah sebelum tweet pertama file? Tentu saja, semua itu dapat ditangani dan diperbaiki dalam skrip AWK, dengan mengorbankan membuatnya lebih kompleks. Tapi mengapa repot-repot dengan itu ketika kita memiliki csplit
alat untuk menyelesaikan tugas itu?
Menggunakan csplit untuk membagi file di Linux
csplit
alat adalah sepupu dari split
alat yang dapat digunakan untuk membagi file menjadi ukuran tetap potongan. Tapi csplit
akan mengidentifikasi batas potongan berdasarkan konten file, daripada menggunakan jumlah byte.
Dalam tutorial ini, saya akan mendemonstrasikan penggunaan perintah csplit dan juga akan menjelaskan output dari perintah ini.
Jadi, misalnya, jika saya ingin membagi file tweet saya berdasarkan ----
pembatas, saya bisa menulis:
sh$ csplit tweets.yaml /----/
0
10846
Anda mungkin sudah menebak csplit
alat menggunakan regex yang disediakan pada baris perintah untuk mengidentifikasi pemisah. Dan apa itu 0
dan 10983
hasil yang ditampilkan pada output standar? Ya, ukurannya dalam byte dari setiap potongan data yang dibuat.
sh$ ls -l xx0*
-rw-r--r-- 1 sylvain sylvain 0 Jun 6 11:30 xx00
-rw-r--r-- 1 sylvain sylvain 10846 Jun 6 11:30 xx01
Tunggu sebentar! Dimana xx00
itu dan xx01
nama file berasal dari? Dan mengapa csplit
bagi file menjadi hanya dua bagian ? Dan mengapa potongan data pertama memiliki panjang nol byte ?
Jawaban untuk pertanyaan pertama sederhana:xxNN
(atau lebih formal xx%02d
) adalah format nama file default yang digunakan oleh csplit
. Tetapi Anda dapat mengubahnya menggunakan --suffix-format
dan --prefix
pilihan. Misalnya, saya dapat mengubah format menjadi sesuatu yang lebih bermakna untuk kebutuhan saya:
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> /----/
0
10846
sh$ ls -l tweet.*
-rw-r--r-- 1 sylvain sylvain 0 Jun 6 11:30 tweet.000.yaml
-rw-r--r-- 1 sylvain sylvain 10846 Jun 6 11:30 tweet.001.yaml
Awalan adalah string biasa, tetapi akhiran adalah string format seperti yang digunakan oleh pustaka C standar printf
fungsi. Sebagian besar karakter format akan digunakan kata demi kata, kecuali untuk spesifikasi konversi yang diperkenalkan oleh tanda persen (%
) dan yang diakhiri dengan penentu konversi (di sini, d
). Di antaranya, format juga dapat berisi berbagai tanda dan opsi. Dalam contoh saya, %03d
spesifikasi konversi berarti:
- tampilkan nomor potongan sebagai bilangan bulat desimal (
d
), - dalam bidang lebar tiga karakter (
3
), - akhirnya diisi di sebelah kiri dengan angka nol (
0
).
Tapi itu tidak membahas interogasi lain yang saya lakukan di atas:mengapa kita memiliki hanya dua potongan, salah satunya berisi nol byte? Mungkin Anda sudah menemukan jawaban untuk pertanyaan terakhir itu sendiri:file data saya dimulai dengan ----
pada baris pertama. Jadi, csplit
menganggapnya sebagai pembatas, dan karena tidak ada data sebelum baris itu, itu membuat potongan pertama yang kosong. Kami dapat menonaktifkan pembuatan file dengan panjang nol byte menggunakan --elide-empty-files
pilihan:
sh$ rm tweet.*
rm: cannot remove 'tweet.*': No such file or directory
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> /----/
10846
sh$ ls -l tweet.*
-rw-r--r-- 1 sylvain sylvain 10846 Jun 6 11:30 tweet.000.yaml
Ok:tidak ada lagi file kosong. Tetapi dalam arti tertentu, hasilnya adalah yang terburuk sekarang, karena csplit
bagi file menjadi satu bingkah. Kita hampir tidak bisa menyebut itu "membelah" file, bukan?
Penjelasan untuk hasil yang mengejutkan itu adalah csplit
apakah tidak sama sekali menganggap setiap chuck harus dibagi berdasarkan sama pemisah. Sebenarnya, csplit
mengharuskan Anda untuk menyediakan setiap pemisah yang digunakan. Meskipun beberapa kali sama:
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> /----/ /----/ /----/
170
250
10426
Saya telah menempatkan tiga pemisah (identik) pada baris perintah. Jadi, csplit
mengidentifikasi ujung potongan pertama berdasarkan pemisah pertama. Ini mengarah ke potongan panjang nol byte yang dihilangkan. Potongan kedua dibatasi oleh baris berikutnya yang cocok dengan /----/
. Mengarah ke potongan 170 byte. Akhirnya, potongan panjang 250 byte ketiga diidentifikasi berdasarkan pemisah ketiga. Data yang tersisa, 10426 byte, dimasukkan ke dalam potongan terakhir.
sh$ ls -l tweet.???.yaml
-rw-r--r-- 1 sylvain sylvain 170 Jun 6 11:30 tweet.000.yaml
-rw-r--r-- 1 sylvain sylvain 250 Jun 6 11:30 tweet.001.yaml
-rw-r--r-- 1 sylvain sylvain 10426 Jun 6 11:30 tweet.002.yaml
Jelas, tidak praktis jika kita harus menyediakan banyak pemisah di baris perintah karena ada potongan di file data. Apalagi angka pastinya itu biasanya tidak diketahui sebelumnya. Untungnya, csplit
memiliki arti pola khusus “ulangi pola sebelumnya sebanyak mungkin.” Meskipun sintaksnya mengingatkan quantifier bintang dalam ekspresi reguler, ini lebih dekat dengan konsep Kleene plus karena digunakan untuk mengulang pemisah yang sudah pernah dicocokkan sekali:
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> /----/ '{*}'
170
250
190
208
140
[...]
247
285
194
214
185
131
316
221
Dan kali ini, akhirnya, saya membagi koleksi tweet saya menjadi beberapa bagian. Namun, apakah csplip
memiliki beberapa pola "khusus" yang bagus seperti itu? Yah, saya tidak tahu apakah kita bisa menyebutnya “istimewa”, tapi yang pasti, csplit
lebih memahami pola.
Pola csplit lainnya
Kami baru saja melihat di bagian sebelumnya bagaimana menggunakan quantifier '{*}' untuk pengulangan tak terikat. Namun, dengan mengganti bintang dengan angka, Anda dapat meminta jumlah pengulangan yang tepat:
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> /----/ '{6}'
170
250
190
208
140
216
9672
Itu mengarah ke kasus sudut yang menarik. Apa yang akan ditambahkan jika jumlah pengulangan melebihi jumlah pembatas sebenarnya dalam file data? Nah, mari kita lihat pada contoh:
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> /----/ '{999}'
csplit: ‘/----/’: match not found on repetition 62
170
250
190
208
[...]
91
247
285
194
214
185
131
316
221
sh$ ls tweet.*
ls: cannot access 'tweet.*': No such file or directory
Menariknya, tidak hanya csplit
melaporkan kesalahan, tetapi juga menghapus semua file chunk yang dibuat selama proses. Berikan perhatian khusus pada kata-kata saya:itu dihapus mereka. Itu berarti file telah dibuat, lalu, ketika csplit
mengalami kesalahan, itu menghapusnya. Dengan kata lain, jika Anda sudah memiliki file yang namanya terlihat seperti file chunk, itu akan dihapus:
sh$ touch tweet.002.yaml
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> /----/ '{999}'
csplit: ‘/----/’: match not found on repetition 62
170
250
190
[...]
87
91
247
285
194
214
185
131
316
221
sh$ ls tweet.*
ls: cannot access 'tweet.*': No such file or directory
Pada contoh di atas, tweet.002.yaml
file yang kami buat secara manual ditimpa, lalu dihapus oleh csplit
.
Anda dapat mengubah perilaku tersebut menggunakan --keep-files
pilihan. Seperti namanya, itu tidak akan menghapus potongan csplit yang dibuat setelah mengalami kesalahan:
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> --keep-files \
> /----/ '{999}'
csplit: ‘/----/’: match not found on repetition 62
170
250
190
[...]
316
221
sh$ ls tweet.*
tweet.000.yaml
tweet.001.yaml
tweet.002.yaml
tweet.003.yaml
[...]
tweet.058.yaml
tweet.059.yaml
tweet.060.yaml
tweet.061.yaml
Perhatikan dalam kasus itu, dan meskipun ada kesalahan, csplit
tidak membuang data apa pun:
sh$ diff -s tweets.yaml <(cat tweet.*)
Files tweets.yaml and /dev/fd/63 are identical
Tetapi bagaimana jika ada beberapa data dalam file yang ingin saya buang? Nah, csplit
memiliki beberapa dukungan terbatas untuk itu menggunakan %regex%
pola.
Melewati data di csplit
Saat menggunakan tanda persen (%
) sebagai pembatas regex alih-alih garis miring (/
), csplit
akan melewati data hingga (tetapi tidak termasuk) baris pertama yang cocok dengan ekspresi reguler. Ini mungkin berguna untuk mengabaikan beberapa catatan, terutama di awal atau akhir file input:
sh$ # Keep only the first two tweets
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> --keep-files \
> /----/ '{2}' %----% '{*}'
170
250
sh$ head tweet.00[012].yaml
==> tweet.000.yaml <==
----
event:
repeat: { days: 180 }
status: |
I think I use the `sed` command daily. And you?
https://www.yesik.it/EP07
#Shell #Linux #Sed #YesIKnowIT
==> tweet.001.yaml <==
----
status: |
Print the first column of a space-separated data file:
awk '{print $1}' data.txt # Print out just the first column
For some unknown reason, I find that easier to remember than:
cut -f1 data.txt
#Linux #AWK #Cut
sh$ # Skip the first two tweets
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> --keep-files \
> %----% '{2}' /----/ '{2}'
190
208
140
9888
sh$ head tweet.00[012].yaml
==> tweet.000.yaml <==
----
status: |
For the #shell #beginners :
« #GlobPatterns : how to move hundreds of files in not time [1/3] »
https://youtu.be/TvW8DiEmTcQ
#Unix #Linux
#YesIKnowIT
==> tweet.001.yaml <==
----
status: |
Want to know the oldest file in your disk?
find / -type f -printf '%TFT%.8TT %p\n' | sort | less
(should work on any Single UNIX Specification compliant system)
#UNIX #Linux
==> tweet.002.yaml <==
----
status: |
When using the find command, use `-iname` instead of `-name` for case-insensitive search
#Unix #Linux #Shell #Find
sh$ # Keep only the third and fourth tweets
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> --keep-files \
> %----% '{2}' /----/ '{2}' %----% '{*}'
190
208
140
sh$ head tweet.00[012].yaml
==> tweet.000.yaml <==
----
status: |
For the #shell #beginners :
« #GlobPatterns : how to move hundreds of files in not time [1/3] »
https://youtu.be/TvW8DiEmTcQ
#Unix #Linux
#YesIKnowIT
==> tweet.001.yaml <==
----
status: |
Want to know the oldest file in your disk?
find / -type f -printf '%TFT%.8TT %p\n' | sort | less
(should work on any Single UNIX Specification compliant system)
#UNIX #Linux
==> tweet.002.yaml <==
----
status: |
When using the find command, use `-iname` instead of `-name` for case-insensitive search
#Unix #Linux #Shell #Find
Menggunakan offset saat memisahkan file dengan csplit
Saat menggunakan ekspresi reguler (baik /…/
atau %…%
) Anda dapat menentukan positif (+N
) atau negatif (-N
) offset di akhir pola jadi csplit
akan membagi file N baris setelah atau sebelum baris yang cocok. Ingat, dalam semua kasus, pola menentukan akhir dari potongan:
sh$ csplit tweets.yaml \
> --prefix='tweet.' --suffix-format='%03d.yaml' \
> --elide-empty-files \
> --keep-files \
> %----%+1 '{2}' /----/+1 '{2}' %----% '{*}'
190
208
140
sh$ head tweet.00[012].yaml
==> tweet.000.yaml <==
status: |
For the #shell #beginners :
« #GlobPatterns : how to move hundreds of files in not time [1/3] »
https://youtu.be/TvW8DiEmTcQ
#Unix #Linux
#YesIKnowIT
----
==> tweet.001.yaml <==
status: |
Want to know the oldest file in your disk?
find / -type f -printf '%TFT%.8TT %p\n' | sort | less
(should work on any Single UNIX Specification compliant system)
#UNIX #Linux
----
==> tweet.002.yaml <==
status: |
When using the find command, use `-iname` instead of `-name` for case-insensitive search
#Unix #Linux #Shell #Find
----
Pisahkan menurut nomor baris
Kita telah melihat bagaimana kita dapat menggunakan ekspresi reguler untuk membagi file. Dalam hal ini, csplit
akan membagi file pada baris pertama cocok ekspresi reguler itu. Tetapi Anda juga dapat mengidentifikasi garis pemisah dengan nomor garisnya seperti yang akan kita lihat sekarang.
Sebelum beralih ke YAML, saya biasa menyimpan tweet terjadwal saya di flat file.
Dalam file itu, tweet dibuat dua baris. Satu berisi pengulangan opsional, dan yang kedua berisi teks tweet, dengan baris baru diganti dengan \n. Sekali lagi file sampel itu tersedia secara online.
Dengan format "ukuran tetap" itu juga dapat menggunakan csplit
untuk menempatkan setiap tweet individu ke dalam filenya sendiri:
sh$ csplit tweets.txt \
> --prefix='tweet.' --suffix-format='%03d.txt' \
> --elide-empty-files \
> --keep-files \
> 2 '{*}'
csplit: ‘2’: line number out of range on repetition 62
1
123
222
161
182
119
184
81
148
128
142
101
107
[...]
sh$ diff -s tweets.txt <(cat tweet.*.txt)
Files tweets.txt and /dev/fd/63 are identical
sh$ head tweet.00[012].txt
==> tweet.000.txt <==
==> tweet.001.txt <==
{ days:180 }
I think I use the `sed` command daily. And you?\n\nhttps://www.yesik.it/EP07\n#Shell #Linux #Sed\n#YesIKnowIT
==> tweet.002.txt <==
{}
Print the first column of a space-separated data file:\nawk '{print $1}' data.txt # Print out just the first column\n\nFor some unknown reason, I find that easier to remember than:\ncut -f1 data.txt\n\n#Linux #AWK #Cut
Contoh di atas tampaknya mudah dipahami, tetapi ada dua jebakan di sini. Pertama, 2
diberikan sebagai argumen untuk csplit
adalah garis angka , bukan baris hitung . Namun, ketika menggunakan pengulangan seperti yang saya lakukan, setelah pertandingan pertama, csplit
akan menggunakan nomor itu sebagai baris hitung . Jika masih kurang jelas, saya coba bandingkan output dari ketiga perintah berikut ini:
sh$ csplit tweets.txt --keep-files 2 2 2 2 2
csplit: warning: line number ‘2’ is the same as preceding line number
csplit: warning: line number ‘2’ is the same as preceding line number
csplit: warning: line number ‘2’ is the same as preceding line number
csplit: warning: line number ‘2’ is the same as preceding line number
1
0
0
0
0
9030
sh$ csplit tweets.txt --keep-files 2 4 6 8 10
1
123
222
161
182
8342
sh$ csplit tweets.txt --keep-files 2 '{4}'
1
123
222
161
182
8342
Saya menyebutkan perangkap kedua, agak terkait dengan yang pertama. Mungkin Anda memperhatikan baris kosong di bagian paling atas tweets.txt
mengajukan? Ini mengarah ke tweet.000.txt
chunk yang hanya berisi karakter baris baru. Sayangnya, itu diperlukan dalam contoh itu karena pengulangan:ingat saya ingin dua baris potongan. Jadi 2
adalah wajib sebelum pengulangan. Tapi itu juga berarti pertama potongan akan pecah, tetapi tidak termasuk , baris dua. Dengan kata lain, potongan pertama berisi satu baris. Semua yang lain akan berisi 2 baris. Mungkin Anda bisa membagikan pendapat Anda di bagian komentar, tetapi menurut saya sendiri, ini adalah pilihan desain yang kurang tepat.
Anda dapat mengurangi masalah itu dengan langsung melompat ke baris pertama yang tidak kosong:
sh$ csplit tweets.txt \
> --prefix='tweet.' --suffix-format='%03d.txt' \
> --elide-empty-files \
> --keep-files \
> %.% 2 '{*}'
csplit: ‘2’: line number out of range on repetition 62
123
222
161
[...]
sh$ head tweet.00[012].txt
==> tweet.000.txt <==
{ days:180 }
I think I use the `sed` command daily. And you?\n\nhttps://www.yesik.it/EP07\n#Shell #Linux #Sed\n#YesIKnowIT
==> tweet.001.txt <==
{}
Print the first column of a space-separated data file:\nawk '{print $1}' data.txt # Print out just the first column\n\nFor some unknown reason, I find that easier to remember than:\ncut -f1 data.txt\n\n#Linux #AWK #Cut
==> tweet.002.txt <==
{}
For the #shell #beginners :\n« #GlobPatterns : how to move hundreds of files in not time [1/3] »\nhttps://youtu.be/TvW8DiEmTcQ\n\n#Unix #Linux\n#YesIKnowIT
Membaca dari stdin
Tentu saja, seperti kebanyakan alat baris perintah, csplit
dapat membaca data input dari input standarnya. Dalam hal ini, Anda harus menentukan -
sebagai nama file masukan:
sh$ tr [:lower:] [:upper:] < tweets.txt | csplit - \
> --prefix='tweet.' --suffix-format='%03d.txt' \
> --elide-empty-files \
> --keep-files \
> %.% 2 '{3}'
123
222
161
8524
sh$ head tweet.???.txt
==> tweet.000.txt <==
{ DAYS:180 }
I THINK I USE THE `SED` COMMAND DAILY. AND YOU?\N\NHTTPS://WWW.YESIK.IT/EP07\N#SHELL #LINUX #SED\N#YESIKNOWIT
==> tweet.001.txt <==
{}
PRINT THE FIRST COLUMN OF A SPACE-SEPARATED DATA FILE:\NAWK '{PRINT $1}' DATA.TXT # PRINT OUT JUST THE FIRST COLUMN\N\NFOR SOME UNKNOWN REASON, I FIND THAT EASIER TO REMEMBER THAN:\NCUT -F1 DATA.TXT\N\N#LINUX #AWK #CUT
==> tweet.002.txt <==
{}
FOR THE #SHELL #BEGINNERS :\N« #GLOBPATTERNS : HOW TO MOVE HUNDREDS OF FILES IN NOT TIME [1/3] »\NHTTPS://YOUTU.BE/TVW8DIEMTCQ\N\N#UNIX #LINUX\N#YESIKNOWIT
==> tweet.003.txt <==
{}
WANT TO KNOW THE OLDEST FILE IN YOUR DISK?\N\NFIND / -TYPE F -PRINTF '%TFT%.8TT %P\N' | SORT | LESS\N(SHOULD WORK ON ANY SINGLE UNIX SPECIFICATION COMPLIANT SYSTEM)\N#UNIX #LINUX
{}
WHEN USING THE FIND COMMAND, USE `-INAME` INSTEAD OF `-NAME` FOR CASE-INSENSITIVE SEARCH\N#UNIX #LINUX #SHELL #FIND
{}
FROM A POSIX SHELL `$OLDPWD` HOLDS THE NAME OF THE PREVIOUS WORKING DIRECTORY:\NCD /TMP\NECHO YOU ARE HERE: $PWD\NECHO YOU WERE HERE: $OLDPWD\NCD $OLDPWD\N\N#UNIX #LINUX #SHELL #CD
{}
FROM A POSIX SHELL, "CD" IS A SHORTHAND FOR CD $HOME\N#UNIX #LINUX #SHELL #CD
{}
HOW TO MOVE HUNDREDS OF FILES IN NO TIME?\NUSING THE FIND COMMAND!\N\NHTTPS://YOUTU.BE/ZMEFXJYZAQK\N#UNIX #LINUX #MOVE #FILES #FIND\N#YESIKNOWIT
Dan hanya itu yang ingin saya tunjukkan kepada Anda hari ini. Saya harap di masa mendatang, Anda akan menggunakan csplit untuk membagi file di Linux. Jika Anda menikmati artikel ini dan jangan lupa untuk membagikan dan menyukainya di jejaring sosial favorit Anda!