This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| fuss:bash [2017/02/22 18:30] – external edit 127.0.0.1 | fuss:bash [2025/10/21 23:26] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 22: | Line 22: | ||
| - | ====== Increment in Bash Arrays | + | ====== Increment in Bash s ====== |
| <code bash> | <code bash> | ||
| Line 57: | Line 57: | ||
| </ | </ | ||
| - | ====== Split a String to an Array and other Array Manipulations ====== | + | ====== Split a String to an and other Manipulations ====== |
| The following code: | The following code: | ||
| Line 120: | Line 120: | ||
| in order to retrieve the temperature of the first core ('' | in order to retrieve the temperature of the first core ('' | ||
| + | ====== Get a List of Files by File Extension from an Array ====== | ||
| + | Via a function '' | ||
| + | <code bash> | ||
| + | # By: https:// | ||
| + | listFiles() { | ||
| + | local -n _OUTPUT_=" | ||
| + | local -n _DIRECTORY_=" | ||
| + | local -n _TYPES_=" | ||
| + | local _FILTER_ | ||
| + | |||
| + | _FILTER_=" | ||
| + | _FILTER_=" | ||
| + | while read -d $' | ||
| + | _OUTPUT_+=( " | ||
| + | done < <( find " | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Example call: | ||
| + | <code bash> | ||
| + | DIR_PATH=/ | ||
| + | TYPES=(mkv mp4 avi) | ||
| + | |||
| + | listFiles FILES DIR_PATH TYPES | ||
| + | </ | ||
| + | |||
| + | ====== Recursively Convert Video Files ====== | ||
| + | |||
| + | As the title would imply, the following script will recursively convert video files to conform to the following parameters: | ||
| + | |||
| + | * MPEG-4 | ||
| + | * 720p or less, | ||
| + | * AVC (H.264), | ||
| + | * AAC | ||
| + | |||
| + | Its main design was meant to convert recorded TV shows where a set quality should not necessarily exceed the mentioned parameters for the sake of storage conservation. | ||
| + | |||
| + | <code bash> | ||
| + | #!/bin/bash | ||
| + | ########################################################################### | ||
| + | ## Copyright (C) Wizardry and Steamworks 2021 - License: GNU GPLv3 ## | ||
| + | ########################################################################### | ||
| + | # The script is a way of converting all video files recursively in a # | ||
| + | # directory tree in order to conform to the following parameters: | ||
| + | # * MPEG-4 (MP4) # | ||
| + | # * 480p or less # | ||
| + | # * AVC (H.264) | ||
| + | # * AAC # | ||
| + | # # | ||
| + | # Notes: | ||
| + | # * There are some added optimizations such as switching to just # | ||
| + | # converting to MPEG-4 in case no more than a container change is # | ||
| + | # necessary | ||
| + | # * This is a long-running script and best ran within a detachable | ||
| + | # terminal such as tmux or screen. | ||
| + | # * This is a bash script that will not run on other shells. | ||
| + | ########################################################################### | ||
| + | |||
| + | ########################################################################### | ||
| + | ## CONFIGURATION | ||
| + | ########################################################################### | ||
| + | |||
| + | # The path to the ffmpeg binary. | ||
| + | FFMPEG='/ | ||
| + | # The ffmpeg flags to use when converting. | ||
| + | # veryfast vs. veryslow https:// | ||
| + | FFMPEG_OPTIONS=`tr -d ' | ||
| + | -c:v libx264 \ | ||
| + | -crf 23 \ | ||
| + | -level 3.1 \ | ||
| + | -preset veryfast \ | ||
| + | -tune film \ | ||
| + | -sws_flags lanczos \ | ||
| + | -sn \ | ||
| + | -c:a libfdk_aac \ | ||
| + | -vbr 5 \ | ||
| + | -q:a 2 \ | ||
| + | -ac 2 \ | ||
| + | -b:a 128k \ | ||
| + | -c:s mov_text \ | ||
| + | -map 0:v:0 \ | ||
| + | -map 0:a:0 \ | ||
| + | -map_metadata -1 \ | ||
| + | -movflags +faststart \ | ||
| + | -strict -2 | ||
| + | EOF` | ||
| + | # The maximal height of the video files. | ||
| + | FFMPEG_HEIGHT=480 | ||
| + | # The maximal integral CPU percentage that will be allowed to be used. | ||
| + | FFMPEG_CPU=600 | ||
| + | # The file types to convert. | ||
| + | TYPES=(mp4 mkv avi) | ||
| + | |||
| + | ########################################################################### | ||
| + | ## INTERNALS | ||
| + | ########################################################################### | ||
| + | |||
| + | if [ -z " | ||
| + | echo " | ||
| + | exit 1 | ||
| + | fi | ||
| + | |||
| + | DIR_PATH=" | ||
| + | |||
| + | # By: https:// | ||
| + | listFiles() { | ||
| + | local -n _OUTPUT_=" | ||
| + | local -n _DIRECTORY_=" | ||
| + | local -n _TYPES_=" | ||
| + | local _FILTER_ | ||
| + | |||
| + | _FILTER_=" | ||
| + | _FILTER_=" | ||
| + | while read -d $' | ||
| + | _OUTPUT_+=( " | ||
| + | done < <( find " | ||
| + | } | ||
| + | |||
| + | TEMP_FILE="" | ||
| + | |||
| + | # Cleanup on file termination. | ||
| + | trap '{ | ||
| + | rm -rf " | ||
| + | exit 0 | ||
| + | }' KILL QUIT TERM EXIT INT HUP | ||
| + | |||
| + | # Iterate over all matching files. | ||
| + | FILES=() | ||
| + | listFiles FILES DIR_PATH TYPES | ||
| + | for FILE in " | ||
| + | FILE_NAME=`basename " | ||
| + | DIR_NAME=`dirname " | ||
| + | FILE_EXTENSION=`echo " | ||
| + | TEMP_FILE="/ | ||
| + | INFO=`mediainfo " | ||
| + | HAS_AVC=`echo " | ||
| + | HAS_AAC=`echo " | ||
| + | HAS_MP4=`echo " | ||
| + | HAS_2CH=`echo " | ||
| + | HAS_TXT=`mediainfo $FILE | grep " | ||
| + | HEIGHT=`mediainfo --Inform=" | ||
| + | | ||
| + | # If all criteria matches then proceed to the next file. | ||
| + | if [[ $HEIGHT -le $FFMPEG_HEIGHT ]] && \ | ||
| + | [[ ! -z $HAS_AVC ]] && \ | ||
| + | [[ ! -z $HAS_AAC ]] && \ | ||
| + | [[ ! -z $HAS_MP4 ]] && \ | ||
| + | [[ ! -z $HAS_2CH ]] && \ | ||
| + | [[ -z $HAS_TXT ]] | ||
| + | then | ||
| + | echo " | ||
| + | continue | ||
| + | fi | ||
| + | |||
| + | FFMPEG_ACTION=$FFMPEG_OPTIONS | ||
| + | # Resize down to $FFMPEG_HEIGHT or less depending on the height of the file. | ||
| + | if [[ $HEIGHT -gt $FFMPEG_HEIGHT ]]; then | ||
| + | FFMPEG_SIZE=" | ||
| + | fi | ||
| + | | ||
| + | # Start the conversion. | ||
| + | echo -n " | ||
| + | # -loglevel quiet | ||
| + | " | ||
| + | FFMPEG_PID=$! | ||
| + | cpulimit -q -z -e ffmpeg -l $FFMPEG_CPU >/ | ||
| + | T1=`date +%s` | ||
| + | wait $FFMPEG_PID | ||
| + | T2=`date +%s` | ||
| + | RUNTIME=$(( T2 - T1 )) | ||
| + | T_H=$((RUNTIME / 3600)) | ||
| + | T_M=$((RUNTIME % 3600 / 60 )) | ||
| + | T_S=$(((RUNTIME % 3600) % 60 )) | ||
| + | if [[ " | ||
| + | echo "Done ($T_H: | ||
| + | # Permute the files on successful conversion. | ||
| + | rm " | ||
| + | cp " | ||
| + | if [ $? -ne 0 ]; then | ||
| + | echo " | ||
| + | continue | ||
| + | fi | ||
| + | rm " | ||
| + | else | ||
| + | echo " | ||
| + | fi | ||
| + | done | ||
| + | |||
| + | </ | ||
| + | |||
| + | ====== Netstat without Netstat ====== | ||
| + | |||
| + | The following code allows a user to print out a list of established connections without having to resort to using the command-line tool '' | ||
| + | |||
| + | <code bash> | ||
| + | # | ||
| + | ########################################################################### | ||
| + | ## Copyright (C) Wizardry and Steamworks 2022 - License: GNU GPLv3 ## | ||
| + | ## Please see: http:// | ||
| + | ## rights of fair usage, the disclaimer and warranty conditions. | ||
| + | ########################################################################### | ||
| + | # # | ||
| + | # This script processes the TCP connections from / | ||
| + | # output a list of locally listening TCP IP addresses and ports (sockets) # | ||
| + | # mapped to a list of remotely connecting TCP IP addresses and ports. | ||
| + | # # | ||
| + | # The script is written with a minimalistic environment in mind with just # | ||
| + | # a few tools available that should be present within most busyboxes. | ||
| + | # # | ||
| + | # For best results, disable IPv6 on the executing machine (via the kernel # | ||
| + | # command line) because some TCP connections are processed through IPv6 # | ||
| + | # even if they are IPv4 such that / | ||
| + | # # | ||
| + | ########################################################################### | ||
| + | |||
| + | decode () { | ||
| + | DUO=$1 | ||
| + | LEN=`echo -n $DUO | wc -c` | ||
| + | OUT="" | ||
| + | for X in `seq 0 2 $(($LEN-2))`; | ||
| + | OUT=$((16# | ||
| + | done | ||
| + | OUT=${OUT:: | ||
| + | echo $OUT | ||
| + | } | ||
| + | |||
| + | tcp_state() { | ||
| + | OUT="" | ||
| + | for SET in $1; do | ||
| + | IP_HEX=`echo $SET | awk -F':' | ||
| + | PO_HEX=`echo $SET | awk -F':' | ||
| + | IP=`decode $IP_HEX` | ||
| + | PO=$((16# | ||
| + | OUT=$OUT" | ||
| + | done | ||
| + | echo $OUT | ||
| + | } | ||
| + | |||
| + | TMP_STATE=/ | ||
| + | trap '{ rm -rf $TMP_STATE; }' KILL QUIT TERM EXIT INT HUP | ||
| + | cat / | ||
| + | |||
| + | TCP_STATE_LEN=`cat $TMP_STATE | wc -l` | ||
| + | LISTEN_LOCAL=`cat $TMP_STATE | \ | ||
| + | awk 'NR==1 { for (i=1; | ||
| + | tail +2` | ||
| + | REMOTE_ADDRS=`cat $TMP_STATE | \ | ||
| + | awk 'NR==1 { for (i=1; | ||
| + | tail +2` | ||
| + | |||
| + | LISTEN=`tcp_state " | ||
| + | REMOTE=`tcp_state " | ||
| + | |||
| + | for I in `seq 1 1 $(($TCP_STATE_LEN-1))`; | ||
| + | A=`echo $LISTEN | awk -v var=$I '{ print $var }'` | ||
| + | B=`echo $REMOTE | awk -v var=$I '{ print $var }'` | ||
| + | echo $A" - "$B | ||
| + | done | ||
| + | |||
| + | </ | ||
| + | |||
| + | ====== Publishing Incoming TCP Connections to MQTT Broker ====== | ||
| + | |||
| + | The following script is meant to run in the background and poll ''/ | ||
| + | |||
| + | < | ||
| + | server -> FQDN hostname of the machine that the script is running on, | ||
| + | this -> the listening IP address followed by the port separated by a colon | ||
| + | that -> the connecting IP address followed by the port separated by a colon | ||
| + | </ | ||
| + | |||
| + | <code bash> | ||
| + | # | ||
| + | ########################################################################### | ||
| + | ## Copyright (C) Wizardry and Steamworks 2022 - License: GNU GPLv3 ## | ||
| + | ## Please see: http:// | ||
| + | ## rights of fair usage, the disclaimer and warranty conditions. | ||
| + | ########################################################################### | ||
| + | # # | ||
| + | # This script publishes the state of TCP connections to the machine over # | ||
| + | # a specified port to an MQTT broker. The script leverages polling | ||
| + | # / | ||
| + | # just the most basic commands making it suitable for various cases. | ||
| + | # # | ||
| + | ########################################################################### | ||
| + | |||
| + | ########################################################################### | ||
| + | # | ||
| + | ########################################################################### | ||
| + | |||
| + | # path to the mosquitto publishing tool | ||
| + | MOSQUITTO_PUBLISHER=/ | ||
| + | # the host name of the MQTT broker | ||
| + | MOSQUITTO_HOST=iot.internal | ||
| + | # the MQTT topic to publish on | ||
| + | MOSQUITTO_TOPIC=arcade | ||
| + | # the TCP port to monitor for connections | ||
| + | MONITOR_PORT=55435 | ||
| + | |||
| + | # the time to sleep between polling | ||
| + | SLEEP_TIME=1 | ||
| + | |||
| + | ########################################################################### | ||
| + | # | ||
| + | ########################################################################### | ||
| + | |||
| + | decode () { | ||
| + | DUO=$1 | ||
| + | LEN=`echo -n $DUO | wc -c` | ||
| + | OUT="" | ||
| + | for X in `seq 0 2 $(($LEN-2))`; | ||
| + | OUT=$((16# | ||
| + | done | ||
| + | OUT=${OUT:: | ||
| + | echo $OUT | ||
| + | } | ||
| + | |||
| + | tcp_state() { | ||
| + | OUT="" | ||
| + | for SET in $1; do | ||
| + | IP_HEX=`echo $SET | awk -F':' | ||
| + | PO_HEX=`echo $SET | awk -F':' | ||
| + | IP=`decode $IP_HEX` | ||
| + | PO=$((16# | ||
| + | OUT=$OUT" | ||
| + | done | ||
| + | echo $OUT | ||
| + | } | ||
| + | |||
| + | |||
| + | TMP_STATE=/ | ||
| + | RUN=1 | ||
| + | trap '{ rm -rf $TMP_STATE; RUN=0; }' KILL QUIT TERM EXIT INT HUP SIGUSR1 | ||
| + | |||
| + | while [ $RUN -ne 0 ]; do | ||
| + | cat / | ||
| + | TCP_STATE_LEN=`cat $TMP_STATE | wc -l` | ||
| + | LISTEN_LOCAL=`cat $TMP_STATE | \ | ||
| + | awk 'NR==1 { for (i=1; | ||
| + | tail +2` | ||
| + | REMOTE_ADDRS=`cat $TMP_STATE | \ | ||
| + | awk 'NR==1 { for (i=1; | ||
| + | tail +2` | ||
| + | |||
| + | LISTEN=`tcp_state " | ||
| + | REMOTE=`tcp_state " | ||
| + | |||
| + | for I in `seq 1 1 $(($TCP_STATE_LEN-1))`; | ||
| + | X=`echo $LISTEN | awk -v var=$I '{ print $var }'` | ||
| + | Y=`echo $REMOTE | awk -v var=$I '{ print $var }'` | ||
| + | NETPLAY=`echo $X $Y | grep $MONITOR_PORT | grep -v 0.0.0.0` | ||
| + | if [ ! -z " | ||
| + | THIS=`echo $NETPLAY | awk '{ print $1 }'` | ||
| + | THAT=`echo $NETPLAY | awk '{ print $2 }'` | ||
| + | | ||
| + | $MOSQUITTO_PUBLISHER \ | ||
| + | -h $MOSQUITTO_HOST \ | ||
| + | -t $MOSQUITTO_TOPIC \ | ||
| + | -m "{ \" | ||
| + | fi | ||
| + | done | ||
| + | sleep $SLEEP_TIME | ||
| + | done | ||
| + | |||
| + | </ | ||
| + | |||
| + | ====== Text Activity Spinners in Bash ====== | ||
| + | |||
| + | In order to use the [[/ | ||
| + | |||
| + | <code bash> | ||
| + | # | ||
| + | |||
| + | ASCII_SPINNER=( " | ||
| + | while [ 1 = 1 ]; do | ||
| + | CAR=" | ||
| + | CDR=(" | ||
| + | ASCII_SPINNER=(${CDR[@]} ${CAR}) | ||
| + | echo -n -e " | ||
| + | done | ||
| + | |||
| + | </ | ||
| + | |||
| + | The logic is pretty straight-forward: | ||
| + | * the spinner is loaded as a bash array, | ||
| + | * the array is shuffled by shifting the first element, keeping a copy it and then adding the first element to the end of the spinner array, | ||
| + | * the first element is printed out, without any newlines and by prepending a carriage-return '' | ||
| + | |||
| + | ====== Snippet that Generates Subtitles by Reading a File ====== | ||
| + | |||
| + | The following script fragment: | ||
| + | <code bash> | ||
| + | COUNT=0 && while [ 1 ]; do while read i; do echo -e " | ||
| + | </ | ||
| + | will poll the contents of the file '' | ||
| + | |||
| + | Here is some example output: | ||
| + | < | ||
| + | 1 | ||
| + | 00: | ||
| + | 56dB ± 1.5 | ||
| + | |||
| + | 2 | ||
| + | 00: | ||
| + | 55dB ± 1.5 | ||
| + | |||
| + | 3 | ||
| + | 00: | ||
| + | 55dB ± 1.5 | ||
| + | |||
| + | 4 | ||
| + | 00: | ||
| + | 56dB ± 1.5 | ||
| + | |||
| + | 5 | ||
| + | 00: | ||
| + | 56dB ± 1.5 | ||
| + | |||
| + | 6 | ||
| + | 00: | ||
| + | 57dB ± 1.5 | ||
| + | |||
| + | 7 | ||
| + | 00: | ||
| + | 57dB ± 1.5 | ||
| + | |||
| + | 8 | ||
| + | 00: | ||
| + | 56dB ± 1.5 | ||
| + | |||
| + | 9 | ||
| + | 00: | ||
| + | 56dB ± 1.5 | ||
| + | |||
| + | 10 | ||
| + | 00: | ||
| + | 56dB ± 1.5 | ||
| + | |||
| + | 11 | ||
| + | 00: | ||
| + | 54dB ± 1.5 | ||
| + | |||
| + | 12 | ||
| + | 00: | ||
| + | 54dB ± 1.5 | ||
| + | |||
| + | 13 | ||
| + | 00: | ||
| + | 59dB ± 1.5 | ||
| + | |||
| + | 14 | ||
| + | 00: | ||
| + | 59dB ± 1.5 | ||
| + | |||
| + | 15 | ||
| + | 00: | ||
| + | 57dB ± 1.5 | ||
| + | |||
| + | </ | ||
| + | |||
| + | The snippet is useful, for example, if some tool is running at the same time in the background (ie: ffmpeg or mencoder) that records some live stream, and then this script can be used to generate subtitles for the live stream being recorded. | ||
| + | |||
| + | For example, if the file being generated by recording the live stream is named '' | ||
| + | <code bash> | ||
| + | ... > stream.sub | ||
| + | </ | ||
| + | such that when '' | ||
| + | |||
| + | ====== Pipe Heredoc ====== | ||
| + | |||
| + | Heredoc can be used to include block-text in a bash script listing verbatim and sometimes it is nifty to be able to pipe the included block-text to a command. For example, imagine that one would want to include a script inside a Dockerfile to be expanded when the image is built without involving any filesystem files to be included with the '' | ||
| + | * write the script, | ||
| + | * encode the script to Base64, | ||
| + | * include the Base64 in the Dockerfile using heredoc and pipe it to a command that decodes the Base64 text and saves it to a file | ||
| + | |||
| + | Here is what it would look like within a Dockerfile: | ||
| + | < | ||
| + | RUN mkdir -p / | ||
| + | IyEvdXNyL2Jpbi9lbnYgYmFzaAoKIyBkZWZpbmUgZGVmYXVsdCBwYXJhbWV0ZXJzClRPUl9TT0NL | ||
| + | PSIke1RPUl9ETlNfUE9SVDotMC4wLjAuMDo5MDUzfSIKQ0hFQ0tfQ0lSQ1VJVF9QT1JUPSIke0NI | ||
| + | RUNLX0NJUkNVSVRfUE9SVDotNzA1MH0iCkpBM19QT1JUPSIke0pBM19QT1JUOi05MDQwfSIKIyAx | ||
| + | ... | ||
| + | EOT | ||
| + | |||
| + | </ | ||
| + | which is meant to perform the following via the '' | ||
| + | * creates the directory ''/ | ||
| + | * reads the large block of text with seemingly random characters using heredoc up to the '' | ||
| + | |||
| + | Counter-intuitively one would want to echo or '' | ||
| + | |||
| + | Note that the quotes around the '' | ||
For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.