File Handling in Bash Scripting

File Handling in Bash Scripting

When writing bash scripts, it's common to need to work with files and directories. This includes tasks like reading and writing file contents, checking if a file exists, renaming or deleting files, and getting file metadata like permissions and size.

This article will demonstrate bash scripting techniques for effectively handling files and directories with well-commented examples.

Reading Files

A common task is reading the contents of a file into a variable in your script. This allows you to store the file contents and process it later.

The cat command prints a file's contents to standard output. We can use command substitution to assign that output to a variable:

#!/bin/bash

# Store contents of the file in a variable
file_contents=$(cat file.txt)

echo "File contents: $file_contents"

This reads file.txt and assigns it to $file_contents. We echo the variable to print the contents.

For larger files, cat may be inefficient. An alternate is reading a file line-by-line with a while loop:

#!/bin/bash

# Read file line-by-line
while read line; do
  echo "$line" 
done < "input.txt"

This loops through each line of input.txt, assigning it to $line and echoing it.

Writing to Files

To write to a file, use the > redirect operator:

#!/bin/bash 

# Write output to file
echo "This is some text" > outfile.txt

This writes "This is some text" into outfile.txt, overwriting any existing content.

To append instead of overwrite, use >>:

#!/bin/bash

# Append output to the end of file 
echo "Appending this line" >> appendfile.txt

You can also use tee to write to a file and also print to standard out at the same time:

echo "This gets printed and logged" | tee logfile.txt

Checking if a File Exists

To avoid errors, check if a file exists before accessing it:

#!/bin/bash

# Check if file exists
if [ -f "file.txt" ]; then
  echo "File found" 
else
  echo "File not found"
fi

The -f flag for the if condition checks if the file exists and is a regular file. Other test flags include:

  • -d - Directory exists

  • -e - File exists (regular file or directory)

  • -s - File exists and size is greater than 0

Getting File Metadata

You can get useful metadata about files like size, permissions, etc.

For example, get a file's size in bytes:

#!/bin/bash

# Get file size
filesize=$(stat -c%s "file.txt")
echo "Size: $filesize bytes"

The stat command returns file info, and %s prints just the size.

To get a file's permissions:

#!/bin/bash

# Get file permissions
perms=$(stat -c%A "file.txt")
echo "Permissions: $perms"

%A prints the permissions in symbolic format. Other useful formats are %U for the user and %G for the group.

Renaming and Deleting Files

You can rename or delete files and directories from within scripts.

To rename a file:

#!/bin/bash

# Rename file 
mv oldname.txt newname.txt

To delete:

#!/bin/bash

# Delete file
rm filename.txt

And to remove a directory and all its contents recursively:

#!/bin/bash

# Delete directory recursively
rm -r directoryname

Other File Handling Commands

There are a few more useful bash commands that can be included when covering file handling in bash scripting:

  • cp - Copy files and directories. Useful for backing up files or setting up test data. Example:

      cp file.txt file_copy.txt
    
  • mkdir - Make new directories. Often needed to create output directories for scripts:

      mkdir /path/to/newdir
    
  • find - Search for files recursively matching given criteria. Helps locate files based on name, size, permissions, etc:

      find /home -name "*.txt"
    
  • head/tail - Read first or last N lines of a file. Useful for previewing file contents:

      head -n 10 file.txt 
      tail -n 5 file.txt
    
  • touch - Update file timestamps or create empty files. Can force a file update:

      touch file.txt
    
  • chmod - Change file permissions like make executable:

      chmod +x script.sh
    
  • gzip/gunzip - Compress and extract gzip files:

      gzip file.txt
      gunzip file.txt.gz
    
  • tar - Archive multiple files and dirs into a tar file:

      tar -cvf archive.tar /path/to/files
    
  • Reading CSV files:

      # Read CSV file line-by-line
      while IFS="," read -r col1 col2 col3; do
        echo "$col1 | $col2 | $col3"
      done < file.csv
    

    Use IFS (input field separator) to split CSV rows into columns.

  • Parse XML/JSON with jq:

      # Extract JSON value
      name=$(cat file.json | jq -r '.name')
    

    jq allows easy JSON parsing to extract values in scripts.

  • File globbing - Match patterns with * and ?:

      # Copy files matching pattern
      cp /path/*.txt /backup/
    
  • Check file contents with grep:

      # Grep for match in file
      if grep -q "some_text" file.txt; then
        echo "Found"
      fi
    

    Use grep to search file contents.

  • Create temp files and dirs:

      # Create temp file
      tmpfile=$(mktemp)
    

    mktemp makes disposable temp files.

  • Stdout/stderr redirection:

      # Redirect stdout and stderr 
      program >out.txt 2>err.txt
    

    Send stdout and stderr output to different files.

  • Named pipes for inter-process communication:

      # Read from named pipe
      mkfifo mypipe
      cat mypipe
    

    Multiple processes can communicate via named pipes.

These provide additional file and directory manipulation capabilities that can be useful in bash scripts. The key concepts remain using redirects, loops, command substitution, and conditional checks for robust file handling.

So in summary, bash provides many useful commands for reading, writing, checking files, getting metadata, renaming/deleting and more. Mastering file handling will enable you to write more powerful scripts.

The key is knowing which commands like cat, redirects, stat, mv, rm to use for different file tasks. Refer to this article for syntax examples.