Customizing Linux File Listing

ShellShellBeginner
Practice Now

Introduction

In this project, you will learn how to create a custom ls command that displays the sizes of directories in a user-friendly format. This project will help you understand how to retrieve and format file information in a Linux environment using Zsh scripting.

๐Ÿ‘€ Preview

$ cd /home/labex
$ sh newls.sh /home/labex
## Example
drwxr-xr-x 1 labex 6B Oct 13 10:11 Code
drwxr-xr-x 1 labex 120B Oct 13 10:11 Desktop
drwxr-xr-x 1 labex 28B Sep 23 2021 golang
drwxr-xr-x 1 labex 22B Oct 23 10:17 project

๐ŸŽฏ Tasks

In this project, you will learn:

  • How to create a Zsh script to display file and directory information
  • How to format the file size for better readability
  • How to determine the appropriate date format for file modification times
  • How to output the file information in the same format as the ls -lh command

๐Ÿ† Achievements

After completing this project, you will be able to:

  • Write a Zsh script that can display the sizes of files and directories in a specified directory
  • Format the file size information for better readability
  • Determine the appropriate date format for file modification times based on the current year and the file's last modification time
  • Output the file information in a user-friendly format that matches the ls -lh command

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL shell(("`Shell`")) -.-> shell/ControlFlowGroup(["`Control Flow`"]) linux(("`Linux`")) -.-> linux/BasicFileOperationsGroup(["`Basic File Operations`"]) linux(("`Linux`")) -.-> linux/BasicSystemCommandsGroup(["`Basic System Commands`"]) linux(("`Linux`")) -.-> linux/InputandOutputRedirectionGroup(["`Input and Output Redirection`"]) linux(("`Linux`")) -.-> linux/TextProcessingGroup(["`Text Processing`"]) linux(("`Linux`")) -.-> linux/FileandDirectoryManagementGroup(["`File and Directory Management`"]) linux(("`Linux`")) -.-> linux/SystemInformationandMonitoringGroup(["`System Information and Monitoring`"]) shell(("`Shell`")) -.-> shell/BasicSyntaxandStructureGroup(["`Basic Syntax and Structure`"]) shell(("`Shell`")) -.-> shell/VariableHandlingGroup(["`Variable Handling`"]) shell(("`Shell`")) -.-> shell/AdvancedScriptingConceptsGroup(["`Advanced Scripting Concepts`"]) shell(("`Shell`")) -.-> shell/SystemInteractionandConfigurationGroup(["`System Interaction and Configuration`"]) shell/ControlFlowGroup -.-> shell/if_else("`If-Else Statements`") linux/BasicFileOperationsGroup -.-> linux/tail("`File End Display`") linux/BasicFileOperationsGroup -.-> linux/cut("`Text Cutting`") linux/BasicSystemCommandsGroup -.-> linux/exit("`Shell Exiting`") linux/BasicSystemCommandsGroup -.-> linux/echo("`Text Display`") linux/InputandOutputRedirectionGroup -.-> linux/pipeline("`Data Piping`") linux/InputandOutputRedirectionGroup -.-> linux/redirect("`I/O Redirecting`") linux/BasicSystemCommandsGroup -.-> linux/read("`Input Reading`") linux/BasicSystemCommandsGroup -.-> linux/printf("`Text Formatting`") linux/TextProcessingGroup -.-> linux/sed("`Stream Editing`") linux/TextProcessingGroup -.-> linux/awk("`Text Processing`") linux/BasicSystemCommandsGroup -.-> linux/bc("`Arithmetic Calculations`") linux/FileandDirectoryManagementGroup -.-> linux/cd("`Directory Changing`") linux/BasicFileOperationsGroup -.-> linux/ls("`Content Listing`") linux/SystemInformationandMonitoringGroup -.-> linux/date("`Date/Time Displaying`") linux/SystemInformationandMonitoringGroup -.-> linux/time("`Command Timing`") shell/BasicSyntaxandStructureGroup -.-> shell/shebang("`Shebang`") shell/BasicSyntaxandStructureGroup -.-> shell/comments("`Comments`") shell/BasicSyntaxandStructureGroup -.-> shell/quoting("`Quoting Mechanisms`") shell/VariableHandlingGroup -.-> shell/variables_decl("`Variable Declaration`") shell/VariableHandlingGroup -.-> shell/variables_usage("`Variable Usage`") shell/VariableHandlingGroup -.-> shell/str_manipulation("`String Manipulation`") shell/VariableHandlingGroup -.-> shell/param_expansion("`Parameter Expansion`") shell/ControlFlowGroup -.-> shell/for_loops("`For Loops`") shell/ControlFlowGroup -.-> shell/while_loops("`While Loops`") shell/ControlFlowGroup -.-> shell/cond_expr("`Conditional Expressions`") shell/ControlFlowGroup -.-> shell/exit_status("`Exit and Return Status`") shell/AdvancedScriptingConceptsGroup -.-> shell/arith_ops("`Arithmetic Operations`") shell/AdvancedScriptingConceptsGroup -.-> shell/arith_expansion("`Arithmetic Expansion`") shell/AdvancedScriptingConceptsGroup -.-> shell/read_input("`Reading Input`") shell/AdvancedScriptingConceptsGroup -.-> shell/cmd_substitution("`Command Substitution`") shell/AdvancedScriptingConceptsGroup -.-> shell/subshells("`Subshells and Command Groups`") shell/AdvancedScriptingConceptsGroup -.-> shell/adv_redirection("`Advanced Redirection`") shell/AdvancedScriptingConceptsGroup -.-> shell/here_strings("`Here Strings`") shell/SystemInteractionandConfigurationGroup -.-> shell/globbing_expansion("`Globbing and Pathname Expansion`") linux/FileandDirectoryManagementGroup -.-> linux/wildcard("`Wildcard Character`") subgraph Lab Skills shell/if_else -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/tail -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/cut -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/exit -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/echo -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/pipeline -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/redirect -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/read -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/printf -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/sed -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/awk -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/bc -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/cd -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/ls -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/date -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/time -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/shebang -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/comments -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/quoting -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/variables_decl -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/variables_usage -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/str_manipulation -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/param_expansion -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/for_loops -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/while_loops -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/cond_expr -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/exit_status -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/arith_ops -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/arith_expansion -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/read_input -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/cmd_substitution -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/subshells -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/adv_redirection -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/here_strings -.-> lab-301467{{"`Customizing Linux File Listing`"}} shell/globbing_expansion -.-> lab-301467{{"`Customizing Linux File Listing`"}} linux/wildcard -.-> lab-301467{{"`Customizing Linux File Listing`"}} end

Create the newls.sh Script

In this step, you will create the newls.sh script that will display the sizes of all the files and directories in a specified directory. Follow the steps below to complete this step:

  1. Open a text editor and create a new file named newls.sh in the /home/labex/project directory.
  2. Add the following shebang line at the beginning of the file to specify that this is a Zsh script:
#!/bin/zsh
  1. Add a comment block at the beginning of the script to explain its purpose:
## This script retrieves and formats file information from a specified directory, displaying it in the same format as the "ls -lh" command.
  1. Now, add the code to check if the input parameter (the directory to list) is provided. If not, display a usage message and exit the script:
## Check if the input parameter is empty
if [ $## -eq 0 ]; then
  echo "Usage: sh newls.sh <directory>"
  exit 1
fi
  1. Next, add code to check if the specified directory exists. If not, display an error message and exit the script:
## Check if the directory exists
if [ ! -d "$1" ]; then
  echo "Directory $1 does not exist."
  exit 1
fi

Retrieve and Format File Information

In this step, you will add the code to retrieve the file and directory information from the specified directory and format the output in the same way as the ls -lh command. Follow the steps below to complete this step:

  1. Use the ls command to retrieve the detailed information about the files and directories in the specified directory, and store the output in the ls_output variable:
## Use the ls command to retrieve detailed information about files and directories, then process the output line by line
ls_output=$(ls -l --time-style="+%b %d %Y %H:%M" "$1" | tail -n +2)
  1. Add a while loop to process each line of the ls_output and extract the necessary information:
## Process each line of the ls output
while IFS= read -r line; do
  ## Remove trailing whitespace from each line
  line=$(echo "$line" | sed 's/[[:space:]]*$//')

  ## Parse each field of the line
  permissions=$(echo "$line" | awk '{print $1}')
  link_count=$(echo "$line" | awk '{print $2}')
  owner=$(echo "$line" | awk '{print $3}')
  size=$(echo "$line" | awk '{print $5}')
  month=$(echo "$line" | awk '{print $6}')
  day=$(echo "$line" | awk '{print $7}')
  year=$(echo "$line" | awk '{print $8}')
  time=$(echo "$line" | awk '{print $9}')
  name=$(echo "$line" | awk '{print $10}')
  1. Add code to format the file size for better readability:
## Format the file size for better readability
if [[ $size =~ ^[0-9]+$ ]]; then
  ## If the size is a number, format it
  if ((size < 1024)); then
    size_formatted="${size}B"
  elif ((size < 1024 ** 2)); then
    size_formatted="$(printf "%.1f" $(echo "scale=2; $size / 1024" | bc))K"
  elif ((size < 1024 ** 3)); then
    size_formatted="$(printf "%.1f" $(echo "scale=2; $size / (1024**2)" | bc))M"
  else
    size_formatted="$(printf "%.1f" $(echo "scale=2; $size / (1024**3)" | bc))G"
  fi
else
  ## If the size cannot be parsed as a number, keep it as is
  size_formatted="$size"
fi
  1. Add code to determine the appropriate date format based on the file's modification time:
current_year=$(date +"%Y")

## Extract year, month, and day from the modified date
file_year=$(echo "$year" | cut -d' ' -f3)
file_month=$(echo "$month" | cut -d' ' -f1)
file_day=$(echo "$day" | cut -d' ' -f1)

## Convert leading zero in month to decimal format
file_month=$(echo "$file_month" | sed 's/^0//')

## Get the timestamp of the file's last modification
file_modified=$(date -d "$month $day $year" +"%s")

## Get the timestamp of 6 months ago
six_months_ago=$(date -d "6 months ago" +"%s")

## Calculate the difference in seconds between the current time and the file's last modification
time_diff=$(($(date +"%s") - file_modified))

if ((file_year == current_year)); then
  ## If the file's year is the same as the current year
  if ((time_diff >= six_months_ago)); then
    ## More than 6 months ago, display the full date: Month Day Year
    formatted_date="$month $day $year"
  else
    ## Within the last 6 months, display the date and time: Month Day HH:MM
    formatted_date="$month $day $time"
  fi
else
  ## If the file's year is different from the current year, display the full date: Month Day Year
  formatted_date="$month $day $year"
fi
  1. Finally, add the code to output the formatted file information:
  ## Output format includes customized date formatting based on the condition
  printf "%-12s %-5s %-8s %-8s %s %s %-15s %s\n" "$permissions" "$link_count" "$owner" "$size_formatted" "$formatted_date" "$name"
done <<< "$ls_output"

The complete newls.sh script is now ready. You can save the file and move on to the next step.

Test the newls.sh Script

In this step, you will test the newls.sh script to ensure it works as expected. Follow the steps below to complete this step:

  1. Open a terminal and navigate to the /home/labex/project directory.
  2. Run the newls.sh script with the /home/labex directory as the argument:
sh newls.sh /home/labex
  1. Verify that the output matches the expected format, as shown in the challenge description:
drwxr-xr-x 1 labex 6B Oct 13 10:11 Code
drwxr-xr-x 1 labex 120B Oct 13 10:11 Desktop
drwxr-xr-x 1 labex 28B Sep 23 2021 golang
drwxr-xr-x 1 labex 22B Oct 23 10:17 project
  1. If the output matches the expected format, your newls.sh script is working correctly. If not, review the script and make any necessary corrections.

Congratulations! You have successfully completed the project and created a custom ls command that displays the sizes of directories in a user-friendly format.

Summary

Congratulations! You have completed this project. You can practice more labs in LabEx to improve your skills.

Other Shell Tutorials you may like