# Normal assignment doesn't work.
lines=$(printf "abc\n123\n\n"); echo -n "$lines" | wc -l
# 1 -- WRONG
empty=$(printf ""); echo -n "$empty" | wc -l
# 0 -- WRONG
# Problem is that variable assignment strips all newlines when assigning the
# variable.
# Solution is to not assign the variable that way.
IFS= read -rd '' lines < <(printf "abc\n123\n"); echo -n "$lines" | wc -l
# 2 -- CORRECT
IFS= read -rd '' empty < <(printf ""); echo -n "$empty" | wc -l
# 0 -- CORRECT
# echo -n or printf should be used, as "<<<" adds a newline.
IFS= read -rd '' lines < <(printf "abc\n123\n"); wc -l <<< "$lines"
# 3 -- WRONG
IFS= read -rd '' empty < <(printf ""); wc -l <<< "$empty"
# 1 -- WRONG
Warning: coproc seems to only exist and work correctly when the command expects and waits for input or output.
coproc printf "abc\n123\n"
IFS= read -rd '' -u "${COPROC[0]}" lines
if ! wait -n $COPROC_PID; then
echo failure
else
echo success
echo -n "${lines}"
echo
echo -n "${lines}" | wc -l
fi
s=0123456789
echo ${#s} # 10
Uses formats:
${VAR:START_IDX}
${VAR:START_IDX:LENGTH}
${VAR:START_IDX:-LEN_FROM_END}
s=0123456789
echo ${s:0} # 0123456789
echo ${s:0:-1} # 012345678
echo ${s:0:0} #
echo ${s:0:1} # 0
echo ${s:1} # 12345678
echo ${s:1:1} # 1
new_var="${some_var/FIELD/${field}}"
new_var="${some_var//FIELD/${field}}"
s="abd
def
kje
kjer
"
echo "${s//
/ }"
# abd def kje kjer
s=test.service
echo ${s#test} # prints ".service"
s=1test.service
echo ${s#test} # prints "1test.service"
s=test.service
echo ${s%.service} # prints "test"
s=test.service1
echo ${s%.service} # prints "test.service"
cat <<'EOF'
This will be displayed
via the cat
program.
EOF
Don’t quote the delimiter word if variable expansion, command substitution or arithmetic expansion is desired.
cat <<EOF
Hello. I am $(whoami). Nice to meet you!
There are $((60*60*24)) seconds in a day!
EOF
Can also suppress TABs. Doesn’t work on spaces. :(
cat <<-EOF
This will be displayed
via the cat
program.
EOF
Or output to a file.
cat <<'EOF' > /tmp/outfile
This will be written
to /tmp/outfile.
EOF
Same rules apply as in previous section.
IFS='' read -r -d '' VARNAME <<'EOF'
This will go into VARNAME.
EOF
: <<'COMMENTBLOCK'
echo "This line will not echo."
This is a comment line missing the "#" prefix.
This is another comment line missing the "#" prefix.
&*@!!++=
The above line will cause no error message,
because the Bash interpreter will ignore it.
COMMENTBLOCK
declare -a arr=(1 2 3 4 5)
printf '%s ' "${arr[@]:1}" # 2 3 4 5
printf '%s ' "${arr[@]:1:2}" # 2 3
printf '%s ' "${arr[@]:1:1}" # 2
printf '%s ' "${arr[@]: -2}" # 4 5
printf '%s ' "${arr[@]: -3:1}" # 3
echo ${#MYARR[@]}
Local array with indirect contents:
eval declare -r -a local_array=\( \${${indirect}[@]} \)
declare -A weapons=(
['Straight Sword']=75
['Tainted Dagger']=54
['Imperial Sword']=90
['Edged Shuriken']=25
)
function print_array {
eval "declare -A arg_array="${1#*=}
for i in "${!arg_array[@]}"; do
printf "%s\n" "$i ==> ${arg_array[$i]}"
done
}
print_array "$(declare -p weapons)"
unset MYMAP["${key}"]
declare -A MYMAP=( [a]=1 [b]=2 )
echo ${#MYMAP[@]} # 2
# lines
sort < <(for i in "${!MYMAP[@]}"; do echo "$i"; done)
# to array / on single line
declare -a sorted=( $(sort < <(for i in "${!MYMAP[@]}"; do echo "$i"; done)) )
echo "${sorted[@]}"
echo "${!MYMAP[@]}"
for key in "${!MYMAP[@]}"; do
echo "${key}"
done
if [[ "${MYMAP["${key}"]+_}" ]]; then
echo "Found"
else
echo "Not found"
fi
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_03.html
case $TYPE in
either|or)
stuff...
;;
*)
default_stuff...
;;
esac
no_mysql_insecure_password_warning()
{
sed -e '/Using a password on the command line interface can be insecure/d'
}
mysql "$@" 2> >(no_mysql_insecure_password_warning)
To stdout:
cmd 2>&1
Use the following syntax:
cmd &>file
OR
cmd > file-name 2>&1
Another useful example:
find /usr/home -name .profile 2>&1 | more
IFS=$'\t' read -r user hash length filename \
< <(mysql-query.sh --query "SELECT \`user\`, \`hash\`, \`length\`, \`filename\` FROM \`my_db\`.\`file_stuff\` LIMIT 1")
echo "User: $user Hash: $hash Length: $length Filename: $filename"
Skip adding lines to history that begin with spaces
HISTCONTROL=ignorespace
Don’t add lines that exist already.
HISTCONTROL=ignoredups
Remove lines in past that match new entry.
HISTCONTROL=erasedups
Multiple entries are separated by a colon.
Use “#” with “[@]”.
WARNING: Using “#” without the “[@]” will cause you to only output the number of characters in the first element of the array.
echo ${#an_array[@]}
sort < <(for i in "${range_ip_and_ports[@]}"; do echo "$i"; done)
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
echo "Is run directly"
else
echo "Is sourced"
fi
# Or
[[ "${BASH_SOURCE[0]}" == "${0}" ]] && main "$@"
# Commands should be in this array AND have a command_CMDNAME function.
declare -a commands=(help)
log_stderr() { cat <<< "$@" 1>&2; }
is_a_function() { [[ $(type -t "$1") == "function" ]]; }
command_help()
{
cat <<-EOF
Some text to display.
EOF
}
main_switcn()
{
declare -r command="$1"
declare -r command_function="command_${command}"
shift
if ! elem "${command}" "${commands[@]}"; then
log_stderr "\"${command}\" is not a command."
log_stderr "Commands: $(print_join_array ", " "${commands[@]}")"
return 1
elif ! is_a_function "${command_function}"; then
log_stderr "Missing function \"${command_function}\" for command \"${command}\"."
return 1
else
"${command_function}" "$@"
fi
}
if [[ $# -eq 0 ]]; then
command_help
else
main_switcn "$@"
fi
#!/bin/bash
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-e|--extension)
EXTENSION="$2"
shift # past argument
;;
-s|--searchpath)
SEARCHPATH="$2"
shift # past argument
;;
-l|--lib)
LIBPATH="$2"
shift # past argument
;;
--default)
DEFAULT=YES
;;
*)
# unknown option
;;
esac
shift # past argument or value
done
echo FILE EXTENSION = "${EXTENSION}"
echo SEARCH PATH = "${SEARCHPATH}"
echo LIBRARY PATH = "${LIBPATH}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
echo "Last line of file specified as non-opt/last argument:"
tail -1 $1
fi
#!/bin/bash
declare -r this_script_filename="$(basename "${BASH_SOURCE[0]}")"
stderr() { cat <<< "$@" 1>&2; }
print_help()
{
cat <<EOF
Usage: ${this_script_filename} ...
ARGS:
-h, --help This help
...DESCRIPTION HERE...
EOF
}
main()
{
local key
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-h|--help)
print_help
return 0
;;
-s|--searchpath)
SEARCHPATH="${2:?}"
shift # past argument
;;
--default)
DEFAULT=YES
;;
*)
# unknown option
printf 'Unknown option "%s"\n\n' "${key}" 1>&2
print_help 1>&2
return 1
;;
esac
shift # past argument or value
done
# do stuff...
}
[[ "${BASH_SOURCE[0]}" == "${0}" ]] && main "$@"
declare -r this_script_filename="$(basename "${BASH_SOURCE[0]}")"
declare -r -a argv=("$@")
print_help()
{
cat <<EOF
Usage: ${this_script_filename} ...
ARGS:
--help -h This help
...DESCRIPTION HERE...
EOF
}
is_flag_set()
{
for flag in "$@"; do
if contains_element "$flag" "${argv[@]}"; then
return 0
fi
done
return 1
}
contains_element()
{
local e
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
if is_flag_set "--help" "-h"; then
print_help
exit
fi
main
if [ "${1:-}" = "--help" ] \
|| [ "${1:-}" = "-h" ]
then
cat <<EOF
Usage: $0 ...
ARGS:
--help -h This help
Description
EOF
exit 0
fi
# | Description | sysexit.h |
---|---|---|
0 | Success | EX_OK |
1 | Catchall/generic error | |
2 | Shell builtin misuse | |
64 | Command line usage error | EX_USAGE |
65 | Data format error | EX_DATAERR |
66 | cannot open input | EX_NOINPUT |
67 | addressee unknown | EX_NOUSER |
68 | host name unknown | EX_NOHOST |
69 | service unavailable | EX_UNAVAILABLE |
70 | internal software error | EX_SOFTWARE |
71 | system error (eg can’t fork) | EX_OSERR |
72 | critical OS file missing | EX_OSFILE |
73 | can’t create user output file | EX_CANTCREAT |
74 | input/output error | EX_IOERR |
75 | temp failure(can retry) | EX_TEMPFAIL |
76 | remote error in protocol | EX_PROTOCOL |
77 | permission denied | EX_NOPERM |
78 | configuration error | EX_CONFIG |
126 | Command invoked cannot execute | |
127 | Command not found | |
128 | Invalid argument to exit | |
128+n | Fatal error signal n | |
255 | Exit status out of range(Only 0-255 valid) |
Use empty:
no_inherit_fds()
{
( # Subshell:
# Close all but stdio
SELF=$BASHPID
FDS=$(find /proc/$SELF/fd -type l -printf '%f\n')
# The following will even try to close the fd for the find sub
# shell although it is already closed. (0: stdin, 1: stdout, 2:
# stderr, 3: find)
for n in $FDS ; do
if ((n > 2)) ; then
eval "exec $n>&-"
fi
done
"$@"
)
}
local contents
if ! [[ -e $file_path ]] || ! contents=$(<"${file_path}"); then
return 1
fi
declare -a lines
while IFS='' read -r line || [[ -n "$line" ]]; do
lines+=("${line}")
done < "${file_path}"
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "Text read from file: $line"
done < "${file_path}"
while IFS= read -r line; do
echo "${line}"
done <<< "${var}"
declare -r this_script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
realpath "${dir}"
declare -r ps_defunct="$(ps ax | grep '<[d]efunct>')"
declare -r -i defunct_count="$([[ -n $ps_defunct ]] && wc -l <<< "${ps_defunct}")"
local lines_raw
# Use read to prevent trailing newline trimming
IFS= read -rd '' lines_raw < <(
dmesg | tail -n 1000 |
egrep -i 'hda|hdb|hdc|sda|sdb|sdc|sdd|sde|sdf|sdg|ata|ide')
declare -r -i count=$(echo -n "$lines_raw" | wc -l)
declare -r -i logfile_count=3
declare -r -i today_logfile_num=$(( $(date +%j) % logfile_count ))
declare -r logfile="/lrtemp/logs/set_deleted_date_for_all_trashcan_children.php.${today_logfile_num}.log"
do_something_daily > "${logfile}"
logger_id="$(basename "$0")"
logger_facility=user
log_emerg() { logger -s -t "${logger_id}" -p "${logger_facility}.emerg" <<< "$@"; }
log_alert() { logger -s -t "${logger_id}" -p "${logger_facility}.alert" <<< "$@"; }
log_crit() { logger -s -t "${logger_id}" -p "${logger_facility}.crit" <<< "$@"; }
log_err() { logger -s -t "${logger_id}" -p "${logger_facility}.err" <<< "$@"; }
log_warning() { logger -s -t "${logger_id}" -p "${logger_facility}.warning" <<< "$@"; }
log_notice() { logger -s -t "${logger_id}" -p "${logger_facility}.notice" <<< "$@"; }
log_info() { logger -s -t "${logger_id}" -p "${logger_facility}.info" <<< "$@"; }
log_debug() { logger -s -t "${logger_id}" -p "${logger_facility}.debug" <<< "$@"; }
log() { log_notice "$@"; }
stderr() { cat <<< "$@" 1>&2; }
eprintf() { printf "$@" 1>&2; }
# Echo script as it is processed
set -v
# Output commands run, with variables replaced with actual contents
set -x
func()
{
echo "to syslog"
} 1> >(logger -s -t "$(basename "$0")") 2>&1 # redirect all output to system log
At top of file:
# redirect all output to system log
exec 1> >(logger -s -t "$(basename "$0")") 2>&1
declare -r log_file_path=/tmp/some_file.log
log() { cat <<< "$@" >> "${log_file_path}"; }
declare -r log_file_path=/tmp/some_file.log
log() { echo "$(date '+%Y%m%d-%H%M%S') $*" | tee -a "${log_file_path}"; } 2>&1
declare -r this_script_filename="$(basename "${BASH_SOURCE[0]}")"
declare -r LOGFILE="/var/log/linkright.log"
export LOGFILE
log()
{
declare -a output=("${this_script_filename}:" "$@")
cat <<< "${output[@]}" >> "${LOGFILE}"
}
log() { cat <<< "$@" 1>&2; }
stderr() { cat <<< "$@" 1>&2; }
Convert line to array.
for (( x=0; x<2; ++x )); do
echo $x
done
If contents from read, may have extra empty line at end.
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "Text read from contents: $line"
done <<< "${contents}"
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "Text read from file: $line"
done < "${file_path}"
while IFS= read -r line; do
echo "${line}"
done < <(some_comand with_args)
while IFS= read -r -d $'\0' file; do
echo "${file}"
done < <(find /lib64/ -maxdepth 1 -type f -print0)
# Into an array
declare -a files
while IFS= read -r -d $'\0' file; do
files+=("${file}")
done < <(find /lib64/ -maxdepth 1 -type f -print0)
Bash uses: https://www.gnu.org/software/libc/manual/html_node/Regular-Expressions.html
# Example:
# $ regex_extract "\b((25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\b" "http://13.24.35.255:13"
# 13.24.35.255
regex_extract()
{
declare -r re="$1"
if [[ $2 =~ $re ]]; then
echo "${BASH_REMATCH[0]}"
return 0
fi
return 1
}
# Public: Extract the nth group
#
# Finds matching line and outputs the given group.
#
# $1 - Group number to match. 0=all
# $2 - Regular expression
# $3-* - Search string
#
# Example
#
# local response seconds
# if ! response=$(regex_extract_n 1 "^\[([^]]+)\]" "$*"); then
# # Matches digits out of "[123]...."
# return 1
# fi
#
# On success returns 0 and outputs matching section.
regex_extract_n()
{
declare -r re="$2"
if [[ $3 =~ $re ]]; then
echo "${BASH_REMATCH[$1]}"
return 0
fi
return 1
}
print_cpu_count()
{
read -r -d '' awk_script << 'EOF'
BEGIN {
count = 0
}
{
if(NF == 3 && $1 == "processor")
{
count++
}
}
END {
print count
}
EOF
/bin/awk "${awk_script}" /proc/cpuinfo
}
print_uptime_seconds() { awk '{print $1}' /proc/uptime | sed 's/\..*//'; }
dmesg_from_last_x_seconds() {
declare -r -i second_count="$1"
declare -r -i current_second="$(print_uptime_seconds)"
declare -r -i start_second=$(( current_second - second_count ))
read -r -d '' awk_script << 'EOF'
{
if (started) {
print
} else if (match($0, /^\[([0-9]+)\./, matches) != 0 && matches[1] > start_second) {
started = 1
print
}
}
EOF
dmesg | /bin/awk -v start_second="${start_second}" "${awk_script}"
}
for ((i=0;i<60;++i)); do printf '.' > /dev/pts/1; date; sleep 60; done
while true; do printf '.' | tee /dev/pts/1; sleep $((60*4)); done
printf '%q\n' 'This is a "test"'
declare -r lockfile_path="/tmp/$(basename "${BASH_SOURCE[0]}").lock"
mutually_exclusive_main()
{
# This is auto-populated by bash at bottom of function
local instance_fd
(
# Don't wait for lock on lockfile (fd automatically allocated)
if flock --nonblock --exclusive ${instance_fd}; then
main "$@"
else
echo "Unable to run. Already running." 1>&2
return 1
fi
) {instance_fd}> "${lockfile_path}"
}
mutually_exclusive_main "$@"
mutually_exclusive_blocking_main()
{
# This is auto-populated by bash at bottom of function
local instance_fd
(
# Wait for lock on lockfile (fd automatically allocated)
flock --exclusive ${instance_fd}
main
) {instance_fd}> "${lockfile_path}"
}
for (( divisions=10, seconds_inbetween=60, cur=0; cur<divisions ; ++cur )); do
if [ $(( $(hostname | sed 's/[^0-9]//g') % divisions )) -eq $cur ]; then
echo "Performing action"
break
fi
sleep "${seconds_inbetween}"
done
“man trap” for documentation.
trap [action condition…]
- Signal names should drop the “SIG” prefix.
- Null action ” means ignore the conditions.
- Action ‘-’ means reset each condition to default.
Signals:
Signal | Number | Action | Description | Non-catchable |
---|---|---|---|---|
EXIT | 0 | Term | Script completes | |
HUP | 1 | Term | Terminal disconnected | |
INT | 2 | Core | Interrupt from keyboard | |
QUIT | 3 | Core | Quit from keyboard | |
ILL | 4 | Core | Illegal instruction | |
ABRT | 6 | Core | Signal from abort() | |
FPE | 8 | Core | Floating point exception | |
KILL | 9 | Term | Kill signal | X |
SEGV | 11 | Core | Invalid memory referenced | |
PIPE | 13 | Term | Broken pipe: write to pipe with no readers | |
ALRM | 14 | Term | Timer signal from alarm() | |
TERM | 15 | Term | Termination signal | |
USR1 | 30,10,16 | Term | User defined 1 | |
USR2 | 31,12,17 | Term | User defined 2 | |
CHLD | 20,17,18 | Ign | Child stopped or terminated | |
CONT | 19,18,25 | Cont | Continue process | |
STOP | 17,19,23 | Stop | Stop process | X |
TSTP | 18,20,24 | Stop | Stop typed at terminal | |
Actions | Default action | |||
Term | Terminate the process | |||
Ign | Ignore the signal | |||
Core | Terminate the process and dump core | |||
Stop | Stop the process | |||
Cont | Continue process if stopped |
At top of file add the following:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
if (((RANDOM % 20) == 0)); then
echo Random action
fi
shuf -e $(seq 0 3 30)
shuf -i 0-5
array_returning_function()
{
printf '%q ' \
1 \
"a b c" \
2 \
'15 "22" 19' \
END
}
main()
{
local arr
if ! arr="$(array_returning_function)"; then
echo FAILED
return 1
fi
eval arr=\($arr\)
printf '(%s)\n' "${arr[@]}"
}
main
printf -v newline '\n'
assoc_array_returning_function()
{
declare -A ret
ret[one]='a b c'
ret[two]='15 "22" 19'
ret[three]="${newline}"
ret[four]='END'
ret[f i v e]='s i x'
# associative array return
for key in "${!ret[@]}"; do
printf '[%q]=%q ' "${key}" "${ret["${key}"]}"
done
}
main()
{
local result
if ! result="$(assoc_array_returning_function)"; then
return 1
fi
eval declare -A result=\(${result}\)
for key in "${!result[@]}"; do
printf '(:KEY %s :VALUE %s)\n' "${key}" "${result["${key}"]}"
done
# (:KEY four :VALUE END)
# (:KEY one :VALUE a b c)
# (:KEY f i v e :VALUE s i x)
# (:KEY two :VALUE 15 "22" 19)
# (:KEY three :VALUE
# )
}
main
IFS='' read -r -d '' data <<'EOF'
,abc,123
def,,456
ghi,789,
EOF
map_data()
{
declare -r split_regex=$1
declare -r data=$2
shift 2
while read -r line; do
preg_split "${split_regex}" "${line}" "$@"
done < <(echo -n "${data}")
}
trim()
{
local var="$*"
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"
echo -n "$var"
}
preg_split()
{
declare -r regex=$1
declare -r string=$2
shift 2
declare -a func_args=("$@")
declare -r re="^(.*)${regex}(.*)$"
local scan=$string
declare -a args
while [[ $scan =~ $re ]]; do
if [[ $scan = "${BASH_REMATCH[1]}" ]]; then
return 1
fi
args+=("${BASH_REMATCH[*]: -1:1}")
scan="${BASH_REMATCH[1]}"
done
# Stop if nothing split
if [[ ${#args[*]} -eq 0 ]]; then
return 1
fi
func_args+=("$(trim "${scan}")")
declare -i i
for ((i = ${#args[*]} - 1; i >= 0; --i)); do
func_args+=("$(trim "${args[$i]}")")
done
"${func_args[@]}"
}
map_data "," "${data}" printf '"%s" "%s" "%s"\n'
map_stdin_by_separator()
{
declare -r split_regex=$1
shift 1
while read -r line; do
preg_split "${split_regex}" "${line}" "$@"
done
}
trim()
{
local var="$*"
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"
echo -n "$var"
}
preg_split()
{
declare -r regex=$1
declare -r string=$2
shift 2
declare -a func_args=("$@")
declare -r re="^(.*)${regex}(.*)$"
local scan=$string
declare -a args
while [[ $scan =~ $re ]]; do
if [[ $scan = "${BASH_REMATCH[1]}" ]]; then
return 1
fi
args+=("${BASH_REMATCH[*]: -1:1}")
scan="${BASH_REMATCH[1]}"
done
# Stop if nothing split
if [[ ${#args[*]} -eq 0 ]]; then
return 1
fi
func_args+=("$(trim "${scan}")")
declare -i i
for ((i = ${#args[*]} - 1; i >= 0; --i)); do
func_args+=("$(trim "${args[$i]}")")
done
"${func_args[@]}"
}
map_stdin_by_separator "," printf '"%s" "%s" "%s"\n' <<'EOF'
,abc,123
def,,456
ghi,789,
EOF
declare -r -i is_terminal=$([[ -t 0 ]] && echo 1)
now_unix_timestamp()
{
date '+%s'
}
to_unix_timestamp()
{
date --date="$*" "+%s"
}
timestamp_to_human()
{
declare -r -i unix_timestamp=$1
date --date="@${unix_timestamp}" --rfc-3339=seconds
}
to_year_month_day_hour_minute_second()
{
date --date="$*" "+%Y %m %d %H %M %S"
}
date_array_compare()
{
declare -a date_a
declare -a date_b
if ! date_a=( $(to_year_month_day_hour_minute_second "$1") ); then
return 1
elif ! date_b=( $(to_year_month_day_hour_minute_second "$2") ); then
return 2
fi
for pos in $(seq 1 6); do
if [ "${date_a[$pos]}" -lt "${date_b[$pos]}" ]; then
echo "-1"
return 0
elif [ "${date_a[$pos]}" -gt "${date_b[$pos]}" ]; then
echo "1"
return 0
fi
done
echo "0"
return 0
}
is_date_in_range()
{
local result
if ! result=$(date_array_compare "$2" "$1"); then
return 2
fi
if [ "$result" = "1" ]; then
return 1
fi
if ! result=$(date_array_compare "$3" "$1"); then
return 2
fi
if [ "$result" = "-1" ]; then
return 1
fi
return 0
}
main()
{
declare user_data=${1:-}
if [[ -z $user_data ]]; then
user_data=$(</dev/stdin)
if [[ -z $user_data ]]; then
return 1
fi
fi
echo "Got: ${user_data}"
}
main "$@"
# Public: Ask user for info with optional regex check
#
# --prompt -p Prompt to use.
# --verify-by-regex Regex to verify input by.
#
# Example
#
# ask --prompt "The date?" \
# --verify-by-regex '^[12][0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[0-1])$'
# > The date? 1920-12-32
# > Unvalid input.
#
# Returns user response on success. Otherwise non 0 return code.
ask()
{
local input
declare prompt=""
declare validation_regex=""
declare -i status
while [[ $# -gt 0 ]]; do
case $1 in
-p|--prompt)
prompt="$2"; shift
;;
--verify-by-regex)
validation_regex="$2"; shift
;;
-*)
# unknown option
printf 'Unknown argument: %s\n' "$1" 1>&2
return 1
;;
*)
if [[ -n $prompt ]]; then
printf 'Prompt already set to "%s". Can not reset to: %s\n' \
"$prompt" "$1" 1>&2
return 1
fi
prompt="$1"
;;
esac
shift # past argument or value
done
while true; do
if [[ -n $prompt ]]; then
printf '%s ' "$prompt" 1>&2
fi
read -r input
status=$?
if ((status != 0)); then
printf 'Read user input failure. (%d)\n' "${status}" 1>&2
return 1
fi
if [[ -z $validation_regex ]]; then
break
fi
if [[ $input =~ $validation_regex ]]; then
break
fi
printf 'Unvalid input.\n' 1>&2
done
printf '%s' "${input}"
}
read -p "Are you sure? " -n 1 -r
echo # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]; then
# do dangerous stuff
fi
member() { local e; for e in "${@:2}"; do [[ $e = "$1" ]] && return 0; done; return 1; }
integer? () { [[ $1 =~ ^[0-9]+$ ]]; }
is_natural_number () { [[ $1 =~ ^[1-9][0-9]*$ ]]; }
min () { for i in "$@"; do ((i < t)) && t=$i; done; printf '%d' "$t"; }
max () { for i in "$@"; do ((i > t)) && t=$i; done; printf '%d' "$t"; }
strip-leading-0 () { sed 's/^0\+//' <<< "$*"; }
strip-leading-0-pipe() { sed 's/^0\+//'; }
rand_range_safe()
{
declare -i rmin=${1:?}
declare -i rmax=${2:?}
if ((rmin > rmax)); then
declare -i rtmp=${rmax}
rmax=${rmin}
rmin=${rtmp}
fi
declare -i range=$((rmax + 1 - rmin))
declare -r -i max_random=32767
declare -i return_code=0
if ((range > max_random)); then
range=${max_random}
return_code=1
fi
declare -r -i max_allowed=$(((max_random % range) == 0 ? max_random : max_random - (max_random % range) - 1))
declare -i random_value=$RANDOM
while ((random_value > max_allowed)); do
random_value=$RANDOM
done
printf '%d' $((rmin + (random_value % range)))
return ${return_code}
}
sleep $(rand_range 10 20)
multiply_f()
{
echo "$1 * $2" | bc -l
}
subtract_f()
{
echo "$1 - $2" | bc -l
}
cast_to_int()
{
printf '%.0f\n' "$1"
}
# Public: Replace one instance of a pattern in a string.
#
# $1 - Pattern to find
# $2 - Replacement for pattern
# $3 - String to search
#
# Example
#
# replace_one "a" "b" "aaa"
# # baa
#
# Writes search string, with pattern replaced if found.
replace_one()
{
declare -r pattern="${1:?}"
declare -r replacement="${2:?}"
declare -r string="${3:?}"
printf "%s" "${string/$pattern/$replacement}"
}
# Public: Replace all instances of a pattern in a string.
#
# $1 - Pattern to find
# $2 - Replacement for pattern
# $3 - String to search
#
# Example
#
# replace_all "a" "b" "aaa"
# # bbb
#
# Writes search string, with pattern replaced if found.
replace_all()
{
declare -r pattern="${1:?}"
declare -r replacement="${2:?}"
declare -r string="${3:?}"
printf "%s" "${string//$pattern/$replacement}"
}
to_upper()
{
if ((! $#)); then
tr '[:lower:]' '[:upper:]'
else
tr '[:lower:]' '[:upper:]' <<< "$*"
fi
}
reduce_whitespace()
{
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/[[:space:]][[:space:]]*/ /g'
}
# Reads from stdin
trim()
{
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
}
# Pure posix, takes args
trim()
{
local var="$*"
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"
echo -n "$var"
}
indent() { while IFS= read -r line; do printf '%*s%s\n' "${1:-2}" '' "${line}"; done; }
unlines()
{
declare -r lines="$*"
echo "${lines//
/ }"
}
unlines_with()
{
declare -r sep="$1"; shift
declare -r lines="$*"
echo "${lines//
/${sep}}"
}
get_file_mtime()
{
/usr/bin/stat --printf="%Y" "$1"
}
file_modified_seconds_ago()
{
declare -r path=$1
local -i mtime now
if ! mtime=$(/usr/bin/stat --printf="%Y" "${path}"); then
return 1
fi
if ! now=$(date '+%s'); then
return 1
fi
echo $((now - mtime))
}
get_file_size()
{
/usr/bin/stat --printf="%s" "$1"
}
basename.() { basename "$1" | cut -d. -f1; }
## @fn file_x_modified_within_y_minutes
## @brief Check if a file exists with an mtime within range
##
## @param $1 File path
## @param $2 Minute range
##
## @return 0 if file exists and has mtime in past "range" minutes.
##
## @usage
## file_x_modified_within_y_minutes \
## "${touch_file_name}" \
## "${no_send_more_often_than_minutes}"
## if [ $? -ne 0 ]; then
## # ... do stuff
## tmp_touch_file "${touch_file_name}"
## fi
file_x_modified_within_y_minutes()
{
declare -r file_path="$1"
declare -r -i minutes="$2"
declare -i count
count="$(find "$(dirname "${file_path}")/" \
-maxdepth 1 \
-mmin "-${minutes}" \
-name "$(basename "${file_path}")" \
| wc -l)"
if [ "${count}" -eq 0 ]; then
return 1
else
return 0
fi
}
with_temp_file()
{
declare to_call="${1:?}"
shift
local path
if ! path="$(mktemp)"; then
return 1
fi
trap "rm -f ${tmp_files[*]}" EXIT
"${to_call}" "$@" "${path}"
declare -r -i result=$?
/bin/rm "${path}"
trap - EXIT
return "${result}"
}
with_temp_files()
{
declare -r -i count=${1:?}; shift
declare -a tmp_files
local path
for ((i=0; i<count; ++i)); do
if ! path="$(mktemp)"; then
log "Failed to create temp file."
for path in "${tmp_files[@]}"; do
/bin/rm "${path:?}"
done
return 1
fi
tmp_files+=("${path}")
done
trap "rm -f ${tmp_files[*]}" EXIT
"$@" "${tmp_files[@]}"
declare -r -i result=$?
for path in "${tmp_files[@]}"; do
/bin/rm "${path:?}" &>/dev/null
done
trap - EXIT
return "${result}"
}
varargs_example_inner()
{
declare -r stderr1="${@: -1:1}"
declare -r stderr2="${@: -2:1}"
declare -r stderr3="${@: -3:1}"
# Clear temp files from arguments
set -- "${@:1: $(($#-3))}"
printf "%s " "$@"
}
varargs_example()
{
with_temp_files 3 varargs_example_inner "$@"
}
varargs_example 1 2 3
# Prints: 1 2 3
seconds_to_human() {
declare -r -i T=$1
declare -r -i D=$((T/60/60/24))
declare -r -i H=$((T/60/60%24))
declare -r -i M=$((T/60%60))
declare -r -i S=$((T%60))
((D>0)) && printf '%d days ' $D
((H>0)) && printf '%d hours ' $H
((M>0)) && printf '%d minutes ' $M
((M>0||H>0||D>0)) && printf 'and '
printf '%d seconds\n' $S
}
seconds_to_summary() {
declare -r -i T=$1
declare -r -i D=$((T/60/60/24))
((D>0)) && { printf '%dd' $D; return; }
declare -r -i H=$((T/60/60))
((H>0)) && { printf '%dh' $H; return; }
declare -r -i M=$((T/60))
((M>0)) && { printf '%dm' $M; return; }
printf '%ds' "$T"
}
seconds_to_summary() {
declare p=''
declare -i n=2
declare -r -i T=$1
declare -r -i D=$((T/60/60/24))
((D>0)) && { printf '%s%dd' "$p" $D; p=' '; n+=-1; }
declare -r -i H=$((T/60/60%24))
((H>0)) && { printf '%s%dh' "$p" $H; p=' '; n+=-1; }
declare -r -i M=$((T/60%60))
((M>0 && n>0)) && { printf '%s%dm' "$p" $M; p=' '; n+=-1; }
declare -r -i S=$((T%60))
((S>0 && n>0)) && { printf '%s%ds' "$p" $S; }
}
touch -d@0 FILEPATH
seconds_running() {
declare -i now start_time
if ! now=$(date +%s) || ! start_time=$(stat --format=%Y /proc/${1:?}); then
return 1
fi
echo $((now - start_time))
}
for pid in $(pgrep "^hhvm$"); do
seconds_running "${pid}"
if [[ $(seconds_running "${pid}") -lt 120 ]]; then
echo Short running process: $pid
fi
done
print_timestamp_min()
{
date '+%Y%m%d-%H%M'
}
print_timestamp_sec()
{
date '+%Y%m%d-%H%M%S'
}
month_str_to_num()
{
declare -i idx
idx=$(strindex JanFebMarAprMayJunJulAugSepOctNovDec "$1")
if [ "$idx" -ne -1 ]; then
echo $((idx / 3 + 1))
fi
}
# > month_str_to_num Oct
# 10
print_seconds_and_milliseconds()
{
/bin/date "+%s.%3N"
}
realpath --relative-to="$file1" "$file2"
# For example:
realpath --relative-to=/usr/bin/nmap /tmp/testing
# ../../../tmp/testing
extract_number_group_n()
{
declare -r str=$1
declare -r -i group=${2:-0}
local ch
declare -i i digitp inside_number=0 current_group=0
for ((i = 0; i < ${#str}; ++i)); do
ch=${str:$i:1}
digitp=$([[ $ch != [0-9] ]]; echo $?)
if ((inside_number)); then
if ((group == current_group)); then
if ((!digitp)); then
printf '\n'
return 0
fi
printf '%s' "$ch"
else
inside_number=0
current_group+=1
fi
elif ((digitp)); then
inside_number=1
if ((group == current_group)); then
printf '%s' "$ch"
fi
fi
done
if ((inside_number && group == current_group)); then
printf '\n'
return 0
fi
return 1
}
# declare -r -i coefficient=$(extract_number_group_n "$HOSTNAME" 0 || echo 0)
seconds_to_summary() {
declare -r -i T=$1
declare -r -i D=$((T/60/60/24))
((D>0)) && { printf '%dd' $D; return; }
declare -r -i H=$((T/60/60))
((H>0)) && { printf '%dh' $H; return; }
declare -r -i M=$((T/60))
((M>0)) && { printf '%dm' $M; return; }
printf '%ds' "$T"
}
track_memory_usage () {
declare -r -i pid=${1:?}
local total_line start_total_kb total_kb output last_output start_time now
declare -i difference
start_time=$(date '+%s')
total_line=$(pmap -x "${pid}" | grep ^total)
if ! [[ $total_line ]]; then
echo "No info for pid $pid" 1>&2;
return 1
fi
set -- ${total_line}
start_total_kb=$3 # rss_kb=$3 dirty_kb=$4
echo "${start_total_kb} KB"
#for ((i = 0; i < 10; ++i)); do
while true; do
total_line=$(pmap -x "${pid}" | grep ^total)
if ! [[ $total_line ]]; then
echo "Process terminated"
return 1
fi
total_kb=$3
output="$(date +%H:%M) Start: ${start_total_kb} KB Current: ${total_kb} KB"
difference=$((total_kb - start_total_kb))
if ((difference != 0)); then
now=$(date '+%s')
output="$output Difference: ${difference} KB over $(seconds_to_summary $((now - start_time)))"
fi
if [[ $output != "${last_output}" ]]; then
clear
echo "$output"
last_output=$output
fi
sleep 1
done
}
filter_php_log_recent_seconds()
{
declare -r -i seconds="${1:?}"; shift
declare -r -a files=("$@")
read -r -d '' awk_script << 'EOF'
BEGIN {
m["Jan"]=1; m["Feb"]=2; m["Mar"]=3; m["Apr"]=4; m["May"]=5; m["Jun"]=6
m["Jul"]=7; m["Aug"]=8; m["Sep"]=9; m["Oct"]=10; m["Nov"]=11; m["Dec"]=12
start = systime() - seconds
}
{if (do_print == 1) {print; next}}
/^\[/ {
if (match($0, /^\[[A-Z][a-z][a-z] ([A-Z][a-z][a-z]) ([1-9][0-9]*) ([01][0-9]):([01][0-9]):([01][0-9]) (20[0-9][0-9])\]/, d) == 1) {
ts = mktime(sprintf("%d %02d %02d %02d %02d %02d", d[6], m[d[1]], d[2], d[3], d[4], d[5]))
if (ts > 0 && start <= ts) {
print
do_print = 1
}
}
}
EOF
/bin/awk -v seconds="${seconds}" "${awk_script}" "${files[@]}"
}
filter_var_messages_recent_seconds()
{
declare -r -i seconds="${1:?}"; shift
declare -r -a files=("$@")
read -r -d '' awk_script << 'EOF'
BEGIN {
m["Jan"]=1; m["Feb"]=2; m["Mar"]=3; m["Apr"]=4; m["May"]=5; m["Jun"]=6
m["Jul"]=7; m["Aug"]=8; m["Sep"]=9; m["Oct"]=10; m["Nov"]=11; m["Dec"]=12
year=strftime("%Y")
start = systime() - seconds
}
{
if (do_print == 1) {print; next}
if (match($0, /^([A-Z][a-z][a-z]) ([1-9][0-9]*) ([01][0-9]):([01][0-9]):([01][0-9]) /, d) == 1) {
ts = mktime(sprintf("%d %02d %02d %02d %02d %02d", year, m[d[1]], d[2], d[3], d[4], d[5]))
if (ts > 0 && start <= ts) {
print
do_print = 1
}
}
}
EOF
/bin/awk -v seconds="${seconds}" "${awk_script}" "${files[@]}"
}
gensym()
{
declare -r gensym_file=/tmp/.bash_gensym
declare -r gensym_lock_file=${gensym_file}.lock
local instance_fd
(
# Wait for lock on lockfile (fd automatically allocated)
flock --exclusive ${instance_fd}
declare -i gennum=$(<"${gensym_file}")
gennum+=1
echo "${gennum}" > "${gensym_file}"
echo "f${BASHPID}_${gennum}"
) {instance_fd}> "${gensym_lock_file}" 2>/dev/null
}
lambda()
{
declare s="$1() { "; shift
for arg in "$@"; do printf -v s '%s\n%b' "${s}" "${arg}"; done
eval "$s
}"
}
map_lambda() {
local f="$(gensym)"
lambda "$f" "$1"
shift
for i in "$@"; do
"$f" "$i"
done
unset -f "$f"
}
reduce_lambda() {
local f="$(gensym)"
lambda "$f" "$1"
local acc="$2"
shift 2
for i in "$@"; do
acc=$("$f" "${acc}" "$i")
done
unset -f "$f"
printf '%s' "${acc}"
}
main()
{
local f="$(gensym)"
# Escaping only
lambda "$f" 'echo "1:$1"; echo "2:$2"'
"$f" Test1 Test2
lambda "$f" 'for val in "$@"; do echo "${val}"; done;'
"$f" "Test_A" "Test B"
lambda "$f" \
'for val in "$@"; do' \
' echo "${val}"' \
'done'
"$f" "Test_A" "Test B"
map_lambda 'echo "Lambda sees $1"' \
$(seq 1 3)
reduce_lambda 'echo $(($1 + $2))' 0 \
$(seq 1 100)
}
# An example map function, to use in the example below.
map() { local f="$1"; shift; for i in "$@"; do "$f" "$i"; done; }
# Lambda function [λ], passed to the map function.
λ(){ echo "Lambda sees $1"; }; map λ "$@"
# Public: Curry function and argument into a new function.
#
# Takes a function with some arguments and partially applies them with a new
# function name.
#
# $1 : New function name
# $2- : Functions and arguments
#
# Example
#
# curry log \
# echo "LOGGING: "
# log ERROR MESSAGE
#
# Returns output of expression.
curry() {
declare s="$1() { "; shift
for arg in "$@"; do printf -v s '%s %q' "${s}" "${arg}"; done
eval "$s \"\$@\"; }"
}
# Example
lines() { printf '"%s"\n' "$@"; }
make_curry() { curry test_lines lines "line 1" "line 2"; }
make_curry; test_lines "line 3" # outputs 3 lines
# Public: Compose partial functions into one
#
# Takes functions that compose to get a final result.
#
# $1 : New function name
# $2- : Functions and arguments, delimited by '^'
#
# Example
#
# compose f \
# multiply 3 \
# ^ \
# add 2 \
# ^ \
# multiply 2
# f 15 # Prints "96" as ((15*2)+2)*3 = 96
#
# Returns output of expression if no error occurred.
compose()
{
declare -r function_name=$1; shift
IFS='' read -r -d '' body_template <<'EOF'
%s() {
declare -i status
declare output
declare -a args
%s
printf '%%s' "${output}"
}
EOF
IFS='' read -r -d '' function_template <<'EOF'
output="$("${args[@]}")"
status=$?
if ((status != 0)); then
echo "Failed running: ${args[*]}" 1>&2
return $status
fi
EOF
declare body_content
declare current_args
for arg in "$@"; do
if [[ $arg = '^' ]]; then
printf -v body_content ' args=(%s "${output}")\n%s%s' \
"${current_args}" \
"${function_template}" \
"${body_content}"
current_args=''
else
printf -v current_args '%s %q' "${current_args}" "${arg}"
fi
done
printf -v body_content ' args=(%s "$@")\n%s%s' \
"${current_args}" \
"${function_template}" \
"${body_content}"
printf -v to_eval "${body_template}" "${function_name}" "${body_content}"
eval "${to_eval}"
}
press_key_to_continue()
{
read -n 1 -s -p "Press any key to continue"
}
confirmation_message_y_n()
{
declare -r message="${1:-Are you sure?}"
read -p "${message} (y/n): " -n 1 -r
echo
[[ $REPLY =~ ^[Yy]$ ]]
}
shell-quoted() {
if (($#>1)); then
printf '%q ' "${@:1: $(($#-1))}"
fi
printf '%q' "${@: -1}"
printf '\n'
}
multiplier_to_seconds()
{
case "$1" in
s|'')
echo 1
;;
m)
echo 60
;;
h)
echo $((60*60))
;;
d)
echo $((60*60*24))
;;
w)
echo $((60*60*24*7))
;;
M)
echo $((60*60*24*30))
;;
*)
return 1
;;
esac
}
to_seconds()
{
declare -r all=$1
declare -i multiplier=0
if [[ $all =~ ^[1-9][0-9]*$ ]]; then
echo "$all"
return 0
elif [[ $all =~ ^[1-9][0-9]*.$ ]]; then
declare -r -i value=${all:0: -1}
if ! multiplier=$(multiplier_to_seconds "${all: -1}"); then
return 1
fi
echo $((value * multiplier))
else
return 1
fi
}
use_ifs()
{
declare -r old_ifs=$IFS
IFS=${1:?}; shift
"$@"
declare -r -i status=$?
IFS=$old_ifs
return $status
}
access_log_status_counts()
{
read -r -d '' awk_script << 'EOF'
BEGIN {
m1_remote_ip=1
m1_local_time=2
m1_request=3
m1_status=4
m1_bytes_sent=5
m1_referer=6
m1_forwarded_for=7
}
{
count = match($0,
/^([0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}) - - \[([^\]]*)\] "([^"]*)" \(([0-9]+)\) ([0-9]+) "([^"]*)" "([^"]*)"/,
matches)
if (count != 0) {
status=matches[m1_status]
totals[status] = totals[status] + 1
}
}
END {
for (status in totals) {
print(status ": " totals[status])
}
}
EOF
/bin/awk "${awk_script}" /lrtemp/logs/access.log
}
field_is_one_of()
{
declare -r field=${1:?}; shift
read -r -d '' awk_script << 'EOF'
BEGIN {
IGNORECASE = 1
}
{
field=$FIELD
if (match(field, "VALUE")) {
print
next
}
NEXTTEST
}
EOF
read -r -d '' nexttest_template << 'EOF'
if (match(field, "^VALUE$")) {
print
next
}
NEXTTEST
EOF
awk_script="${awk_script//FIELD/${field}}"
local val nexttest
for val in "$@"; do
nexttest="${nexttest_template//VALUE/${val}}"
awk_script="${awk_script//NEXTTEST/${nexttest}}"
done
awk_script="${awk_script//NEXTTEST/}"
/bin/awk "${awk_script}"
}
version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
get_php_var()
{
declare -r var="$1"; shift
declare -r -a requireds=("$@")
local phpr req
for req in "${requireds[@]}"; do
printf -v phpr '%srequire_once "%s";\n' "${phpr}" "${req}"
done
printf -v phpr '%secho %s;\n' "${phpr}" "${var}"
local from_php
if ! from_php="$(php -derror_reporting=0 -ddisplay_errors=0 -r "${phpr}" 2>/dev/null)"; then
return 1
fi
echo "${from_php}"
}
get_php_var 'MINIMUM_OBFUSCATON_FILESIZE' \
/lr/c/inc/php/downloads.inc.php
is_script_running()
{
declare -r runner=${1:?}
declare -r script=${2:?}
pgrep -f "\b${runner}\b .*\b${script}\b" >/dev/null
}
script_running_count()
{
declare -r runner=${1:?}
declare -r script=${2:?}
pgrep -fc "${runner} .*\b${script}\b"
}
is_program_running()
{
declare -r runner=${1:?}
pgrep -x "${runner}" >/dev/null
}
program_running_count()
{
declare -r runner=${1:?}
pgrep -xc "${runner}"
}
check_service()
{
declare -r service=${1:?}
declare -r -i service_width=${#service}
declare -r -i width=$((cols - service_width - 1))
if ! systemctl -q is-active "$1"; then
printf '%s %*s' "${service}" "${width}" '[ FAILED]'
else
printf '%s %*s' "${service}" "${width}" '[SUCCESS]'
fi
}
static_tail()
{
declare -i prev_len=0
declare -i this_len=0
declare -i cols="$(tput cols)"
printf '\r'
for ((i=0; i < cols; ++i)); do
printf ' '
done
printf '\r'
while IFS= read -r line; do
this_len=${#line}
printf '%s' "${line:0:$cols}"
for ((i=this_len; i < prev_len && i < cols; ++i)); do
printf ' '
done
printf '\r'
prev_len=${this_len}
done
printf '\n'
}
comment()
{
declare -r -a cmd=(printf '<-- %s -->\n')
if (($#)); then
"${cmd[@]}" "$@"
return $?
fi
declare -i status
while read -r line; do
eval set -- $line
"${cmd[@]}" "$@"; status=$?
if ((status)); then
return $status
fi
done
}
comment()
{
declare -r -a cmd=(printf '<-- %s -->\n')
if (($#)); then
"${cmd[@]}" "$*"
return $?
fi
declare -i status
while read -r line; do
"${cmd[@]}" "${line}"; status=$?
if ((status)); then
return $status
fi
done
}
comment()
{
declare arg="$*"
if ((! $#)); then
if ! arg=$(</dev/stdin); then
return 1
fi
fi
printf '<-- %s -->\n' "${arg}"
}
to_upper()
{
if ((! $#)); then
tr '[:lower:]' '[:upper:]'
else
tr '[:lower:]' '[:upper:]' <<< "$*"
fi
}
# Public: Wrapper to make function take arguments or stdin interactive
#
# Turn a command that takes stdin input into a command that takes arguments or
# stdin.
#
# $1 - Separator to follow command and args
# $* - Command and command args
# $? - Separator
# $* - Possible arguments
#
# Example
#
# to_upper()
# {
# arg_or_stdin_from_stdin_cmd -- tr '[:lower:]' '[:upper:]' -- "$@"
# }
arg_or_stdin_from_stdin_cmd()
{
declare -a cmd
local -r separator="${1:?}"; shift
local -i found_end
local arg
while [[ $# -ne 0 ]]; do
arg="$1"; shift
if [[ $arg = "${separator}" ]]; then
found_end=1
break
fi
cmd+=("${arg}")
done
if ((found_end != 1)); then
echo "take_arg_or_stdin: Missing \";\"." 1>&2
return 1
fi
if [[ $# -eq 0 ]]; then
"${cmd[@]}"
else
"${cmd[@]}" <<< "$*"
fi
}
# Public: Wrapper to make function take arguments or stdin interactive
#
# Turn a command that takes stdin input and make a command that takes arguments
# or stdin.
#
# $1 - Separator to follow command and args
# $* - Command and command args
# $? - Separator
# $* - Possible arguments
#
# Example
#
# to_upper()
# {
# arg_or_interact_from_stdin_cmd -- tr '[:lower:]' '[:upper:]' -- "$@"
# }
arg_or_interact_from_stdin_cmd()
{
declare -a cmd
declare -r separator="${1:?}"; shift
local arg
local -i found_end
while [[ $# -ne 0 ]]; do
arg="$1"; shift
if [[ $arg = "${separator}" ]]; then
found_end=1
break
fi
cmd+=("${arg}")
done
if ((found_end != 1)); then
echo "take_arg_or_stdin: Missing \";\"." 1>&2
return 1
fi
if [[ $# -eq 0 ]]; then
while read -r line; do
"${cmd[@]}" <<< "${line}"
done
else
"${cmd[@]}" <<< "$*"
fi
}
# Public: Wrapper to make function take arguments or stdin interactive
#
# Turn a command that takes additional arguments into one that takes arguments
# or stdin line by line.
#
# $1 - Separator to follow command and args
# $* - Command and command args
# $? - Separator
# $* - Possible arguments
#
# Examples
#
# into_lines()
# {
# arg_or_interact_from_arg_cmd -- printf '%s\n' -- "$@"
# }
arg_or_interact_from_arg_cmd()
{
declare -a cmd
declare -r separator="${1:?}"; shift
local arg
local -i found_end
while [[ $# -ne 0 ]]; do
arg="$1"; shift
if [[ $arg = "${separator}" ]]; then
found_end=1
break
fi
cmd+=("${arg}")
done
if ((found_end != 1)); then
echo "take_arg_or_stdin: Missing \";\"." 1>&2
return 1
fi
if [[ $# -eq 0 ]]; then
while read -r line; do
eval set -- $line
"${cmd[@]}" "$@"
done
else
"${cmd[@]}" "$@"
fi
}
is_init_systemd() { [[ "$(ps --pid 1 -o comm=)" = "systemd" ]]; }
declare -r -i cpu_count="$(cat /proc/cpuinfo | grep '^processor[^:]: [0-9]\+' | wc -l)"
print_load_avg_percent()
{
declare -r -i load_avg_x100="$(awk '{print ($1 * 100)}' /proc/loadavg)"
echo "$((load_avg_x100 / cpu_count))"
}
declare -r -i high_load_percent=98
loadavg_percent="$(print_load_avg_percent)"
if ((loadavg_percent < high_load_percent)); then
# ...
fi
#!/bin/bash
declare -r source_dir="/mnt/media/Video/Astro Fighter Sunred"
declare -r target_dir="/mnt/media/Video/TV/Astro Fighter Sunred"
basename.() { basename "$1" | cut -d. -f1; }
extract()
{
declare -r src_file="${1:?}"
declare -r tgt_file="${target_dir}/$(basename. "${src_file}").m4v"
if [[ -e "${tgt_file}" ]]; then
return 0
fi
# rm -f "${tgt_file}"
declare -a args=(
-i "${src_file}"
# Uses https://trac.ffmpeg.org/wiki/Encode/H.264
# Use baseline profile for x264
-profile:v 'baseline'
-pix_fmt yuv420p
# Constant rate factor
-crf 35.0
# web video
-c:v libx264
-x264-params rc_lookahead=30:keyint=500
# web audio
-c:a libfdk_aac
-b:a 128k
-c:s mov_text
"${tgt_file}"
)
echo ffmpeg "${args[@]}"
ffmpeg "${args[@]}" &>/dev/null
}
main()
{
local files="$(find "${source_dir}" -maxdepth 1 -type f -name '*.mkv' | sort)"
echo "File list:"
while IFS= read -r file; do
echo "${file}"
done <<< "${files}"
echo "STARTING..."
while IFS= read -r file; do
echo "${file}"
if ! extract "${file}"; then
echo FAILED
fi
done <<< "${files}"
# while IFS= read -r -d $'\0' file; do
# done < <(find "${source_dir}" -maxdepth 1 -type f -name '*.mkv' -print0 | sort -z)
}
main
stdin_to_arg()
{
local input
if ! input=$(</dev/stdin); then
return 1
fi
"$@" "${input}"
}
less_tail()
{
less +F "$@"
}
get_all_child_processes()
{
declare -r -i pid="$1"
local children
if children="$(pgrep -P "$pid")"; then
for child in ${children}; do
printf '%s\n' ${child}
get_all_child_processes "${child}"
done
fi
}
is_pid_alive()
{
kill -s 0 "${1:?}" 2>/dev/null
}
nice_kill()
{
declare -r -i pid=${1:?}
kill_process_tree "${pid}"
sleep 1
if is_pid_alive "${pid}"; then
kill_process_tree "${pid}" KILL
kill -9 "${pid}"
fi
}
kill_process_tree()
{
declare -r -i pid="${1:?}"
local children
if children="$(pgrep -P "${pid}")"; then
for child in ${children}; do
kill_process_tree "${child}"
done
fi
kill -9 "${pid}"
}
# Public: Setup a command to run every certain number of seconds.
#
# Causes execution to stop while a command is run at an interval. Use with an
# alarm safe sleep if using along with the sleep command. Only one tick is
# supported at a time. Use stop_tick to stop.
#
# WARNING: Signals are only handled by bash when it runs. That means that a
# handler will only fire after a command executes and returns execution to bash,
# which prevents traps from being useful as watchdogs. Any command that could
# take a long time needs to be run as a subprocess, using wait to catch the
# signal.
#
# $1 - Interval in seconds to run command. Takes float as well.
# $2 - Command to run. Uses trap, so command can contain subshell to execute
# each time if single quotes are used. First tick happens after interval,
# not immediately.
# $3 - OPTIONAL Signal to use. Default is USR1.
#
# Examples
#
# start_tick 1 'echo "$(date +%s)"'
# alarm_safe_sleep 3
# echo "STOPPING"
# date
# stop_tick
# alarm_safe_sleep 2
# date
#
# Example output:
# 1496164932
# 1496164933
# STOPPING
# Tue May 30 12:22:14 CDT 2017
# Tue May 30 12:22:16 CDT 2017
declare -i tick_pid=0
start_tick()
{
declare -r rate=${1:?}
declare -r handler=${2:?}
declare -r sig=${3:-USR1}
trap "${handler}" "${sig}"
{
local -i start_tick_child_stop=0
trap "start_tick_child_stop=1" USR1
sleep "$rate"
while ((start_tick_child_stop==0)) &&
kill -s "${sig}" $$ &>/dev/null
do
sleep "$rate"
done
} &
tick_pid=$!
}
# Public: Stop tick initialized in start_tick.
#
# $1 - OPTIONAL Signal to cancel. Should be same used with start_tick.
stop_tick()
{
declare -r sig=${1:-USR1}
trap - "${sig}"
if ((tick_pid > 0)); then
kill -s USR1 $tick_pid
tick_pid=0
fi
}
# Public: Checks if given TCP port is taken.
#
# $1 - IP address
# $2 - Port number
#
# Returns 0 if port is in use.
is_tcp_port_open()
{
declare -r ip="${1:?}"
declare -r port="${2:?}"
lsof -i "tcp@${ip}:${port}" > /dev/null
}
# Public: Waits for port to become available for use.
#
# $1 - IP address
# $2 - Port number
# $3 - Max number of seconds to wait before giving up.
#
# Returns 0 if port became available before timeout reached.
wait_for_port_to_become_available()
{
declare -r ip="${1:?}"
declare -r -i port="${2:?}"
declare -r -i timeout_seconds="${3:?}"
for ((i = 0; i < (timeout_seconds * 4); ++i)); do
if ! is_tcp_port_open "${ip}" "${port}"; then
return 0
fi
sleep 0.25
done
return 1
}
iterate_with_pause_inbetween()
{
declare -r -i times=${1:?}
declare -r pause_seconds=${2:?}
shift 2 # Command follows
if ((times==0)); then
return
fi
for ((i=0; i<times-1; ++i)); do
"$@"
sleep "${pause_seconds}"
done
"$@"
}
example()
{
# Start script in background and wait for completion
${PHP_LOCATION} ${SCRIPT_LOCATION} &
declare -r -i child_pid=$!
if ! wait_with_timeout "${child_pid}" "${min_wait_seconds}"; then
# Script still running, stop it
nice_kill "${child_pid}"
sleep 30
else
# Script completed. Wait a random number of seconds
sleep $(( RANDOM % 541 + 60 ))
fi
}
nice_kill()
{
declare -r -i pid=${1:?}
kill "${pid}"
sleep 1
if is_pid_alive "${pid}"; then
kill -9 "${pid}"
fi
}
is_pid_alive()
{
kill -s 0 "${1:?}" 2>/dev/null
}
wait_with_timeout()
{
declare -r -i pid=${1:?}
declare -r -i timeout_seconds=${2:?}
# Wait for process to complete
for ((i = 0; i < timeout_seconds; ++i)); do
sleep 1
if ! is_pid_alive "${pid}"; then
return 0
fi
done
return 1
}
log_stderr() { cat <<< "$@" 1>&2; }
with_temp_file()
{
declare to_call="${1:?}"
shift
local path
if ! path="$(mktemp)"; then
return 1
fi
trap "rm -f ${tmp_files[*]}" EXIT
"${to_call}" "$@" "${path}"
declare -r -i result=$?
/bin/rm "${path}"
trap - EXIT
return "${result}"
}
rsync_with_password_from_remote()
{
declare -r remote_host="${1:?}"
declare -r remote_user="${2:?}"
declare -r remote_password="${3:?}"
declare -r remote_path="${4:?}"
declare -r local_path="${5:?}"
with_temp_file \
rsync_with_password_inner \
"${remote_user}" \
"${remote_password}" \
"${remote_host:?}:${remote_path}" \
"${local_path}"
}
rsync_with_password_to_remote()
{
declare -r remote_host="${1:?}"
declare -r remote_user="${2:?}"
declare -r remote_password="${3:?}"
declare -r local_path="${4:?}"
declare -r remote_path="${5:?}"
with_temp_file \
rsync_with_password_inner \
"${remote_user}" \
"${remote_password}" \
"${local_path}" \
"${remote_host:?}:${remote_path}"
}
rsync_with_password_inner()
{
declare -r remote_user="${1:?}"
declare -r remote_password="${2:?}"
declare -r from_path="${3:?}"
declare -r to_path="${4:?}"
declare -r password_file="${5:?}"
declare -a rsync_command=(
rsync
--rsh="sshpass -f\"${password_file}\" ssh -l \"${remote_user:?}\""
-av
"${from_path:?}"
"${to_path:?}"
)
log_stderr "Rsync command: ${rsync_command[*]}"
printf '%s\n' "${remote_password}" > "${password_file}"
"${rsync_command[@]}"
declare -i status=$?
if [[ $status -ne 0 ]]; then
log_stderr "Rsync failure: $status"
return 1
fi
return 0
}
human_readable_to_bytes()
{
declare -r human_readable_size="${1:?}"
if ! numfmt --from=auto <<< "${human_readable_size}"; then
return 1
fi
}
bytes_to_mebibytes()
{
declare -r bytes="${1:?}"
if ! numfmt --to=iec-i --suffix=B --format='%.2f' <<< "${bytes}"; then
return 1
fi
}
size_to_human_readable()
{
if ! numfmt --to=iec-i --from=auto "$@"; then
return 1
fi
}
is_pid_alive()
{
kill -s 0 "${1:?}" 2>/dev/null
}
print_info_for_path()
{
declare -r info="$1"
declare -r path="$2"
local format
declare -i rights
if ! [[ -e ${path} ]]; then
return 1
fi
case "${info}" in
size|bytes)
echo $(($(stat -L -c "${format}" "${path}")))
return $?
;;
owner_access_rights)
if ! rights="0$(stat -L -c "%a" "${path}")"; then
return 1
fi
printf '%o' $((rights & 0700 / 0100))
return 0
;;
group_access_rights)
if ! rights="0$(stat -L -c "%a" "${path}")"; then
return 1
fi
printf '%o' $((rights & 070 / 010))
return 0
;;
global_access_rights)
if ! rights="0$(stat -L -c "%a" "${path}")"; then
return 1
fi
printf '%o' $((rights & 07))
return 0
;;
access_rights)
# Prepend 0 for octal
format='0%a'
;;
inode)
format='%i'
;;
mtime)
format='%y'
;;
mtime_seconds)
format='%Y'
;;
mount_point)
format='%m'
;;
optimal_transfer_size)
format='%o'
;;
owner)
format='%G'
;;
user)
format='%U'
;;
*)
return 2
esac
stat -L -c "${format}" "${path}"
}
user_can_write_to_file() {
declare -r user="${1:?missing user}"
declare -r file="${2:?missing file}"
if ! [[ -e ${file} ]]; then
user_can_write_to_file "${user}" "$(dirname "${file}")"
return $?
fi
# Use -L to get information about the target of a symlink,
# not the link itself, as pointed out in the comments
declare -r -a info=( $(stat -L -c "%a %G %U" "$file") )
declare -r -i permissions=0${info[0]}
declare -r group_name=${info[1]}
declare owner_name=${info[2]}
if (((permissions & 0002) != 0)); then
# Everyone has write access
return 0
fi
if (((permissions & 0020) != 0)); then
# Is user in group with access?
for g in $(groups "$user"); do
if [[ $group_name == "$g" ]]; then
return 0
fi
done
elif (((permissions & 0200) != 0)); then
if [[ $user == "$owner_name" ]]; then
return 0
fi
fi
return 1
}
print_free_bytes_on_path()
{
declare path="${1:?}"
if ! [[ -e ${path} ]]; then
return 1
fi
echo $(($(stat -fc "%f*%S" "${path}")))
}
print_free_kilobytes_on_path()
{
declare path="${1:?}"
if ! [[ -e ${path} ]]; then
return 1
fi
echo $(($(stat -fc "%f*%S" "${path}") / 1024))
}
is_a_function() { [[ $(type -t "$1") == "function" ]]; }
readonly this_script_filename="$(basename "${BASH_SOURCE[0]}")"
log()
{
cat <<< "$@"
printf "%s %s >>> %s\n" "$(date "+%m/%d/%Y %H:%M:%S")" "${this_script_filename}" "$*" >> /var/log/linkright.log
logger -p user.notice -t "${this_script_filename}" "$@"
}
err()
{
cat <<< "$@" 1>&2
printf "%s %s >>> %s\n" "$(date "+%m/%d/%Y %H:%M:%S")" "${this_script_filename}" "$*" >> /var/log/linkright.log
logger -p user.error -t "${this_script_filename}" "$@"
}
log "writing to stdout"
err "writing to stderr"
# Public: Join array into string with a delimiter.
#
# $1 - Delimiter
# $2+ - Items to join
#
# Example
#
# print_join_array ", " $(seq 1 10)
# Output: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
#
# Outputs joined string to stdout.
print_join_array()
{
local d=$1
shift
echo -n "$1"
shift
printf "%s" "${@/#/$d}"
}
a="The cat sat on the mat"
b=cat
strindex() {
x="${1%%$2*}"
[[ $x = $1 ]] && echo -1 || echo ${#x}
}
strindex "$a" "$b" # prints 4
strindex "$a" foo # prints -1
has_prefix()
{
declare -r prefix="$1"
declare -r str="$2"
[[ "$str" == "$prefix"* ]]
}
Will echo original value if prefix does not match.
strip_matching_prefix()
{
declare -r prefix="$1"
declare -r str="$2"
echo ${str#$prefix}
}
chr() {
printf \\$(printf '%03o' $1)
}
ord() {
printf '%d' "'$1"
}
w=80
while IFS= read -r line; do
if ((${#line} > w)); then
ind=''
while IFS= read -r inner; do
printf '%s%s' "$ind" "${inner}"
printf -v ind ' \\\n '
done < <(fold -sw$w <<< "$line")
echo
else
echo "${line}"
fi
done <<< "$lines"
Name | Code |
---|---|
Black | 0;30 |
Blue | 0;34 |
Green | 0;32 |
Cyan | 0;36 |
Red | 0;31 |
Purple | 0;35 |
Brown | 0;33 |
Light Gray | 0;37 |
Dark Gray | 1;30 |
Light Blue | 1;34 |
Light Green | 1;32 |
Light Cyan | 1;36 |
Light Red | 1;31 |
Light Purple | 1;35 |
Yellow | 1;33 |
White | 1;37 |
Enclose with:
- Start
- \[\033[
- End
- \]
Set env var GREP_COLORS with the code and pass “–color=always” as an argument.
Example:
tail -f myfwlog | GREP_COLORS='ms=01;36' egrep --color=always 'ssh|$' | GREP_COLORS='ms=01;31' egrep -i --color=always 'drop|deny|$'
You still need to watch out for:
- Backquotes
- Backquoted backticks
- SQL statements
- Nested statements
- Backquoted backticks
%s/`\(.*?\)`/$(\1)/gc