Linux Command Reference

split

Synopsis

split [OPTION]... [FILE [PREFIX]] split -l LINES [FILE [PREFIX]] split -b SIZE[K|M|G] [FILE [PREFIX]] split -C SIZE[K|M|G] [FILE [PREFIX]]

Description

The split command divides a large file into smaller, more manageable pieces. By default, it creates output files of 1000 lines each, naming them with alphabetic suffixes (xaa, xab, xac, etc.). This fundamental Unix utility solves a common problem: handling files that are too large to process efficiently, email, transfer, or store as single units.

Split is essential for numerous scenarios: breaking up large log files for easier analysis, dividing datasets for parallel processing, preparing files for email attachment size limits, creating manageable backup chunks, splitting data for distributed processing, and working within filesystem or application size constraints. The split pieces can be easily reassembled using cat, making split a safe, reversible operation. Modern split implementations support splitting by line count, byte size, or even by pattern matching, with options for custom naming schemes and numeric suffixes.

Key Concepts

Detailed Examples

Example 1

Basic Line-Based Splitting

Split a file by line count using the default 1000 lines per chunk or specify a custom line count. This is the most common use case for splitting text files and logs.
# Create a sample file seq 1 5000 | awk '{print "Line " $1 ": Sample data"}' > large_file.txt # Default split (1000 lines per file) split large_file.txt # Check what was created ls -lh x* # View contents of first chunk head xaa # Count lines in each chunk wc -l x* # Split into 500-line chunks split -l 500 large_file.txt chunk_ # Check chunk sizes wc -l chunk_* # Split into very small chunks (100 lines) split -l 100 large_file.txt small_ # Custom split: 250 lines with descriptive prefix split -l 250 large_file.txt data_part_ # Reassemble the file cat x* > reassembled.txt # Verify reassembly diff large_file.txt reassembled.txt echo "Files are identical: $?" # Clean up rm x* chunk_* small_* data_part_* reassembled.txt
Output:
# After default split: $ ls -lh x* -rw-r--r-- 1 user user 25K Dec 14 18:00 xaa -rw-r--r-- 1 user user 25K Dec 14 18:00 xab -rw-r--r-- 1 user user 25K Dec 14 18:00 xac -rw-r--r-- 1 user user 25K Dec 14 18:00 xad -rw-r--r-- 1 user user 25K Dec 14 18:00 xae $ wc -l x* 1000 xaa 1000 xab 1000 xac 1000 xad 1000 xae 5000 total # After split -l 500: $ wc -l chunk_* 500 chunk_aa 500 chunk_ab 500 chunk_ac 500 chunk_ad 500 chunk_ae 500 chunk_af 500 chunk_ag 500 chunk_ah 500 chunk_ai 500 chunk_aj 5000 total Files are identical: 0
Note:

Default suffix is two lowercase letters (aa, ab, ac...). This supports up to 676 chunks (26×26). If you need more chunks, use -a to increase suffix length. The -l option sets lines per chunk. Always verify reassembly with diff or checksums.

Example 2

Size-Based Splitting

Split files by byte size rather than line count. Essential for binary files, creating equal-sized chunks for distribution, or working with size constraints.
# Create a large file (10MB) dd if=/dev/urandom of=bigfile.bin bs=1M count=10 2>/dev/null # Split into 1MB chunks split -b 1M bigfile.bin part_ # Check chunk sizes ls -lh part_* # Split into 512KB chunks split -b 512K bigfile.bin small_part_ # Split into exact byte size (1048576 bytes = 1MB) split -b 1048576 bigfile.bin byte_part_ # Size with K, M, G suffixes split -b 2M bigfile.bin big_part_ # 2 megabytes split -b 100K bigfile.bin tiny_part_ # 100 kilobytes # Line-boundary-aware size split with -C # (splits at size but doesn't break lines) cat > text_data.txt >> 'EOF' This is a text file with multiple lines. Each line has varying lengths. Some lines are short. Others are considerably longer and contain more information. EOF # Repeat to make it larger for i in {1..1000}; do cat text_data.txt; done > large_text.txt # Split at 10KB boundaries, preserving whole lines split -C 10K large_text.txt text_chunk_ # Compare -b vs -C split -b 10K large_text.txt binary_split_ split -C 10K large_text.txt line_split_ # Check if lines are broken echo "=== Binary split (may break lines) ===" tail -3 binary_split_aa | head -1 echo "=== Line-aware split (whole lines only) ===" tail -1 line_split_aa # Reassemble and verify binary file cat part_* > reassembled.bin cmp bigfile.bin reassembled.bin && echo "Binary files match!" # Clean up rm part_* small_part_* byte_part_* big_part_* tiny_part_* rm text_chunk_* binary_split_* line_split_* rm bigfile.bin reassembled.bin large_text.txt text_data.txt
Output:
# 1MB chunks: $ ls -lh part_* -rw-r--r-- 1 user user 1.0M Dec 14 18:05 part_aa -rw-r--r-- 1 user user 1.0M Dec 14 18:05 part_ab -rw-r--r-- 1 user user 1.0M Dec 14 18:05 part_ac -rw-r--r-- 1 user user 1.0M Dec 14 18:05 part_ad -rw-r--r-- 1 user user 1.0M Dec 14 18:05 part_ae -rw-r--r-- 1 user user 1.0M Dec 14 18:05 part_af -rw-r--r-- 1 user user 1.0M Dec 14 18:05 part_ag -rw-r--r-- 1 user user 1.0M Dec 14 18:05 part_ah -rw-r--r-- 1 user user 1.0M Dec 14 18:05 part_ai -rw-r--r-- 1 user user 1.0M Dec 14 18:05 part_aj # Size suffixes: K = kilobytes (1024 bytes) M = megabytes (1024K) G = gigabytes (1024M) Binary files match!
Note:

Use -b for exact byte splitting (can break lines mid-character). Use -C for line-aware splitting (respects line boundaries, may produce slightly smaller chunks). Size suffixes: K (1024), M (1024K), G (1024M). Binary files work with both -b and -C.

Example 3

Numeric Suffixes and Custom Naming

Use -d for numeric suffixes instead of alphabetic, customize suffix length with -a, and add custom suffixes for better file identification and organization.
# Create sample file seq 1 1000 > numbers.txt # Numeric suffixes with -d split -d -l 100 numbers.txt num_ # Check naming ls num_* # Longer numeric suffix (4 digits) with -a split -d -a 4 -l 100 numbers.txt part_ # Check naming ls part_* # Add custom suffix with --additional-suffix split -d -l 100 --additional-suffix=.txt numbers.txt chunk_ # Check naming ls chunk_* # Combine options: numeric, 3 digits, custom suffix split -d -a 3 -l 100 --additional-suffix=.log numbers.txt log_ # Check naming ls log_* # Create dated output files DATE=$(date +%Y%m%d) split -d -l 100 --additional-suffix="_${DATE}.txt" numbers.txt daily_ # Check naming ls daily_* # Hexadecimal suffixes with -x split -x -l 100 numbers.txt hex_ # Check naming (0a, 0b, 0c... 0f, 10, 11...) ls hex_* # Complex naming for archive purposes split -d -a 5 -b 1M --additional-suffix=".tar.gz.part" \ largefile.tar.gz backup_$(date +%Y%m%d)_ # Practical: split log by date in filename LOG_DATE=$(date +%Y-%m-%d) split -d -l 1000 --additional-suffix="_${LOG_DATE}.log" \ application.log app_log_part_ # Clean up rm num_* part_* chunk_* log_* daily_* hex_* app_log_part_*
Output:
# Default numeric (-d): num_00 num_01 num_02 num_03 num_04 num_05 num_06 num_07 num_08 num_09 # 4-digit suffix (-a 4): part_0000 part_0001 part_0002 part_0003 part_0004 part_0005 ... # With additional suffix (--additional-suffix=.txt): chunk_00.txt chunk_01.txt chunk_02.txt chunk_03.txt ... # Three digits plus .log: log_000.log log_001.log log_002.log log_003.log ... # Dated files: daily_00_20251214.txt daily_01_20251214.txt daily_02_20251214.txt ... # Hexadecimal suffixes: hex_00 hex_01 hex_02 hex_03 hex_04 hex_05 hex_06 hex_07 hex_08 hex_09 hex_0a hex_0b hex_0c hex_0d hex_0e hex_0f
Note:

The -d flag uses numeric suffixes (00, 01, 02...). The -a N sets suffix length (default 2). The --additional-suffix adds a string after the numeric suffix - great for extensions like .txt or .log. The -x flag creates hexadecimal suffixes. Numeric suffixes are easier to sort and process programmatically.

Example 4

Splitting Large Log Files

Split large log files for easier analysis, archival, or distribution. Common scenario in system administration where logs grow too large to process efficiently.
# Create a simulated large log file cat > generate_logs.sh >> 'EOF' #!/bin/bash for i in {1..10000}; do timestamp=$(date -d "$i minutes ago" '+%Y-%m-%d %H:%M:%S') level=$((RANDOM % 4)) case $level in 0) lev="INFO" ;; 1) lev="WARN" ;; 2) lev="ERROR" ;; 3) lev="DEBUG" ;; esac echo "$timestamp [$lev] Application message $i" done EOF chmod +x generate_logs.sh ./generate_logs.sh > application.log # Split into daily-sized chunks (assuming 10000 lines per day) split -d -l 10000 \ --additional-suffix=.log \ application.log \ app_$(date +%Y%m%d)_ # Split by size (10MB chunks) split -d -b 10M \ --additional-suffix=.log \ application.log \ app_size_ # Split for archival (1000 lines each, with dates) split -d -l 1000 \ --additional-suffix="_archive.log" \ application.log \ archive_$(date +%Y%m%d)_ # Split and compress each chunk split -d -l 5000 application.log split_temp_ for file in split_temp_*; do gzip -c "$file" > "${file}.log.gz" rm "$file" done # Process each chunk separately (example: count errors) split -d -l 2000 application.log chunk_ for chunk in chunk_*; do errors=$(grep -c ERROR "$chunk") echo "$chunk: $errors errors" done # Split logs by severity grep "ERROR" application.log > errors.log grep "WARN" application.log > warnings.log grep "INFO" application.log > info.log # Then split each severity log split -d -l 1000 --additional-suffix=.log errors.log error_part_ split -d -l 1000 --additional-suffix=.log warnings.log warn_part_ split -d -l 1000 --additional-suffix=.log info.log info_part_ # Create monthly archive script cat > archive_logs.sh >> 'EOF' #!/bin/bash LOG_FILE="$1" MONTH=$(date +%Y%m) # Split into 1GB chunks split -d -b 1G \ --additional-suffix=".log.part" \ "$LOG_FILE" \ "archive_${MONTH}_" # Compress each chunk for chunk in archive_${MONTH}_*.log.part; do gzip "$chunk" echo "Compressed: ${chunk}.gz" done EOF chmod +x archive_logs.sh # Clean up rm chunk_* split_temp_*.log.gz error_part_* warn_part_* info_part_* rm errors.log warnings.log info.log rm generate_logs.sh archive_logs.sh
Output:
# Daily chunks: app_20251214_00.log (10000 lines) # Size-based chunks: app_size_00.log (10MB) app_size_01.log (10MB) app_size_02.log (remaining) # Archive chunks: archive_20251214_00_archive.log (1000 lines) archive_20251214_01_archive.log (1000 lines) archive_20251214_02_archive.log (1000 lines) ... archive_20251214_09_archive.log (1000 lines) # Error counts per chunk: chunk_00: 523 errors chunk_01: 487 errors chunk_02: 501 errors chunk_03: 495 errors chunk_04: 512 errors # Compressed archives: Compressed: archive_202512_00.log.part.gz Compressed: archive_202512_01.log.part.gz
Note:

Splitting logs makes them manageable for analysis tools, reduces memory requirements, enables parallel processing, and facilitates archival. Combine split with compression (gzip, xz) to save storage. Use date-based naming for chronological organization. Always keep a backup before splitting critical logs.

Example 5

Parallel Processing with Split Files

Split large datasets into chunks for parallel processing across multiple CPU cores or machines. Dramatically speeds up processing of large files.
# Create a large dataset seq 1 100000 | awk '{print $1 "," rand()*100 "," rand()*1000}' > data.csv # Split for parallel processing (10000 lines each) split -d -l 10000 --additional-suffix=.csv data.csv data_chunk_ # Process chunks in parallel with GNU parallel if command -v parallel &> /dev/null; then # Example: sum the second column parallel "awk -F',' '{sum+=\$2} END {print FILENAME, sum}'" ::: data_chunk_*.csv fi # Process chunks in parallel with background jobs for chunk in data_chunk_*.csv; do ( # Simulate processing result=$(awk -F',' '{sum+=$2; count++} END {print sum/count}' "$chunk") echo "$chunk average: $result" ) & done wait # Split and process with xargs ls data_chunk_*.csv | \ xargs -n 1 -P 4 -I {} bash -c 'wc -l {} && sleep 1' # More complex: process and merge results cat > process_chunk.sh >> 'EOF' #!/bin/bash chunk="$1" output="${chunk}.processed" # Simulate complex processing awk -F',' '{ if ($2 > 50) { print $0 ",HIGH" } else { print $0 ",LOW" } }' "$chunk" > "$output" echo "Processed: $chunk -> $output" EOF chmod +x process_chunk.sh # Process all chunks in parallel for chunk in data_chunk_*.csv; do ./process_chunk.sh "$chunk" & done wait # Merge results cat data_chunk_*.csv.processed > final_output.csv echo "Final output lines: $(wc -l < final_output.csv)" # Split for distributed processing across machines split -d -l 10000 --additional-suffix=.csv data.csv /tmp/distributed_ # Create processing script for each machine cat > distributed_process.sh >> 'EOF' #!/bin/bash CHUNK="$1" SERVER="$2" # Copy to remote server scp "$CHUNK" "$SERVER:/tmp/" # Process remotely ssh "$SERVER" "process_data.sh /tmp/$(basename $CHUNK)" # Retrieve results scp "$SERVER:/tmp/$(basename $CHUNK).result" ./results/ EOF # Example: distribute to 4 servers # for i in {00..09}; do # server_num=$((i % 4)) # ./distributed_process.sh distributed_$i.csv server${server_num}.example.com & # done # Clean up rm data_chunk_*.csv data_chunk_*.csv.processed rm process_chunk.sh final_output.csv rm /tmp/distributed_*.csv 2>/dev/null
Output:
# Parallel processing with background jobs: data_chunk_00.csv average: 49.87 data_chunk_03.csv average: 50.23 data_chunk_01.csv average: 49.45 data_chunk_02.csv average: 50.67 data_chunk_04.csv average: 49.91 ... # With xargs parallel: 10000 data_chunk_00.csv 10000 data_chunk_01.csv 10000 data_chunk_02.csv 10000 data_chunk_03.csv ... # Processing chunks: Processed: data_chunk_00.csv -> data_chunk_00.csv.processed Processed: data_chunk_01.csv -> data_chunk_01.csv.processed Processed: data_chunk_02.csv -> data_chunk_02.csv.processed ... Final output lines: 100000
Note:

Split enables parallel processing by dividing work into independent chunks. Use background jobs (&), GNU parallel, or xargs -P for local parallelization. For distributed processing, split files and distribute to multiple machines via scp/rsync. Remember to merge results after processing. Speedup is roughly proportional to number of cores/machines (minus overhead).

Example 6

Email Attachment Size Limits

Split large files to fit within email attachment size limits. Most email services limit attachments to 10-25MB, requiring large files to be split before sending.
# Create a large presentation file (simulated) dd if=/dev/urandom of=presentation.pptx bs=1M count=30 2>/dev/null # Check file size ls -lh presentation.pptx # Split into 10MB chunks (common email limit) split -d -b 10M \ --additional-suffix=.part \ presentation.pptx \ presentation_ # List parts ls -lh presentation_*.part # Create README for recipient cat > REASSEMBLE.txt >> 'EOF' To reassemble the presentation: On Linux/Mac: cat presentation_*.part > presentation.pptx On Windows (Command Prompt): copy /b presentation_00.part+presentation_01.part+presentation_02.part presentation.pptx Or use the included script: bash reassemble.sh Verify the checksum matches: MD5: [checksum will be here] EOF # Add checksum to README md5sum presentation.pptx | awk '{print " MD5: " $1}' >> REASSEMBLE.txt # Create reassembly script cat > reassemble.sh >> 'EOF' #!/bin/bash echo "Reassembling presentation.pptx..." cat presentation_*.part > presentation.pptx echo "Done! Verifying checksum..." # Add verification here echo "presentation.pptx created successfully" EOF chmod +x reassemble.sh # Split with instructions embedded in filenames split -d -a 2 -b 10M \ --additional-suffix="_of_3.zip" \ largefile.zip \ archive_part_ # For Windows compatibility, create .bat file cat > reassemble.bat >> 'EOF' @echo off echo Reassembling files... copy /b presentation_00.part+presentation_01.part+presentation_02.part presentation.pptx echo Done! pause EOF # Create a complete email package mkdir email_package mv presentation_*.part email_package/ mv REASSEMBLE.txt email_package/ mv reassemble.sh email_package/ mv reassemble.bat email_package/ # Create tar archive of the package tar czf email_package.tar.gz email_package/ echo "Package ready for email: email_package.tar.gz" echo "Or send individual files from: email_package/" # Alternative: Base64 encode for text-only email cat > email_text.sh >> 'EOF' #!/bin/bash FILE="$1" CHUNK_SIZE=10000 # lines, not bytes # Split file split -d -l $CHUNK_SIZE \ --additional-suffix=.b64 \ <(base64 "$FILE") \ "${FILE%.${FILE##*.}}_b64_" echo "Created base64-encoded chunks:" ls -lh "${FILE%.${FILE##*.}}"_b64_*.b64 echo "To reassemble:" echo "cat ${FILE%.${FILE##*.}}_b64_*.b64 | base64 -d > $FILE" EOF chmod +x email_text.sh # Clean up rm -rf email_package email_package.tar.gz rm presentation.pptx archive_part_* email_text.sh
Output:
# Original file: -rw-r--r-- 1 user user 30M Dec 14 18:30 presentation.pptx # Split into 10MB chunks: -rw-r--r-- 1 user user 10M Dec 14 18:30 presentation_00.part -rw-r--r-- 1 user user 10M Dec 14 18:30 presentation_01.part -rw-r--r-- 1 user user 10M Dec 14 18:30 presentation_02.part # Email package contents: email_package/ ├── presentation_00.part ├── presentation_01.part ├── presentation_02.part ├── REASSEMBLE.txt ├── reassemble.sh └── reassemble.bat Package ready for email: email_package.tar.gz Or send individual files from: email_package/ # REASSEMBLE.txt contents: To reassemble the presentation: On Linux/Mac: cat presentation_*.part > presentation.pptx On Windows (Command Prompt): copy /b presentation_00.part+presentation_01.part+... MD5: a1b2c3d4e5f6...
Note:

Common email limits: Gmail 25MB, Outlook 20MB, Yahoo 25MB. Split to 10MB to be safe. Always include reassembly instructions for recipients. Include checksums to verify integrity. Create both Unix (cat) and Windows (copy /b) reassembly instructions. Consider base64 encoding for text-only email systems.

Example 7

Filter and Transform During Split

Use --filter option to process chunks through a command as they're created. Enables compression, encryption, or custom processing during the split operation.
# Create sample data seq 1 10000 > data.txt # Split and compress each chunk with gzip split -d -l 1000 \ --filter='gzip > $FILE.gz' \ data.txt chunk_ # Check compressed chunks ls -lh chunk_*.gz # Split and compress with xz (better compression) split -d -l 1000 \ --filter='xz -9 > $FILE.xz' \ data.txt compressed_ # Compare compression ls -lh compressed_*.xz # Split and encrypt each chunk split -d -b 1M \ --filter='openssl enc -aes-256-cbc -salt -pass pass:secret > $FILE.enc' \ sensitive_data.bin encrypted_ # Split with custom processing split -d -l 500 \ --filter='awk "{print NR, \$0}" | gzip > $FILE.processed.gz' \ data.txt processed_ # Split and upload to remote storage split -d -b 100M \ --filter='aws s3 cp - s3://bucket/backup/$FILE' \ largefile.tar.gz backup_ # Split with hash generation split -d -b 10M \ --filter='tee $FILE | md5sum > $FILE.md5' \ archive.tar archive_part_ # Split and send each chunk through pipeline split -d -l 1000 \ --filter='sort | uniq | gzip > $FILE.sorted.gz' \ unsorted_data.txt sorted_ # Combine multiple filters split -d -b 5M \ --filter='gzip | gpg --encrypt --recipient user@example.com > $FILE.gpg' \ confidential.pdf secure_ # Split with progress indication split -d -l 1000 \ --filter='pv -N "Processing $FILE" > $FILE && gzip $FILE' \ largefile.txt monitored_ # Reassemble filtered chunks (gzip example) for file in chunk_*.gz; do gunzip -c "$file" done > reassembled.txt # Verify reassembly diff data.txt reassembled.txt # Reassemble encrypted chunks for file in encrypted_*.enc; do openssl enc -d -aes-256-cbc -pass pass:secret -in "$file" done > decrypted_data.bin # Clean up rm chunk_*.gz compressed_*.xz processed_*.processed.gz rm sorted_*.sorted.gz reassembled.txt
Output:
# Compressed chunks: $ ls -lh chunk_*.gz -rw-r--r-- 1 user user 2.1K Dec 14 18:45 chunk_00.gz -rw-r--r-- 1 user user 2.1K Dec 14 18:45 chunk_01.gz -rw-r--r-- 1 user user 2.1K Dec 14 18:45 chunk_02.gz ... # Size comparison (1000 lines each): Original chunk: ~5KB Gzip compressed: ~2.1KB XZ compressed: ~1.8KB # With hash generation: archive_part_00 archive_part_00.md5 archive_part_01 archive_part_01.md5 # Contents of .md5 file: a1b2c3d4e5f6g7h8 archive_part_00 # Verification: Files are identical after reassembly
Warning:

The --filter option is powerful but use with caution. The $FILE variable contains the chunk filename. Commands in --filter run for each chunk. Test your filter on small data first. Remember that filtered chunks require reverse processing to reassemble (e.g., gunzip for gzipped chunks). Keep encryption passwords secure!

Example 8

Splitting and Reassembly Verification

Implement robust splitting with checksums, verification, and automated reassembly scripts. Essential for ensuring data integrity when splitting critical files.
# Create a complete split-and-verify system cat > smart_split.sh >> 'EOF' #!/bin/bash set -euo pipefail # Configuration INPUT_FILE="$1" CHUNK_SIZE="${2:-10M}" PREFIX="${3:-chunk_}" # Validation if [ ! -f "$INPUT_FILE" ]; then echo "Error: File not found: $INPUT_FILE" exit 1 fi echo "Smart Split System" echo "==================" echo "Input file: $INPUT_FILE" echo "Chunk size: $CHUNK_SIZE" echo "Prefix: $PREFIX" echo "" # Generate checksums echo "Generating checksum..." ORIGINAL_MD5=$(md5sum "$INPUT_FILE" | awk '{print $1}') ORIGINAL_SHA256=$(sha256sum "$INPUT_FILE" | awk '{print $1}') ORIGINAL_SIZE=$(stat -f%z "$INPUT_FILE" 2>/dev/null || stat -c%s "$INPUT_FILE") echo "Original MD5: $ORIGINAL_MD5" echo "Original SHA256: $ORIGINAL_SHA256" echo "Original size: $ORIGINAL_SIZE bytes" echo "" # Split the file echo "Splitting file..." split -d -b "$CHUNK_SIZE" \ --additional-suffix=.part \ "$INPUT_FILE" \ "$PREFIX" # Count chunks CHUNK_COUNT=$(ls ${PREFIX}*.part | wc -l) echo "Created $CHUNK_COUNT chunks" echo "" # Create manifest MANIFEST="${PREFIX}manifest.txt" echo "Creating manifest: $MANIFEST" { echo "# Split File Manifest" echo "# Generated: $(date)" echo "# Original file: $INPUT_FILE" echo "# Original MD5: $ORIGINAL_MD5" echo "# Original SHA256: $ORIGINAL_SHA256" echo "# Original size: $ORIGINAL_SIZE" echo "# Chunk size: $CHUNK_SIZE" echo "# Chunk count: $CHUNK_COUNT" echo "" echo "# Chunk checksums:" for chunk in ${PREFIX}*.part; do chunk_md5=$(md5sum "$chunk" | awk '{print $1}') chunk_size=$(stat -f%z "$chunk" 2>/dev/null || stat -c%s "$chunk") echo "$chunk $chunk_size $chunk_md5" done } > "$MANIFEST" # Create reassembly script REASSEMBLE="${PREFIX}reassemble.sh" echo "Creating reassembly script: $REASSEMBLE" cat > "$REASSEMBLE" >> EOFSCRIPT #!/bin/bash set -euo pipefail echo "Reassembly Script" echo "=================" # Read manifest MANIFEST="$MANIFEST" if [ ! -f "\$MANIFEST" ]; then echo "Error: Manifest not found" exit 1 fi # Extract original checksums ORIGINAL_MD5=\$(grep "# Original MD5:" "\$MANIFEST" | awk '{print \$4}') ORIGINAL_SIZE=\$(grep "# Original size:" "\$MANIFEST" | awk '{print \$4}') ORIGINAL_FILE=\$(grep "# Original file:" "\$MANIFEST" | awk '{print \$4}') echo "Target file: \$ORIGINAL_FILE" echo "Expected MD5: \$ORIGINAL_MD5" echo "Expected size: \$ORIGINAL_SIZE bytes" echo "" # Verify all chunks exist echo "Verifying chunks..." MISSING=0 while read line; do if [[ "\$line" =~ ^# ]] || [[ -z "\$line" ]]; then continue fi chunk=\$(echo "\$line" | awk '{print \$1}') if [ ! -f "\$chunk" ]; then echo "Missing: \$chunk" MISSING=\$((MISSING + 1)) fi done < "\$MANIFEST" if [ \$MISSING -gt 0 ]; then echo "Error: \$MISSING chunks missing" exit 1 fi echo "All chunks present" echo "" # Verify chunk checksums echo "Verifying chunk checksums..." ERRORS=0 while read line; do if [[ "\$line" =~ ^# ]] || [[ -z "\$line" ]]; then continue fi chunk=\$(echo "\$line" | awk '{print \$1}') expected_md5=\$(echo "\$line" | awk '{print \$3}') actual_md5=\$(md5sum "\$chunk" | awk '{print \$1}') if [ "\$expected_md5" != "\$actual_md5" ]; then echo "Checksum mismatch: \$chunk" ERRORS=\$((ERRORS + 1)) fi done < "\$MANIFEST" if [ \$ERRORS -gt 0 ]; then echo "Error: \$ERRORS chunks have checksum errors" exit 1 fi echo "All checksums valid" echo "" # Reassemble echo "Reassembling file..." cat ${PREFIX}*.part > "\$ORIGINAL_FILE" # Verify reassembled file echo "Verifying reassembled file..." REASSEMBLED_MD5=\$(md5sum "\$ORIGINAL_FILE" | awk '{print \$1}') REASSEMBLED_SIZE=\$(stat -f%z "\$ORIGINAL_FILE" 2>/dev/null || stat -c%s "\$ORIGINAL_FILE") if [ "\$REASSEMBLED_MD5" = "\$ORIGINAL_MD5" ]; then echo "✓ MD5 checksum matches" else echo "✗ MD5 checksum MISMATCH" echo " Expected: \$ORIGINAL_MD5" echo " Got: \$REASSEMBLED_MD5" exit 1 fi if [ "\$REASSEMBLED_SIZE" = "\$ORIGINAL_SIZE" ]; then echo "✓ File size matches" else echo "✗ File size MISMATCH" exit 1 fi echo "" echo "SUCCESS: File reassembled and verified" echo "Output: \$ORIGINAL_FILE" EOFSCRIPT chmod +x "$REASSEMBLE" echo "" echo "Split complete!" echo "Chunks: ${PREFIX}*.part" echo "Manifest: $MANIFEST" echo "Reassemble with: ./$REASSEMBLE" EOF chmod +x smart_split.sh # Use the smart split system dd if=/dev/urandom of=testfile.bin bs=1M count=25 2>/dev/null ./smart_split.sh testfile.bin 5M split_ # Test reassembly rm testfile.bin ./split_reassemble.sh # Verify ls -lh testfile.bin # Clean up rm split_*.part split_manifest.txt split_reassemble.sh rm testfile.bin smart_split.sh
Output:
Smart Split System ================== Input file: testfile.bin Chunk size: 5M Prefix: split_ Generating checksum... Original MD5: a1b2c3d4e5f6... Original SHA256: 1a2b3c4d5e6f... Original size: 26214400 bytes Splitting file... Created 5 chunks Creating manifest: split_manifest.txt Creating reassembly script: split_reassemble.sh Split complete! Chunks: split_*.part Manifest: split_manifest.txt Reassemble with: ./split_reassemble.sh # Running reassembly: Reassembly Script ================= Target file: testfile.bin Expected MD5: a1b2c3d4e5f6... Expected size: 26214400 bytes Verifying chunks... All chunks present Verifying chunk checksums... All checksums valid Reassembling file... Verifying reassembled file... ✓ MD5 checksum matches ✓ File size matches SUCCESS: File reassembled and verified Output: testfile.bin
Note:

This complete system ensures data integrity through checksums, creates detailed manifests, and provides automated reassembly with verification. Always use checksums for critical data. The manifest file documents the split operation and enables verification. The reassembly script checks every step before declaring success.

Example 9

Advanced Pattern-Based Splitting

While split doesn't natively support pattern splitting, combine it with other tools to split files based on content patterns, useful for structured data like multi-document files.
# Create multi-document file cat > documents.txt >> 'EOF' === DOCUMENT 1 === This is the first document. It has multiple lines. More content here. === DOCUMENT 2 === Second document content. Different information. Additional data. === DOCUMENT 3 === Third document here. Even more content. Final lines. EOF # Split on pattern using awk awk '/=== DOCUMENT/{ if (out) close(out) out = "doc_" sprintf("%02d", ++n) ".txt" print > out next } out {print > out}' documents.txt # Check created files ls -l doc_*.txt # Split CSV file by column value cat > data.csv >> 'EOF' Region,Product,Sales North,Widget,100 North,Gadget,150 South,Widget,120 South,Gadget,130 East,Widget,110 East,Gadget,140 West,Widget,105 West,Gadget,145 EOF # Split by region awk -F',' 'NR==1{header=$0; next} { region=$1 if (!(region in seen)) { print header > region ".csv" seen[region]=1 } print > region ".csv" }' data.csv # Check created files ls -l *.csv # Split log file by date cat > timestamped.log >> 'EOF' 2025-12-14 Event 1 2025-12-14 Event 2 2025-12-15 Event 3 2025-12-15 Event 4 2025-12-16 Event 5 EOF awk '{ date=substr($1,1,10) file="log_" date ".txt" print > file }' timestamped.log # Split on blank lines (paragraphs) cat > paragraphs.txt >> 'EOF' First paragraph. More text here. Second paragraph. Different content. Third paragraph. Final text. EOF awk 'BEGIN{n=1} /^$/{n++; next} {print > "para_" sprintf("%02d", n) ".txt"}' paragraphs.txt # Split XML/JSON by record cat > records.json >> 'EOF' {"id": 1, "name": "Alice"} {"id": 2, "name": "Bob"} {"id": 3, "name": "Charlie"} EOF # Split JSON lines into separate files awk '{print > "record_" sprintf("%03d", NR) ".json"}' records.json # Split based on file size threshold within pattern cat > smart_split_pattern.sh >> 'EOF' #!/bin/bash INPUT="$1" PATTERN="$2" MAX_SIZE="$3" # in lines awk -v pattern="$pattern" -v maxsize="$MAX_SIZE" ' BEGIN {chunk=0; count=0} $0 ~ pattern { if (count >= maxsize) { close(out) chunk++ count=0 } if (count == 0) { out = sprintf("chunk_%03d.txt", chunk) } } { print > out count++ }' "$INPUT" EOF chmod +x smart_split_pattern.sh # Clean up example files rm doc_*.txt North.csv South.csv East.csv West.csv rm log_*.txt para_*.txt record_*.json rm documents.txt data.csv timestamped.log paragraphs.txt records.json rm smart_split_pattern.sh
Output:
# Documents split: doc_01.txt doc_02.txt doc_03.txt # Contents of doc_01.txt: === DOCUMENT 1 === This is the first document. It has multiple lines. More content here. # CSV files by region: North.csv South.csv East.csv West.csv # North.csv contents: Region,Product,Sales North,Widget,100 North,Gadget,150 # Log files by date: log_2025-12-14.txt log_2025-12-15.txt log_2025-12-16.txt # Paragraphs split: para_01.txt para_02.txt para_03.txt # JSON records: record_001.json record_002.json record_003.json
Note:

While split doesn't handle patterns directly, awk provides powerful pattern-based splitting. Use awk to split on delimiters, dates, blank lines, or any pattern you can match. This is essential for structured data like logs, CSV files, or multi-document formats. Combine pattern detection with file size limits for sophisticated splitting.

Example 10

Complete Backup Splitting System

A production-ready backup system that splits large backups into manageable chunks, compresses them, generates manifests, and creates restoration scripts. Suitable for automated backup workflows.
#!/bin/bash # File: backup_splitter.sh # Production backup splitting and archival system set -euo pipefail # ============================================ # Configuration # ============================================ readonly BACKUP_DIR="${BACKUP_DIR:-./backups}" readonly ARCHIVE_DIR="${ARCHIVE_DIR:-./archives}" readonly CHUNK_SIZE="${CHUNK_SIZE:-100M}" readonly COMPRESSION="${COMPRESSION:-gzip}" readonly LOG_FILE="${LOG_FILE:-backup_split.log}" # ============================================ # Logging # ============================================ log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" } # ============================================ # Initialize directories # ============================================ mkdir -p "$BACKUP_DIR" "$ARCHIVE_DIR" # ============================================ # Compression functions # ============================================ compress_chunk() { local input="$1" local output="$2" case "$COMPRESSION" in gzip) gzip -c "$input" > "${output}.gz" rm "$input" echo "${output}.gz" ;; xz) xz -c "$input" > "${output}.xz" rm "$input" echo "${output}.xz" ;; bzip2) bzip2 -c "$input" > "${output}.bz2" rm "$input" echo "${output}.bz2" ;; none) mv "$input" "$output" echo "$output" ;; *) log "Unknown compression: $COMPRESSION" exit 1 ;; esac } # ============================================ # Split and archive function # ============================================ split_and_archive() { local source_file="$1" local backup_name="${2:-$(basename "$source_file")}" log "=========================================" log "Starting backup split: $backup_name" log "Source: $source_file" log "Chunk size: $CHUNK_SIZE" log "Compression: $COMPRESSION" log "=========================================" # Validate source if [ ! -f "$source_file" ]; then log "Error: Source file not found: $source_file" return 1 fi # Create timestamped archive directory local timestamp=$(date +%Y%m%d_%H%M%S) local archive_path="$ARCHIVE_DIR/${backup_name}_${timestamp}" mkdir -p "$archive_path" log "Archive path: $archive_path" # Generate checksums log "Generating checksums..." local original_md5=$(md5sum "$source_file" | awk '{print $1}') local original_sha256=$(sha256sum "$source_file" | awk '{print $1}') local original_size=$(stat -c%s "$source_file" 2>/dev/null || stat -f%z "$source_file") log "Original MD5: $original_md5" log "Original size: $original_size bytes" # Split the file log "Splitting file..." local split_prefix="$archive_path/chunk_" split -d -b "$CHUNK_SIZE" \ -a 4 \ --additional-suffix=.part \ "$source_file" \ "$split_prefix" # Compress chunks log "Compressing chunks..." local chunk_count=0 local compressed_chunks=() for chunk in ${split_prefix}*.part; do local compressed=$(compress_chunk "$chunk" "$chunk") compressed_chunks+=("$compressed") ((chunk_count++)) log " Compressed: $(basename "$compressed")" done log "Created $chunk_count chunks" # Generate manifest log "Creating manifest..." local manifest="$archive_path/MANIFEST.txt" { echo "# Backup Archive Manifest" echo "# =======================" echo "# Created: $(date)" echo "# Source: $source_file" echo "# Backup name: $backup_name" echo "# Original MD5: $original_md5" echo "# Original SHA256: $original_sha256" echo "# Original size: $original_size bytes" echo "# Chunk size: $CHUNK_SIZE" echo "# Compression: $COMPRESSION" echo "# Chunk count: $chunk_count" echo "" echo "# Chunk details (filename | size | md5):" for compressed in "${compressed_chunks[@]}"; do local chunk_size=$(stat -c%s "$compressed" 2>/dev/null || stat -f%z "$compressed") local chunk_md5=$(md5sum "$compressed" | awk '{print $1}') local chunk_name=$(basename "$compressed") echo "$chunk_name | $chunk_size | $chunk_md5" done } > "$manifest" # Create restoration script log "Creating restoration script..." local restore_script="$archive_path/RESTORE.sh" cat > "$restore_script" >> 'EOFSCRIPT' #!/bin/bash set -euo pipefail echo "=====================================" echo "Backup Restoration Script" echo "=====================================" # Read manifest MANIFEST="MANIFEST.txt" if [ ! -f "$MANIFEST" ]; then echo "Error: MANIFEST.txt not found" exit 1 fi # Parse manifest ORIGINAL_FILE=$(grep "# Source:" "$MANIFEST" | cut -d: -f2- | xargs) ORIGINAL_MD5=$(grep "# Original MD5:" "$MANIFEST" | awk '{print $4}') ORIGINAL_SIZE=$(grep "# Original size:" "$MANIFEST" | awk '{print $4}') COMPRESSION=$(grep "# Compression:" "$MANIFEST" | awk '{print $3}') CHUNK_COUNT=$(grep "# Chunk count:" "$MANIFEST" | awk '{print $4}') echo "Source file: $ORIGINAL_FILE" echo "Expected MD5: $ORIGINAL_MD5" echo "Expected size: $ORIGINAL_SIZE bytes" echo "Compression: $COMPRESSION" echo "Chunks: $CHUNK_COUNT" echo "" # Output file OUTPUT_FILE="${1:-restored_$(basename "$ORIGINAL_FILE")}" echo "Output file: $OUTPUT_FILE" echo "" # Verify chunks echo "Verifying chunks..." CHUNK_ERRORS=0 while IFS='|' read -r filename size md5; do # Skip comments and empty lines [[ "$filename" =~ ^# ]] && continue [[ -z "$filename" ]] && continue # Trim whitespace filename=$(echo "$filename" | xargs) size=$(echo "$size" | xargs) md5=$(echo "$md5" | xargs) if [ ! -f "$filename" ]; then echo "✗ Missing: $filename" ((CHUNK_ERRORS++)) else actual_md5=$(md5sum "$filename" | awk '{print $1}') if [ "$md5" != "$actual_md5" ]; then echo "✗ Checksum mismatch: $filename" ((CHUNK_ERRORS++)) fi fi done < <(grep -v "^#" "$MANIFEST" | grep -v "^$") if [ $CHUNK_ERRORS -gt 0 ]; then echo "" echo "Error: $CHUNK_ERRORS chunk verification failures" exit 1 fi echo "✓ All chunks verified" echo "" # Decompress and reassemble echo "Decompressing and reassembling..." case "$COMPRESSION" in gzip) gunzip -c chunk_*.part.gz | cat > "$OUTPUT_FILE" ;; xz) xz -dc chunk_*.part.xz | cat > "$OUTPUT_FILE" ;; bzip2) bunzip2 -c chunk_*.part.bz2 | cat > "$OUTPUT_FILE" ;; none) cat chunk_*.part > "$OUTPUT_FILE" ;; *) echo "Error: Unknown compression: $COMPRESSION" exit 1 ;; esac # Verify restored file echo "Verifying restored file..." RESTORED_MD5=$(md5sum "$OUTPUT_FILE" | awk '{print $1}') RESTORED_SIZE=$(stat -c%s "$OUTPUT_FILE" 2>/dev/null || stat -f%z "$OUTPUT_FILE") if [ "$RESTORED_MD5" = "$ORIGINAL_MD5" ]; then echo "✓ MD5 checksum matches" else echo "✗ MD5 mismatch!" echo " Expected: $ORIGINAL_MD5" echo " Got: $RESTORED_MD5" exit 1 fi if [ "$RESTORED_SIZE" = "$ORIGINAL_SIZE" ]; then echo "✓ File size matches" else echo "✗ Size mismatch!" exit 1 fi echo "" echo "=====================================" echo "SUCCESS: File restored and verified" echo "Output: $OUTPUT_FILE" echo "=====================================" EOFSCRIPT chmod +x "$restore_script" # Create archive summary local total_size=0 for compressed in "${compressed_chunks[@]}"; do local chunk_size=$(stat -c%s "$compressed" 2>/dev/null || stat -f%z "$compressed") total_size=$((total_size + chunk_size)) done local compression_ratio=$(awk "BEGIN {printf \"%.2f\", ($original_size/$total_size)}") log "=========================================" log "Backup complete!" log "Original size: $original_size bytes" log "Compressed size: $total_size bytes" log "Compression ratio: ${compression_ratio}:1" log "Chunks: $chunk_count" log "Archive location: $archive_path" log "Restore with: cd $archive_path && ./RESTORE.sh" log "=========================================" } # ============================================ # Main # ============================================ main() { case "${1:-}" in split) if [ $# -lt 2 ]; then echo "Usage: $0 split <file> [backup_name]" exit 1 fi split_and_archive "$2" "${3:-}" ;; *) echo "Usage: $0 split <file> [backup_name]" echo "" echo "Environment variables:" echo " BACKUP_DIR - Source directory (default: ./backups)" echo " ARCHIVE_DIR - Archive directory (default: ./archives)" echo " CHUNK_SIZE - Chunk size (default: 100M)" echo " COMPRESSION - Compression type: gzip|xz|bzip2|none (default: gzip)" exit 1 ;; esac } main "$@"
Script usage output:
$ ./backup_splitter.sh split database.tar [2025-12-14 19:00:00] ========================================= [2025-12-14 19:00:00] Starting backup split: database.tar [2025-12-14 19:00:00] Source: database.tar [2025-12-14 19:00:00] Chunk size: 100M [2025-12-14 19:00:00] Compression: gzip [2025-12-14 19:00:00] ========================================= [2025-12-14 19:00:00] Archive path: ./archives/database.tar_20251214_190000 [2025-12-14 19:00:00] Generating checksums... [2025-12-14 19:00:00] Original MD5: a1b2c3d4... [2025-12-14 19:00:00] Original size: 524288000 bytes [2025-12-14 19:00:00] Splitting file... [2025-12-14 19:00:05] Compressing chunks... [2025-12-14 19:00:05] Compressed: chunk_0000.part.gz [2025-12-14 19:00:06] Compressed: chunk_0001.part.gz [2025-12-14 19:00:07] Compressed: chunk_0002.part.gz [2025-12-14 19:00:08] Compressed: chunk_0003.part.gz [2025-12-14 19:00:09] Compressed: chunk_0004.part.gz [2025-12-14 19:00:09] Created 5 chunks [2025-12-14 19:00:09] Creating manifest... [2025-12-14 19:00:09] Creating restoration script... [2025-12-14 19:00:09] ========================================= [2025-12-14 19:00:09] Backup complete! [2025-12-14 19:00:09] Original size: 524288000 bytes [2025-12-14 19:00:09] Compressed size: 367001600 bytes [2025-12-14 19:00:09] Compression ratio: 1.43:1 [2025-12-14 19:00:09] Chunks: 5 [2025-12-14 19:00:09] Archive location: ./archives/database.tar_20251214_190000 [2025-12-14 19:00:09] Restore with: cd ./archives/database.tar_20251214_190000 && ./RESTORE.sh [2025-12-14 19:00:09] ========================================= # Archive directory structure: archives/database.tar_20251214_190000/ ├── chunk_0000.part.gz ├── chunk_0001.part.gz ├── chunk_0002.part.gz ├── chunk_0003.part.gz ├── chunk_0004.part.gz ├── MANIFEST.txt └── RESTORE.sh
Note:

This production system provides complete backup splitting with compression, checksums, manifests, and automated restoration. Features: configurable chunk size and compression, integrity verification, detailed logging, timestamped archives, and self-contained restoration. Perfect for automated backup workflows, database dumps, and large file archival.

Tips & Best Practices

Default is 1000 Lines
Without options, split creates 1000-line chunks with prefix 'x' and alphabetic suffixes. Equivalent to: split -l 1000. Change with -l for lines or -b for bytes.
Use -d for Numeric Suffixes
Numeric suffixes (00, 01, 02...) are easier to sort and process than alphabetic (aa, ab...). Use split -d for better organization. Combine with -a to set suffix length.
Reassemble with cat
Always reassemble with: cat prefix* > original_file. Wildcard expansion sorts correctly if you used consistent naming. Verify with diff or checksums after reassembly.
Use -C for Text Files
For text files, use -C SIZE instead of -b SIZE. -C respects line boundaries, preventing mid-line splits. Chunks may be slightly smaller than specified size.
Calculate Suffix Length
Default suffix length (-a 2) supports 676 chunks (26²). For more chunks, increase: -a 3 = 17,576 chunks, -a 4 = 456,976 chunks. Calculate: chunks needed / suffix_length.
Add Extensions with --additional-suffix
Use --additional-suffix=.txt to add extensions for easier identification. Especially useful for: .part, .log, .csv, or any extension that helps identify file type.
Combine with Compression
Use --filter to compress while splitting: split --filter='gzip > $FILE.gz'. Saves storage and can be faster than split-then-compress. Remember to decompress when reassembling.
Always Include Manifests
For critical data, create manifests with checksums and chunk details. Include reassembly instructions. Future you (or others) will thank you when restoration is needed.
Test Reassembly Before Deleting
Always test reassembly and verify with checksums before deleting original files. Use diff original reassembled or compare checksums to ensure perfect reconstruction.