Earlier within this chapter, we referred to functions as scripts within scripts and we will still maintain that analogy. Similar to how a script can have input parameters, we can create functions that also accept parameters that can make their operation less static. Before we work on a script, we can look at a useful function in the command line.
The GNU's Not Unix (GNU) Linux sed command can easily edit the file for us and remove commented lines and empty lines. We are introducing the stream editor, sed, here but we will look at it in more detail in the following chapter.
The sed command line that runs the in-place edit will be as follows:
$ sed -i.bak '/^\s*#/d;/^$/d' <filename>
We can run out forensics in the command line by breaking it down element by element. Let's take a deeper look:
- sed -i.bak: This edits the file and creates a backup with the extension .bak. The original file will then be accessible as <filename>.bak.
- /^: This caret character (^) means edit the lines that start with what after the caret. So the caret matches the beginning of a line.
- \s*: This means any amount of white space, including no spaces or tabs.
- #/: This is a normal # sign. So the total expression ^\s*# means we are looking for lines that begin with comment or spaces and a comment.
- d: This is the delete action to remove matching lines.
- ;/^$/d: The semicolon is used to separate expressions and the second expression is similar to the first but this time we are preparing to delete empty lines.
To move this into a function, we will simply need to think of a great name. I like to build verbs into function names; it helps with the uniqueness and identifies the purpose of the function. We will create the clean_file function as follows:
$ function clean_file { sed -i.bak '/^\s*#/d;/^$/d' "$1" }
As within scripts, we use positional parameters to accept command-line arguments. We can replace the hardcoded filename that we used previously with $1 within the function. We will quote this variable to protect against spaces within the filename. To test the clean_file function, we will make a copy of a system file and work with the copy. In this way, we can be sure that no harm comes to any system file. We can assure all readers that no system files were harmed during the making of this book. The following are the detailed steps we need to follow to perform the test on the new function:
- Create the clean_file function as described
- Move to your home directory using the cd command without arguments
- Copy the time configuration file to your home directory cp /etc/ntp.conf $HOME
- Count the number of lines in the file with the following command: wc -l $HOME/ntp.conf
- Now remove the commented and empty lines with clean_file $HOME/ntp.conf
- Now recount the lines using wc -l $HOME/ntp.conf
- Also, check the count the backup of the original file that we created: wc -l $HOME/ntp.conf.bak
The sequence of commands is shown in the following screenshot:
We can direct the attention of the function to the required file using the argument that was supplied while executing the function. If we need to persist this function, then we should add it to a login script. However, if we want to test this within a shell script, we can create the following file to do this and practice some of the other elements we have learned. We will need to take notice that the functions should always be created at the start of the script as they need to be stored in memory by the time they are called. Just think that your function needs to be unlocked and loaded before you pull the trigger.
We will create a new shell script, $HOME/bin/clean.sh, and the execute permission, as always, will need to be set. The code of the script is as follows:
#!/bin/bash # Script will prompt for filename # then remove commented and blank lines is_file() { if [ ! -f "$1" ] ; then echo "$1 does not seem to be a file" exit 2 fi } clean_file() { is_file "$1" BEFORE=$(wc -l "$1") echo "The file $1 starts with $BEFORE" sed -i.bak '/^\s*#/d;/^$/d' "$1" AFTER=$(wc -l "$1") echo "The file $1 is now $AFTER" } read -p "Enter a file to clean: " clean_file "$REPLY" exit 1
We have provided two functions within the script. The first, is_file, simply tests to ensure that the filename we have entered is a regular file. Then we declare the clean_file function with a little added functionality, displaying the line count of the file before and after the operation. We can also see that functions can be nested and we call the is_file function with clean_file.
Without the function definitions, we have only three lines of code at the end of the file, which we can see in the example code laid out in the previous code block that has been saved as $HOME/bin/clean.sh. We first prompt for the filename and then run the clean_file function, which in turn calls the is_file function. The simplicity of the main code is important here. The complexity is in the functions, as each function can be worked on as a standalone unit.
We can now test the script operation, first using a wrong filename, as we can see in the following screenshot:
Now that we have seen the operation with an incorrect file, we can try again using an actual file! We can use the same system file we worked on before. We need to first return the files to their original state:
$ cd $HOME $ rm $HOME/ntp.conf $ mv ntp.conf.bak ntp.conf
With the file now ready, we can execute the script from the $HOME directory as shown in the following screenshot: