# Bash programming/Function Usage

Functions allows you to group pieces of code and reuse it along your programs.

## Pre-requisites

Now is the time to note, while some concepts are assumed as a pre-requisite, it's reasonable to expect not every student will be familiar with all concepts. Therefore, where a concept is listed here in the pre-requisites, it will be introduced with a link to a more detailed explanation.
Environment:
* have completed the course Introduction
* use ENVIRONMENT variables
* stdin, stdout
* re-direct outputs
Commands:
* how to define a function
* expr -- does arithmetic,
* tail -- delivers last output lines
* history -- delivers command history

## New Concepts

commands:
* declare -- to view functions
* history -- to view command history
* set -- positional parameters
* source -- to re-use, or re-load functions
shell features
* positional parameters
* environment variables
* sub-shell execution

## Use function arguments

In this exercise, we will write two functions which use command arguments. The first function will display a function body or bodies. The second will display our command history.. It will have an optional single argument, a number, which will default to the number of LINES, an Environment variable, in your terminal window. In the course of the exercise we will:
* use a single function argument
* usa a variable number of arguments
* use a default argument
* use a function to display our function repertoire.
View a function body or bodies:
First, from our last section, recall:
Does your output meet your expectations. If this is the first time you've tried it, you will see something different than you entered.
Let's write a function to do this:
Now you are less surprised by the output. What's the $* doing? It says "when this command (or function) is executed, put all the blank-separated positional parameters right here". You observe the function is little different from using the built-in command declare with it's -f option. Like an alias, but the function allows upward-compatible evolution. In this case, a version of fbdy may return one-line functions as one-liners, another may routinely add (or remove) function tracing to the function body.
Make sure you've executed the examples above.

## Use command history

How many lines is our terminal displaying?
An Environment variable, LINES is often set by the shell to the number of lines the terminal window is displaying.
If you see nothing, then variable is not set on your terminal. We'll supply a default. It is useful to define a function, again almost an alias th, standing for Tail History. The reason for this function goes a bit beyond our needs here. However it's useful as a short-hand, if not not to provide a consistent interface to different shell's with a different sets of options for a single command.
execute that command; if needed, look ahead to subshells here is a sample of results:
Notice a number of things about that command:
* number 713 is the last command itself
* thirty-one commands were displayed
Now, convert that into a function, and test it.
It turns out in the bash shell, history is a builtin, which shows a complete list of bash history builtins here.
What happened here?
The set command (ksh version) in this usage assigns the positional parameters. If the function is used with one, then its done like this:
If no argument is used, then it becomes
but if LINES was say, 34, then it's expr 34 - 3 (= 31) ... history | tail -31
The shell parameter substitution works for named variables (LINES, SHELL, ...) as well as the positional parameters (1, 2, ... *) and this expression is most useful to assign a default value, in this example:
And this last feature introduced here is the ability to insert sub-shell results in the command. The general idea is:
where the results of sub-shell command or commands... is inserted into the command ... In our case then, the result of the shell arithmetic is inserted:

## Display functions

To collect our new and useful work, lets' see what we have:
For example:
This is progress. Do your results compare?

## Save functions

You have at least two ways to keep a consistent set of functions available for your command line:
* save them in a local file
* save them to load when you login
We'll exercise both methods here. First the local file. You'll find that not all functions are needed in all instances.

### In a local file

Functions can be stored in any .sh file. To load the functions from that file, run the command . filename.sh.

### load at login

Functions can be stored in the file named .bashrc. It is typically located in the home directory (shortcut: ~/.bashrc).

## Examples

* Create a directory and enter it:
** mkcd { if [ ! -d "$@" ];then mkdir -p "$@" ;fi; cd "$@"; }
* Change the window title in a terminal emulator:
** window_title { printf "\033]0;$*\007"; }
* Prevent a hard drive that does not respond to hdparm from spinning down:
** spindisk { while : ; do (sudo dd if=/dev/$1 of=/dev/null iflag=direct ibs=4096 count=1; sleep 29); done }
** Requires root access to run due to block-level disk access.
** Adjust the time after sleep to just below your hard drive's default spin-down timeout.
** Exit with CTRL+C.
* Count the files in a directory: filecount { ls "$@" |wc -l; }
* Count the files in a directory and its subdirectories: filecount-total { find "$@" |wc -l; }
* Count the folders only: subfoldercount { find "$@" -type d |wc -l; }
* Search for files in a directory: findfile { find "$2" |grep -i "$1"; }
* Search text inside 7z (7-Zip) archives: 7zgrep { 7z e -so "$2" |grep -i "$1"; }
* Find the newest or the oldest file in a given directory:
** newestfile { find "$@" -type f -printf '%T+ %p\n' |sort |tail -n 1; }
** oldestfile { find "$@" -type f -printf '%T+ %p\n' |sort |head -n 1; }
* Add time stamps at the beginning of specified file names:
* Generate check sums of all files in the current or a specified directory:
* Compare the list of files between two directories:

### Working with multimedia

* Create a file list for the concatenation feature of ffmpeg from the find command:
** fffile { sed -r "s/(.*)/file '\1'/g"; }
** Example use with pipe: find DCIM/Camera/VID_20250312*.mp4 |fffile >>example.txt
* Access the concatenation feature of ffmpeg with only two parameters:
** ffconcat { ffmpeg -f concat -safe 0 -i "$1" -c copy "$2"; }
** Example use: ffconcat example.txt example.mp4
* Verify the integrity of multimedia files using its decoding mechanism, independently from the file system: fferror { ffmpeg -v error -i "$1" -f null - ;}
* Generate a table of video resolutions and framerates by processing the text generated by the mediainfo tool.
** mediainfotable { mediainfo "$@" |grep -v "Frame rate.*SPF" |grep -P "(name|Width|Height|Frame rate )" |tr '\n' ' ' |sed -r 's/FPS/FPS\n/g' |sed -r "s/( )+//g"; }
* Redact geolocation from video files before sharing for privacy. This will overwrite the specified video files in-place, so it is recommended to only use it on copies of video files.
** gpsnull { sed -i -r "s/\+[0-9][0-9]\.[0-9][0-9][0-9][0-9]\+[0-9][0-9][0-9]\.[0-9][0-9][0-9][0-9]\//+00.0000+000.0000\//g" "$@"; }
* Mute the audio of a video (specify input and output file): ffmute { ffmpeg -i "$1" -c:v copy -an "$2"; }
* Extract the audio from a video file intp a separate file (specify input and output file): ffaudio { ffmpeg -i "$1" -c:a copy -vn "$2"; }
* Find out the total size of a selection of files: totalsize { du -sh -c "$@" |tail -n 1; }
* Get the frame rate of a video: getfps { ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate "$1"; }
* Get the resolution of a video: getRes { echo $(ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=height "$1"; )p; }
* Stick all videos from one folder into one video file without re-encoding (only works for files from the same device with the same width and height and frame rate):
Example use: ffconcat-dir folder_name output_video_name.mp4
This works for other file types too, but not with mixed file types. The extension specified file type has to match the type of the source files. The default name is tmp.mp4 if none is specified.