Introduction to tail
The tail command is a fundamental Unix/Linux utility used to display the last part of files. It's essential for log file monitoring, real-time file watching, and extracting data from the end of files. Understanding tail thoroughly is crucial for system administration, debugging, and log analysis.
Key Concepts
- Default Behavior: Shows last 10 lines of a file
- Real-time Monitoring: Follow mode for live updates
- Multiple Files: Can display tails of several files simultaneously
- Byte-based Output: Can work with bytes instead of lines
- Integration: Often used in pipelines with other commands
1. Basic Usage
Displaying Last Lines
#!/bin/bash # Create a sample file seq 1 100 > numbers.txt # Basic tail - last 10 lines (default) tail numbers.txt # Specify number of lines tail -n 20 numbers.txt tail --lines=20 numbers.txt # Shorthand for 20 lines tail -20 numbers.txt # Note: no 'n' before number # Start from a specific line tail -n +50 numbers.txt # Show lines 50 to end # Show last N bytes tail -c 100 numbers.txt # Last 100 bytes tail --bytes=100 numbers.txt # Show from a specific byte tail -c +500 numbers.txt # From byte 500 to end # Multiple files tail file1.txt file2.txt file3.txt # With headers tail -v numbers.txt # Always show header tail -q numbers.txt # Never show header (quiet)
Common Examples
#!/bin/bash # View last 20 lines of a log file tail -20 /var/log/syslog # Combine with other commands tail -n 50 app.log | grep ERROR # Monitor multiple log files tail -f /var/log/nginx/access.log /var/log/nginx/error.log # Extract last 1000 lines and save tail -n 1000 large_log.txt > last_1000_lines.txt # View last 5 lines of multiple files with headers tail -v -n 5 file1.txt file2.txt file3.txt # Show last 1KB of a file tail -c 1024 bigfile.dat # Display last 10 lines and follow tail -10f app.log # Skip first 100 lines, show the rest tail -n +101 data.txt
2. Essential Options
Common Options Reference
#!/bin/bash # -n, --lines: Number of lines tail -n 50 file.txt tail --lines=50 file.txt # -c, --bytes: Number of bytes tail -c 1024 file.txt tail --bytes=1K file.txt # GNU extension (1 Kilobyte) # -f, --follow: Follow file as it grows tail -f logfile.log # -F: Follow with retry (for log rotation) tail -F /var/log/syslog # -q, --quiet: Never print headers tail -q file1.txt file2.txt # -v, --verbose: Always print headers tail -v file.txt # --pid: Stop following when process dies tail -f --pid=1234 logfile.log # -s, --sleep-interval: Sleep between checks tail -f -s 2 logfile.log # Check every 2 seconds # --retry: Keep trying if file becomes inaccessible tail --retry -f logfile.log # -z, --zero-terminated: Line delimiter is NUL, not newline tail -z file.txt # Combine options tail -fn 50 app.log tail -F -s 5 /var/log/system.log
Option Combinations
#!/bin/bash # Real-time monitoring with line limit tail -f -n 1000 app.log # Follow with custom sleep interval tail -f -s 0.5 logfile.log # Check every 0.5 seconds # Follow and truncate output tail -f app.log | while read line; do echo "[$(date)] $line" done # Multiple files with headers tail -v -f access.log error.log # Follow until process ends long_running_process & tail -f --pid=$! output.log # Follow with byte limit tail -f -c 1024 logfile.log # Retry on log rotation tail -F -n 0 /var/log/syslog # Start from end, retry
3. Real-time Monitoring with -f
Basic Following
#!/bin/bash # Simple follow mode tail -f application.log # Follow multiple files tail -f /var/log/{syslog,auth.log,mail.log} # Follow with timestamps tail -f logfile.log | while read line; do echo "$(date '+%Y-%m-%d %H:%M:%S') - $line" done # Colorize output while following tail -f app.log | awk ' /ERROR/ {print "\033[31m" $0 "\033[0m"; next} /WARN/ {print "\033[33m" $0 "\033[0m"; next} /INFO/ {print "\033[32m" $0 "\033[0m"; next} {print} ' # Follow and filter tail -f app.log | grep --line-buffered ERROR # Follow with notification on pattern tail -f logfile.log | while read line; do if echo "$line" | grep -q "ERROR"; then notify-send "Error detected" "$line" fi done Advanced Following Techniques
#!/bin/bash # Monitor log and trigger actions monitor_log() { local logfile="$1" local pattern="$2" local command="$3" tail -F "$logfile" | while read line; do if echo "$line" | grep -q "$pattern"; then echo "Pattern detected: $line" eval "$command" fi done } # Usage monitor_log "/var/log/auth.log" "Failed password" "echo 'Security alert!' | mail -s 'Alert' admin" # Follow with aggregation tail -f access.log | awk ' { ip[$1]++ if (ip[$1] > 100) { print "High traffic from:", $1 } }' # Follow multiple logs with labels tail -f /var/log/nginx/access.log /var/log/nginx/error.log | awk ' FILENAME ~ /access/ {print "[ACCESS]", $0} FILENAME ~ /error/ {print "[ERROR] ", $0} ' # Follow with rate limiting tail -f app.log | awk ' { current_time = systime() if (current_time - last_time > 60) { print "--- Minute summary ---" print "Lines:", count count = 0 last_time = current_time } count++ } ' 4. Working with Multiple Files
Displaying Multiple Files
#!/bin/bash # Basic multiple file display tail file1.txt file2.txt file3.txt # With custom line count for each tail -n 20 file1.txt file2.txt # Headers automatically included tail -v file1.txt file2.txt # Force headers tail -q file1.txt file2.txt # Suppress headers # Follow multiple files tail -f log1.log log2.log log3.log # Format output tail -q -n 5 file1.txt file2.txt | awk ' BEGIN {file=""} FILENAME != file {file=FILENAME; print "\n==> " file " <=="} {print} ' # Process each file separately for file in *.log; do echo "=== $file ===" tail -n 5 "$file" done # Combine tails into one tail -q -n 100 *.log > combined_last_100_lines.txt Comparing Multiple Files
#!/bin/bash # Compare tails of files compare_tails() { local files=("$@") local lines=10 for file in "${files[@]}"; do echo "=== $file ===" tail -n "$lines" "$file" echo done } # Show differences in file ends diff <(tail -n 50 file1.txt) <(tail -n 50 file2.txt) # Monitor multiple logs with context watch_logs() { local files=("$@") while true; do clear date for file in "${files[@]}"; do echo "--- $file ---" tail -n 20 "$file" echo done sleep 2 done } # Usage watch_logs /var/log/syslog /var/log/auth.log 5. Byte-based Operations
Using -c Option
#!/bin/bash # Last N bytes tail -c 100 file.txt tail --bytes=100 file.txt # Human-readable sizes (GNU extension) tail -c 1K file.txt # 1024 bytes tail -c 1M file.txt # 1048576 bytes tail -c 1G file.txt # 1073741824 bytes # From specific byte position tail -c +1000 file.txt # From byte 1000 to end # Extract binary data tail -c +513 -c 512 image.jpg # Extract 512 bytes from position 513 # Check file size and tail appropriately file_size=$(stat -c%s largefile.dat) bytes_to_show=$((file_size / 10)) # Show last 10% tail -c "$bytes_to_show" largefile.dat # Working with non-text files tail -c 1000 binary_file.bin | hexdump -C tail -c +1000 -c 500 binary_file.bin > chunk.bin # Combine with head to extract middle portion head -c 2000 file.bin | tail -c 500 # Bytes 1500-2000
Practical Byte Examples
#!/bin/bash # Extract footer from file extract_footer() { local file="$1" local footer_size="${2:-512}" tail -c "$footer_size" "$file" > footer.bin echo "Extracted $footer_size bytes footer" } # Check file signature check_signature() { local file="$1" local expected_sig="$2" local sig=$(tail -c 4 "$file" | hexdump -v -e '4/1 "%02x"') if [ "$sig" = "$expected_sig" ]; then echo "Signature matches" else echo "Signature mismatch: $sig != $expected_sig" fi } # Split file into header and footer split_file() { local file="$1" local header_size="${2:-1024}" local total_size=$(stat -c%s "$file") local footer_size=$((total_size - header_size)) head -c "$header_size" "$file" > "${file}.header" tail -c "$footer_size" "$file" > "${file}.footer" echo "Split $file into header and footer" } # Show file encoding detection from end tail -c 1000 file.txt | file - 6. Integration with Other Commands
Piping with tail
#!/bin/bash # Filter tail output tail -f app.log | grep ERROR # Process tail output line by line tail -n 1000 access.log | awk '{print $1}' | sort | uniq -c | sort -rn # Count occurrences in tail tail -n 1000 app.log | grep -c ERROR # Transform tail output tail -f logfile.log | sed 's/ERROR/\x1b[31m&\x1b[0m/g' # Multiple pipes tail -n 5000 bigfile.txt | sort | uniq | head -20 # Use with xargs tail -n 100 filelist.txt | xargs ls -la # Create pipeline for real-time analysis tail -f access.log | awk ' {requests[$7]++} {print "\033[2J\033[H"; for (r in requests) printf "%s: %d\n", r, requests[r]} ' # Monitor and alert tail -f logfile.log | while read line; do if echo "$line" | grep -q "CRITICAL"; then echo "$line" | mail -s "Critical Alert" [email protected] fi done Using with Other Commands
#!/bin/bash # Find most recent files and show their tails find . -name "*.log" -type f -mmin -60 | xargs tail -n 50 # Watch directory for new files and tail them inotifywait -m /var/log -e create | while read path action file; do if [[ "$file" =~ \.log$ ]]; then tail -f "$path/$file" & fi done # Combine with watch watch -n 5 'tail -n 20 /var/log/syslog' # Use with ssh to monitor remote logs ssh user@server "tail -f /var/log/app.log" # Monitor multiple remote logs for host in server1 server2 server3; do ssh "$host" "tail -f /var/log/app.log" | sed "s/^/$host: /" & done # Aggregate logs from multiple sources tail -f /var/log/*.log | awk '{print strftime("%Y-%m-%d %H:%M:%S"), $0}' 7. Working with Compressed Files
Using with Compression Tools
#!/bin/bash # View end of compressed log gzcat access.log.gz | tail -n 50 zcat access.log.gz | tail -n 50 # Follow compressed file (uncompress on the fly) while true; do gzcat logfile.gz | tail -n 1 sleep 60 done # Tail multiple compressed files for file in *.gz; do echo "=== $file ===" zcat "$file" | tail -n 10 done # Monitor compressed log as it grows tail_compressed() { local file="$1" local lines="${2:-10}" if [[ "$file" =~ \.gz$ ]]; then zcat "$file" | tail -n "$lines" else tail -n "$lines" "$file" fi } # Tail rotated logs with compression tail_rotated() { local base="$1" local lines="${2:-50}" # Get current log if [ -f "$base" ]; then tail -n "$lines" "$base" fi # Check compressed rotated logs for log in "$base".[0-9]*.gz; do if [ -f "$log" ]; then echo "=== $log ===" zcat "$log" | tail -n "$lines" fi done } # Usage tail_rotated "/var/log/syslog" 20 8. Performance and Large Files
Efficient Large File Handling
#!/bin/bash # For very large files, tail is efficient (doesn't read whole file) tail -n 1000000 hugefile.txt > last_million.txt # Time comparison time tail -n 1000 largefile.txt > /dev/null time head -n -1000 largefile.txt > /dev/null # Negative head not standard # Use with dd for binary files dd if=largefile.dat bs=1M skip=$((total_mb - 10)) 2>/dev/null | tail -c 1M # Check file size before tailing file_size=$(stat -c%s largefile.txt) if [ $file_size -gt 100000000 ]; then echo "File is very large, tailing last 1000 lines only" tail -n 1000 largefile.txt else cat largefile.txt fi # Memory-efficient processing tail -f hugefile.log | while read line; do # Process one line at a time process_line "$line" done # Limit memory usage with buffer tail -n 100000 hugefile.txt | split -l 10000 - chunk_
Performance Optimization
#!/bin/bash # Benchmark different approaches benchmark_tail() { local file="testfile.dat" local size="100M" # Create test file dd if=/dev/zero of="$file" bs=1M count=100 2>/dev/null echo "=== Tail Performance ===" time tail -n 1000 "$file" > /dev/null time tail -c 1M "$file" > /dev/null # Compare with alternatives time sed -n '9000,10000p' "$file" > /dev/null 2>&1 time awk 'NR>9000' "$file" > /dev/null 2>&1 rm "$file" } # Use appropriate buffer sizes export _POSIX2_LINE_MAX=4096 # For better line handling # Avoid unnecessary pipes for large files # Slow tail -n 1000000 large.log | grep ERROR # Fast grep ERROR large.log | tail -n 1000000 9. Script Examples
Log Monitoring Script
#!/bin/bash # Comprehensive log monitor LOG_DIR="/var/log" ALERT_EMAIL="[email protected]" TMP_FILE="/tmp/log_monitor.$$" # Configuration declare -A LOG_PATTERNS=( ["error"]="ERROR|Error|error|Failed|failed" ["auth"]="Failed password|authentication failure|Invalid user" ["conn"]="Connection refused|timeout|unreachable" ["disk"]="No space left|I/O error|disk full" ) declare -A LOG_ACTIONS=( ["error"]="send_alert" ["auth"]="log_security" ["disk"]="check_disk" ) monitor_logs() { local log_files=("$@") for log in "${log_files[@]}"; do if [ ! -f "$log" ]; then echo "Warning: $log not found" continue fi echo "Monitoring: $log" tail -F "$log" | while read line; do timestamp=$(date '+%Y-%m-%d %H:%M:%S') # Check each pattern for pattern_name in "${!LOG_PATTERNS[@]}"; do pattern="${LOG_PATTERNS[$pattern_name]}" if echo "$line" | grep -qE "$pattern"; then handle_match "$pattern_name" "$log" "$line" "$timestamp" fi done done & pids[$log]=$! done # Wait for all tail processes for pid in "${pids[@]}"; do wait "$pid" done } handle_match() { local pattern="$1" local log="$2" local line="$3" local timestamp="$4" echo "[$timestamp] $pattern detected in $log: $line" # Execute action if defined if [ -n "${LOG_ACTIONS[$pattern]}" ]; then "${LOG_ACTIONS[$pattern]}" "$pattern" "$log" "$line" "$timestamp" fi } send_alert() { local pattern="$1" local log="$2" local line="$3" local timestamp="$4" cat << EOF | mail -s "Alert: $pattern in $log" "$ALERT_EMAIL" Time: $timestamp File: $log Pattern: $pattern Line: $line EOF } log_security() { local line="$3" echo "$line" >> /var/log/security_alerts.log } check_disk() { df -h | mail -s "Disk Alert" "$ALERT_EMAIL" } # Main log_files=( "/var/log/syslog" "/var/log/auth.log" "/var/log/nginx/error.log" "/var/log/mysql/error.log" ) monitor_logs "${log_files[@]}" # Cleanup on exit trap 'kill ${pids[@]} 2>/dev/null' EXIT
Log Rotation Helper
#!/bin/bash # Helper script for managing log rotation with tail ROTATE_DIR="/var/log/archive" CURRENT_LOG="/var/log/application.log" monitor_with_rotation() { local log="$1" local rotate_after="${2:-86400}" # 24 hours default local check_interval=60 echo "Monitoring $log with rotation every $rotate_after seconds" while true; do start_time=$(date +%s) # Start tail in background tail -F "$log" & tail_pid=$! # Wait for rotation interval sleep "$rotate_after" # Rotate log if [ -f "$log" ]; then archive_name="$ROTATE_DIR/$(basename "$log").$(date +%Y%m%d-%H%M%S)" mv "$log" "$archive_name" touch "$log" echo "Rotated $log to $archive_name" fi # Kill old tail and restart kill "$tail_pid" 2>/dev/null wait "$tail_pid" 2>/dev/null done } # Monitor with automatic reconnection tail_with_retry() { local file="$1" local retry_interval="${2:-5}" while true; do if [ -f "$file" ]; then echo "Tailing $file (PID: $$)" tail -F "$file" else echo "Waiting for $file to appear..." fi sleep "$retry_interval" done } # Usage # monitor_with_rotation "$CURRENT_LOG" 3600 # tail_with_retry "/var/log/intermittent.log" 10. Edge Cases and Error Handling
Common Issues and Solutions
#!/bin/bash # Issue: File doesn't exist # Solution: Check existence and use -F for retry if [ -f "app.log" ]; then tail -f app.log else echo "Log file not found, waiting..." tail -F app.log 2>/dev/null || sleep 5 fi # Issue: Permission denied # Solution: Check permissions and use sudo if needed if [ -r "/var/log/syslog" ]; then tail -f /var/log/syslog else sudo tail -f /var/log/syslog fi # Issue: Binary file warnings # Solution: Use -q to suppress warnings tail -q binary.dat 2>/dev/null # Issue: Very long lines # Solution: Adjust buffer or use other tools export _POSIX2_LINE_MAX=4096 tail -f app.log | cut -c1-200 # Issue: File truncated while following # Solution: Use -F which handles truncation tail -F /var/log/application.log # Issue: No newline at end of file # Solution: tail handles this gracefully tail -n 1 file_without_newline.txt # Issue: Race conditions in log rotation # Solution: Use -F and handle signals tail_with_rotation() { local file="$1" while true; do tail -F "$file" & TAIL_PID=$! inotifywait -e move_self "$file" 2>/dev/null kill "$TAIL_PID" wait "$TAIL_PID" 2>/dev/null done } Error Handling in Scripts
#!/bin/bash # Robust tail wrapper safe_tail() { local file="$1" local lines="${2:-10}" local follow="${3:-false}" # Validate input if [ -z "$file" ]; then echo "Error: No file specified" >&2 return 1 fi # Check if file exists if [ ! -e "$file" ]; then echo "Error: File $file does not exist" >&2 return 1 fi # Check if readable if [ ! -r "$file" ]; then echo "Error: Cannot read $file (permission denied)" >&2 return 1 fi # Check if it's a regular file if [ ! -f "$file" ] && [ ! -p "$file" ]; then echo "Error: $file is not a regular file or pipe" >&2 return 1 fi # Execute tail with appropriate options if [ "$follow" = "true" ]; then tail -F -n "$lines" "$file" else tail -n "$lines" "$file" fi } # Usage with error handling if ! safe_tail "/var/log/app.log" 50 true; then echo "Failed to tail log file" >&2 exit 1 fi # Monitor with timeout timeout_tail() { local file="$1" local timeout="${2:-30}" local lines="${3:-10}" timeout "$timeout" tail -f -n "$lines" "$file" 2>/dev/null local exit_code=$? if [ $exit_code -eq 124 ]; then echo "Tail timed out after $timeout seconds" >&2 fi return $exit_code } # Retry logic tail_with_retry() { local file="$1" local max_retries="${2:-5}" local retry_count=0 while [ $retry_count -lt $max_retries ]; do if [ -f "$file" ] && [ -r "$file" ]; then tail -F "$file" return 0 fi retry_count=$((retry_count + 1)) echo "Attempt $retry_count: $file not accessible, retrying in 5s..." >&2 sleep 5 done echo "Max retries reached, giving up" >&2 return 1 } 11. Advanced Techniques
Custom Tail Implementations
#!/bin/bash # Pure Bash tail implementation bash_tail() { local file="$1" local nlines="${2:-10}" local lines=() if [ ! -f "$file" ]; then echo "File not found: $file" >&2 return 1 fi while IFS= read -r line; do lines+=("$line") if [ ${#lines[@]} -gt $nlines ]; then lines=("${lines[@]:1}") fi done < "$file" printf '%s\n' "${lines[@]}" } # Efficient tail for large files (seek to end) seek_tail() { local file="$1" local lines="${2:-10}" local block_size=512 local file_size=$(stat -c%s "$file" 2>/dev/null) local position=$file_size local line_count=0 local output="" while [ $position -gt 0 ] && [ $line_count -lt $lines ]; do position=$((position - block_size)) [ $position -lt 0 ] && position=0 output=$(dd if="$file" bs=1 skip="$position" 2>/dev/null) line_count=$(echo "$output" | wc -l) done echo "$output" | tail -n "$lines" } # Follow implementation in pure Bash bash_follow() { local file="$1" local last_size=0 while true; do if [ -f "$file" ]; then current_size=$(stat -c%s "$file" 2>/dev/null) if [ $current_size -gt $last_size ]; then dd if="$file" bs=1 skip="$last_size" 2>/dev/null last_size=$current_size fi fi sleep 1 done } # Usage # bash_tail "/var/log/syslog" 20 # bash_follow "/var/log/app.log" Pattern-based Tail
#!/bin/bash # Tail until pattern tail_until_pattern() { local file="$1" local pattern="$2" local lines=() tail -f "$file" | while read line; do echo "$line" if echo "$line" | grep -q "$pattern"; then echo "Pattern found, stopping." kill $$ break fi done } # Tail from pattern tail_from_pattern() { local file="$1" local pattern="$2" local found=0 tail -f "$file" | while read line; do if [ $found -eq 1 ]; then echo "$line" elif echo "$line" | grep -q "$pattern"; then found=1 echo "Found pattern, showing from here:" echo "$line" fi done } # Context-aware tail tail_with_context() { local file="$1" local pattern="$2" local before="${3:-5}" local after="${4:-5}" # Get line numbers of matches matches=$(grep -n "$pattern" "$file" | cut -d: -f1) for line_num in $matches; do start=$((line_num - before)) [ $start -lt 1 ] && start=1 end=$((line_num + after)) echo "--- Context around line $line_num ---" sed -n "${start},${end}p" "$file" echo done } # Usage # tail_until_pattern "logfile.log" "Shutdown complete" # tail_from_pattern "logfile.log" "Initialization complete" # tail_with_context "app.log" "ERROR" 3 3 12. Best Practices and Tips
Shell Configuration
# ~/.bashrc additions # Aliases for common tail operations alias tl='tail -n 50' # Last 50 lines alias tlf='tail -f' # Follow alias tlf100='tail -f -n 100' # Follow last 100 alias tlF='tail -F' # Follow with retry alias tla='tail -f /var/log/*.log' # Follow all logs # Function to tail with grep tlg() { tail -f "$1" | grep --line-buffered "${2:-ERROR}" } # Function to tail multiple logs with labels tlmulti() { for file in "$@"; do tail -f "$file" | sed "s/^/[$file] /" & done wait } # Function to tail and timestamp tldate() { tail -f "$1" | while read line; do echo "$(date '+%Y-%m-%d %H:%M:%S') - $line" done } # Function to tail and highlight tlcolor() { tail -f "$1" | awk ' /ERROR/ {print "\033[31m" $0 "\033[0m"; next} /WARN/ {print "\033[33m" $0 "\033[0m"; next} /INFO/ {print "\033[32m" $0 "\033[0m"; next} {print} ' } # Function to tail last N lines of all files in directory tldir() { local dir="${1:-.}" local lines="${2:-20}" find "$dir" -type f -name "*.log" -print0 | while IFS= read -r -d '' file; do echo "=== $file ===" tail -n "$lines" "$file" echo done } # Complete with file names complete -f tlg tlmulti tldate tlcolor Productivity Tips
#!/bin/bash # 1. Monitor specific keywords in real-time tail -f app.log | grep --line-buffered -E 'ERROR|WARN|CRITICAL' # 2. Watch log with statistics tail -f access.log | awk ' {requests[$1]++} NR % 10 == 0 {system("clear"); for (ip in requests) print ip, requests[ip]} ' # 3. Monitor log and send email on pattern tail -f app.log | while read line; do if echo "$line" | grep -q "FATAL"; then echo "$line" | mail -s "Fatal error" [email protected] fi done # 4. Combine tail with other monitoring tools tail -f app.log | tee -a /var/log/app.log | logger -t app # 5. Monitor multiple servers for server in web{1..5}; do ssh "$server" "tail -f /var/log/nginx/access.log" | sed "s/^/$server: /" & done # 6. Create log summary every minute while true; do echo "--- $(date) ---" tail -n 100 app.log | grep ERROR | wc -l sleep 60 done # 7. Monitor disk usage while tailing tail -f app.log & watch -n 10 df -h # 8. Check if file is being written last_size=0 while true; do size=$(stat -c%s app.log 2>/dev/null) if [ "$size" != "$last_size" ]; then echo "File size changed: $size bytes" last_size=$size fi sleep 5 done Common Patterns and Recipes
#!/bin/bash # 1. Last N lines with line numbers cat -n file.txt | tail -n 20 # 2. Show last lines of multiple files with filename find . -name "*.log" -exec tail -n 20 {} \; -print # 3. Monitor new files in directory inotifywait -m /var/log -e create | while read dir action file; do echo "New log file: $file" tail -f "$dir/$file" & done # 4. Rotate log and continue tailing rotate_and_tail() { local log="$1" local backup="$log.$(date +%Y%m%d-%H%M%S)" cp "$log" "$backup" > "$log" echo "Rotated to $backup" tail -f "$log" } # 5. Tail with buffer flush for pipes tail -f app.log | stdbuf -oL grep ERROR | while read line; do echo "Found error: $line" done # 6. Check if file is growing check_growth() { local file="$1" local interval="${2:-60}" local threshold="${3:-1024}" size1=$(stat -c%s "$file" 2>/dev/null) sleep "$interval" size2=$(stat -c%s "$file" 2>/dev/null) growth=$((size2 - size1)) if [ $growth -lt $threshold ]; then echo "Warning: Slow log growth ($growth bytes/$interval sec)" fi } # 7. Tail and rotate based on size tail_and_rotate() { local file="$1" local max_size="${2:-100M}" tail -F "$file" | while read line; do echo "$line" size=$(stat -c%s "$file") if [ $size -gt $(numfmt --from=iec "$max_size") ]; then mv "$file" "$file.old" touch "$file" echo "Rotated log file (size exceeded $max_size)" fi done } # 8. Monitor specific time window monitor_time_window() { local file="$1" local start_time="$2" local end_time="$3" awk -v start="$start_time" -v end="$end_time" ' $0 ~ "^" start, $0 ~ "^" end {print} ' "$file" | tail -n 50 } Conclusion
The tail command is an essential tool for any system administrator or developer working with log files and text data:
Key Takeaways
- Default Behavior: Shows last 10 lines of files
- Line Control:
-nfor custom line counts - Byte Control:
-cfor byte-based operations - Follow Mode:
-ffor real-time monitoring - Multiple Files: Can display tails of several files
- Error Handling:
-Ffor robust log rotation handling - Performance: Efficient for large files
- Integration: Excellent for pipelines
- Scripting: Essential for log monitoring scripts
- Flexibility: Works with files, pipes, and special files
Best Practices Summary
- Use
-Finstead of-ffor production log monitoring (handles rotation) - Limit output with
-nto avoid overwhelming displays - Combine with grep for pattern filtering
- Use in pipelines for complex data processing
- Handle errors in scripts (permissions, missing files)
- Consider performance for very large files
- Use appropriate buffer sizes for real-time processing
- Clean up background processes in scripts
- Test with sample data before production
- Document complex pipelines for maintainability
The tail command's simplicity belies its power. When combined with other Unix tools and used in scripts, it becomes an indispensable part of system monitoring, log analysis, and data extraction workflows.
Complete Bash Exercises with Solutions
Practice Bash scripting with solved exercises to improve your command-line and scripting skills.
Link: https://macronepal.com/complete-bash-exercises-with-solutions/
Complete Guide to Bash Crontab Command
Learn how to schedule and automate tasks efficiently using the Bash crontab command.
Link: https://macronepal.com/complete-guide-to-bash-crontab-command-schedule-tasks/
Complete Guide to Bash Arrays
Understand how to create, manage, and use arrays in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-arrays/
Bash Quiz: Test Your Knowledge
Test your Bash scripting knowledge with quizzes and practical questions.
Link: https://macronepal.com/bash-quiz-test-your-knowledge/
Complete Guide to Bash Functions
Learn how to write reusable Bash functions for cleaner and efficient scripts.
Link: https://macronepal.com/complete-guide-to-bash-functions/
Complete Guide to Bash Loops
Master loops in Bash for repetitive tasks and automation.
Link: https://macronepal.com/complete-guide-to-bash-loops/
Complete Guide to Bash If-Else Statements
Learn decision-making in Bash using conditional statements.
Link: https://macronepal.com/complete-guide-to-bash-ifelse-statements/
Complete Guide to Bash Operators
Explore arithmetic, logical, and comparison operators in Bash.
Link: https://macronepal.com/complete-guide-to-bash-operators/
Complete Guide to Bash Data Types
Understand variables and different data types used in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-data-types/
Complete Guide to Bash Variables
Learn how to declare, assign, and use variables in Bash scripts.
Link: https://macronepal.com/complete-guide-to-bash-variables/
Complete Guide to Bash Scripting
A complete introduction to writing Bash scripts for automation.
Link: https://macronepal.com/complete-guide-to-bash-scripting/
Complete Guide to Basic Bash Syntax
Learn the fundamental syntax needed to write Bash commands and scripts.
Link: https://macronepal.com/complete-guide-to-basic-bash-syntax/
Complete Guide to Bash Chgrp Command
Learn how to change group ownership of files and directories.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership-2/
Complete Guide to Bash Chgrp Command (Alternate)
Another detailed explanation of Bash group ownership management.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership/
Complete Guide to Bash File Permissions and Ownership
Understand Linux file permissions and ownership management.
Link: https://macronepal.com/complete-guide-to-bash-file-permissions-and-ownership/
Complete Guide to Bash Chmod Command
Learn how to modify file permissions using chmod.
Link: https://macronepal.com/complete-guide-to-bash-chmod-command-change-file-permissions/
Complete Guide to Bash Chown Command
Change file ownership using the Bash chown command.
Link: https://macronepal.com/complete-guide-to-bash-chown-command-change-file-ownership/
Complete Guide to Bash SCP Command
Securely transfer files between systems using SCP.
Link: https://macronepal.com/complete-guide-to-bash-scp-command-secure-copy/
Complete Guide to Bash SSH Command
Learn secure remote access and server management using SSH.
Link: https://macronepal.com/complete-guide-to-bash-ssh-command/
Bash Wget Command Complete Guide
Download files from the internet using the wget command.
Link: https://macronepal.com/bash-wget-command-complete-guide-to-non-interactive-network-downloader/
