Documentation for the functions in filesystem.sh. A general overview is given in the project documentation.
- copy_file(), copy_folder() (+ the internal handler handle_cp_or_mv())
- create_folder()
- get_real_path()
- get_existing_path_part()
- get_new_path_part()
- get_script_path()
- is_path_a()
- is_readable()
- is_writeable()
- load_configuration_file_value()
- move_file() and move_folder() (+ the internal handler handle_cp_or_mv())
- remove_file() and remove_folder() (+ the internal handler - handle_rm())
- try_filepath_deduction()
If the pipes are not documented, the default is:
stdin
: ignoredstdout
: empty
Parameters enclosed in brackets [ ] are optional.
The function processes the path $1
in 4 ways:
- if it's a relative path, it's transformed to the absolute equivalent
- symbolic file links are resolved, even if they are chained (i.e. a link pointing to a link pointing to a link etc.)
- symbolic folder links are resolved using
cd
's-P
flag - it cleans up ../ and ./ components
It works for files and folders with the restriction that they must exist. Use the string handling collection's get_absolute_path() for paths that don't exist.
Param. | $1 | path to resolve and clean |
Pipes | stdout | if status is 0, the "real" path of $1 , empty otherwise |
Status | 0 | success, "real" path written on stdout |
1 | $1 doesn't exist |
Inspired by this StackOverflow(SO) answer.
The function returns the full path including the filename and it's able to work in any call constellation: sourced, called in a subshell etc.
It relies on $BASH_SOURCE
which changes depending on the constellation, however, the element in this array with the highest index is always the path of the script
executed initially.
Important: call this function as early as possible; before any directory changes in the script. The $BASH_SOURCE
entry depends on the way the script is called and one of the
possibilities is that the script is executed in a terminal using a relative filepath with respect to the shell's current directory; in that case the $BASH_SOURCE
entry only contains that relative filepath and if the current directory changes, the output of this function is wrong.
Pipes | stdout | "real" absolute path (folder + file symlink resolved, cleaned) of the executed script |
Status | 0 |
Combined existence and type check. Example:
is_path_a "$path" "file" || echo "$path is not a file" && return
# do something with the file at $path...
Param. | $1 | path |
$2 | inode type: accepted values are folder, file or symlink | |
Status | 0 | path $1 is of type $2 |
1 | path $1 is not of type $2 | |
2 | path $1 doesn't exist | |
3 | $1 is empty | |
4 | $2 is empty | |
5 | $2 is unknown |
Helpers to avoid read permission errors, with an optional additional internal is_path_a() type check. A typical use example is:
is_readable "$path" || echo "Path $path is not readable. Aborting..." && exit
Param. | $1 | path |
[$2 ] | inode type: accepted values are folder, file or symlink. If the value is omitted or empty, no type check is performed. | |
Status | 0 | path $1 is readable |
1 | path $1 is not readable | |
2 | path $1 doesn't exist | |
3 | $1 doesn't have type $2 | |
4 | $1 is empty | |
5 | $2 is unknown |
Helper to avoid write permission errors. Example:
is_writeable "$path" || echo "Path $path is not writeable. Aborting..." && exit
The function has a "check on existing path part" flag which configures the behavior for filepaths that don't exist on the system (yet). In
these cases the answer whether a write will succeed or not depends on the type of operation and whether it requires the direct parent folder to
exist or not. mkdir
is a good example - let's imagine there's a empty directory /test
where the user has write permission and wants to create
the path /test/folder/subfolder
:
mkdir /test/folder/subfolder
will fail because/test/folder
doesn't existmkdir -p /test/folder/subfolder
works because the-p
tellsmkdir
it's allowed to create multiple nested directories and since writing to/test
is permitted, the operation is successful
This function is able to take this into account:
- if the "check on existing path part" flag
$2
is not raised it fails if the direct parent folder doesn't exist - if the flag is raised (
$2
set to 1), it does what its name indicates: it walks up the path until it finds an existing directory and checks the write permission on that directory.
In the mkdir
example above, is_writeable /test/folder/subfolder
would return status 2 (= not writeable, parent missing); with the flag ($2
set to 1),
that switches to status 0 (= writeable).
Param. | $1 | path |
[$2 ] | "check on existing path part" flag - if the parent directory of $1 doesn't exist, it
configures whether the function fails or if it walks up $1 until it finds an existing folder on which it checks the write
permission - see explanations above
| |
Status | 0 | path $1 can be written |
1 | there's no write permission on path $1 | |
2 | the direct parent folder of path $1 doesn't exist (can only happen if $2 is omitted or set to 0)
| |
3 | if $1 is empty |
Extracts the part of $1
which does not exist on the filesystem.
Param. | $1 | path |
Pipes | stdout | part of $1 which does not exist on the filesystem |
Status | 0 |
Extracts the part of $1
which exists on the filesystem. Returns "at least" /
Param. | $1 | path |
Pipes | stdout | part of $1 which exists on the filesystem |
Status | 0 |
If there's only a single file (match) in the folder $1
, returns its path
Param. | $1 | path of the folder to search in |
[$2 ] | search pattern, if omitted, defaults to * (= everything) | |
Pipes | stdout | if status is 0, the absolute filepath of the single match, empty otherwise |
Status | 0 | successful deduction, path is written on stdout |
1 | folder $1 doesn't exist | |
2 | there's no match for $2 in $1 | |
3 | there's more than 1 match for $2 in $1 |
mkdir
wrapper with:
- several checks before the actual creation attempt which allow to get specific status codes for any possible error type: if the path is empty (status 2), exists (3) or if the user has no write permission (4)
- control over
stdout
andstderr
:mkdir
writes onstderr
in case of failure. This function allows to be sure:- that
stdout
contains either nothing, themkdir
status code orstderr
message, or a custom message, depending on the the stdout configuration$2
- that
stderr
remains silent, even in case ofmkdir
failure
- that
- a system for the message customization: one template by status, with variable placeholders to inject the runtime parameters
Verbose mode / message customization:
The variable placeholders %path
and %stderr_msg
are replaced by the path $1
and the mkdir
error message. Latter is only relevant if
status is 1 (mkdir
error), otherwise it should be empty. The default message templates are:
Status | Template |
---|---|
0 | folder %path created\n |
1 | %stderr_msg \n |
2 | folder creation error: no path provided\n |
3 | folder creation error: %path exists\n |
4 | folder creation error: no write permission for %path \n |
%stderr_msg
is empty if status is not 1.
create_folder "/new/folder/path" "verbose"
prints folder /new/folder/path created\n in case of success. The messages can
be customized by setting up an array variable where the indizes are the states and the values the corresponding templates (i.e. the success message
template is at index 0, etc.). The name of the array variable - and not the variable itself - has to be provided as 3rd call parameter.
It's perfectly valid to customize a subset of states/templates, the function falls back to the default templates where it can't find a
customization. In the next example, the success message template is overwritten:
msg_defs[0]="custom message: folder %path created\n"
create_folder "new/folder/path" "verbose" "msg_defs"
prints custom message: folder /new/folder/path/ created\n if it's successful.
Examples:
stdout
silent:create_folder "path/to/new/dir"
- status code captured to variable:
status=$(create_folder "/path/to/my_new_dir" "status")
mkdir
error message captured to variable:error_msg=$(create_folder "/path/to/my_new_dir" "error_message")
Param. | $1 | path |
[$2 ] | stdout configuration:
| |
[$3 ] | if $2 is set to verbose, the name of the array variable which contains
the custom message templates - if omitted, the default templates are used; see explanations above | |
Pipes | stdout |
|
Status | 0 | folder $1 created |
1 | mkdir error, if $2 is set to error_message, stdout
contains the content of mkdir 's stderr output | |
2 | path $1 exists | |
3 | path $1 is not writeable | |
4 | $1 is empty |
Internal handler for file/folder copy/move, used by the wrapper functions copy_file(), copy_folder(), move_file() and move_folder(). See their documentation for details.
Param. | $1 | mode, possible values: copy, cp, move or mv |
$2 | source path | |
$3 | destination path | |
[$4 ] | stdout configuration:
| |
[$5 ] | if $4 is set to verbose, the name of the array variable which contains the
custom message templates - if omitted, the default templates are used, see exaplanation in the wrapper functions | |
Pipes and status are documented below for the wrapper functions. handle_cp_or_mv() has just one additional status which will
never occur if the wrapper functions are used: status 7 to signal mode $1 is unknown |
cp
and mv
wrapper with:
- several checks before the actual copy/move attempt which allow to get specific status codes for any possible error type:
- if the source path is empty (status 2), doesn't exist (3) or if the user has no read permission (4)
- if the destination path exists (status 5) or if the user has no write permission (6)
- control over
stdout
andstderr
:mv
andcp
write onstderr
in case of failure. The functions allows to be sure:- that
stdout
either contains nothing, themv
/cp
status code orstderr
message, or a custom message, depending on the thestdout
configuration$3
- that
stderr
remains silent, even in case ofmv
/cp
failure
- that
- a system for the message customization: one template by status, with variable placeholders to inject the runtime parameters
Verbose mode / message customization:
The templates support 4 variable placeholders:
%src
: set to$2
%dest
: set to$3
%stderr_msg
: thestderr
output of themv
orcp
call. Only relevant for status 1.%op
: has the value move or copy
The default message templates are:
Status | Template |
---|---|
0 | - %src moved to %dest \n (for move/mv)- %src copied to %dest \n (for copy/cp) |
1 | %stderr_msg \n |
2 | error: %op failed, source path empty\n |
3 | error: %op from %src to %dest failed because %src doesn't exist\n |
4 | error: %op from %src to %dest failed because there's no read permission on %src \n |
5 | error: %op from %src to %dest failed because %dest exists (won't overwrite)\n |
6 | error: %op from %src to %dest failed because there's no write permission on %dest \n |
copy_file "/path/to/src" "path/to/dest" "verbose"
prints /path/to/src copied to /path/to/dest\n in case of success. The messages can
be customized by setting up an array variable where the indizes are the states and the values the corresponding templates (i.e. the success message
template is at index 0, etc.). The name of the array variable - and not the variable itself - has to be provided as 4th call parameter.
It's perfectly valid to customize a subset of states/templates, the function falls back to the default templates where it can't find a
customization. In the next example, the success message template is overwritten:
msg_defs[0]="custom message: %source copied to %destination"
copy_file "/path/to/src" "/path/to/dest" "verbose" "msg_defs"
prints custom message: /path/to/src copied to /path/to/dest if it's successful.
Examples:
stdout
silent:move_file "path/to/src" "path/to/dest"
- status code captured to variable:
status=$(move_folder "/path/to/src" "/path/to/dest" "status")
cp
error message captured to variable:err_msg=$(copy_file "/path/to/src" "/path/to/dest" "error_message")
Param. | $1 | source path |
$2 | destination path | |
[$3 ] | stdout configuration:
| |
[$4 ] | if $3 is set to verbose, the name of the array variable which contains the
custom message patterns. If omitted, the default message patterns are used; see explanations above | |
Pipes | stdout |
|
Status | 0 | operation successful |
1 | operation failure, if $3 is set to error_message (or its aliases), stdout
contains mv 's respectively cp 's stderr output | |
2 | the source path $2 doesn't exist | |
3 | no read permission on source path $2 | |
4 | the destination path $3 exists | |
5 | no write permission on destination path $3 | |
6 | the source path $2 is empty | |
7 | the destination path $3 is empty |
Internal handler for file/folder removal, used by the wrapper functions remove_file(), remove_folder(), see their documentation for details.
Param. | $1 | path |
[$2 ] | stdout configuration:
| |
[$3 ] | if $2 is set to verbose, the name of the array variable which contains the
custom message patterns. If omitted, the default message patterns are used | |
Pipes and status are documented below for the wrapper functions. |
rm
wrapper with:
- several checks before the actual removal attempt which allow to get specific status codes for any possible error type: if the path is empty (status 2), doesn't exist (3) or if the user has no write permission (4)
- control over
stdout
andstderr
:rm
writes onstderr
in case of failure. This functions allows to be sure:- that
stdout
either contains nothing, therm
status code orstderr
message, or a custom message, depending on thestdout
configuration$2
- that
stderr
remains silent, even in case ofrm
failure
- that
- a system for the message customization: one template by status, with variable placeholders to inject the runtime parameters
Verbose mode / message customization:
The variable placeholders %path
and %stderr_msg
are replaced by the path $1
and the mkdir
error message. The default message templates are:
Status | Template |
---|---|
0 | %path removed\n |
1 | %stderr_msg \n |
2 | removal error: path is empty\n |
3 | removal error: %path doesn't exist\n |
4 | emoval error: no write permission on %path \n |
%stderr_msg
is empty if status is not 1.
remove_file "/path/to/remove" "verbose"
prints /path/to/remove removed\n in case of success. The messages can
be customized by setting up an array variable where the indizes are the states and the values the corresponding templates (i.e. the success message
template is at index 0, etc.). The name of the array variable - and not the variable itself - has to be provided as 3rd call parameter.
It's perfectly valid to customize a subset of states/templates, the function falls back to the default templates where it can't find a
customization. In the next example, the success message template is overwritten:
msg_defs[0]="custom message: %path removed\n"
remove_folder "/path/to/remove" "verbose" "msg_defs"
prints custom message: /path/to/remove removed if it's successful.
Examples:
stdout
silent:remove_folder "path/to/new/dir"
- status code captured to a variable:
status=$(remove_file "/path/to/file_to_remove" "status")
rm
error message captured to a variable:
err_msg=$(remove_folder "/path/to/my_new_dir" "error_message")
Param. | $1 | path |
[$2 ] | stdout configuration:
| |
[$3 ] | if $2 is set to verbose, the name of the array variable which contains
the custom message templates - see explanations above | |
[$4 ] | "return error if $1 doesn't exist" flag:
| |
Pipes | stdout |
|
Status | 0 | $1 removed or doesn't exist (if $4 is omitted or set to something else than 1) |
1 | rm error, if $2 is set to error_message (or aliases), stdout
contains the content of rm 's stderr output | |
2 | path $1 is not writeable | |
3 | path $1 doesn't exist (only if $4 is set to 1) | |
4 | $1 is empty |
Bash allows to source
(aka .
) files which is a convenient way to load f.ex. configuration files, however, it has disadvantages as well:
- the files have to comply with the bash syntax of course, f.ex. regarding comments, the way the variables are defined, etc.
- the calling application has no control which variables are defined (or not), which ones are overwritten, etc.
It's sometimes easier and more flexible to load values with a file content search and extraction method like this function which is based on a search with grep
and the extraction of the value using string processing utilities.
Variable definitions should have the assignment format:
<variable name>=<value>
Each definition has to be on a single line, with any number of whitespaces before the variable name, between the variable name and the assignment operator '=' or between the operator and the value. Inline comments are not allowed, they should be on their own lines. Examples of valid definitions:
cfg_filepath="/etc/test.conf"
I'm a comment
cfg_filepath="/etc/test2.conf"
timeout = 25
If the variable is enclosed in quotes (i.e. the quotes are loaded as part of the value) they are removed with the string handling collection's sanitize_variable_quotes()
Param. | $1 | path of the configuration file |
$2 | name of the variable to load | |
Pipes | stdout | if status is 0, the loaded value, empty otherwise |
Status | 0 | successful, value is written on stdout |
1 | file $1 doesn't exist | |
2 | path $1 is not a file | |
3 | no read permission on file $1 | |
4 | no variable definition for the name $2 | |
5 | $1 is empty | |
6 | $2 is empty |