Converting Bat to Bash
I am wanting to convert a bat noscript into bash and I want to ensure this is right.
If someone could review the changes and let me know if this is proper that would be absolutely amazing.
Commenting Below the Original code then Converted code
----------------------------------------------
@echo off
noscript COS Regional Flasher
echo.**********************************************************************
echo.
echo. Oneplus 13 - COS Regional Flasher
echo. Originally two noscripts by FTH PHONE 1902 and Venkay
echo. modified by docnok63 and Jonas Salo
echo.
@echo off
cd %~dp0
set fastboot=Platform-Tools\fastboot.exe
if not exist "%fastboot%" echo "%fastboot%" not found. & pause & exit /B 1
set file=vendor_boot
echo.************************ START FLASH ************************
%fastboot% --set-active=a
:: Flash the fastboot images first
%fastboot% flash boot COS_FILES_HERE\boot.img
%fastboot% flash dtbo COS_FILES_HERE\dtbo.img
%fastboot% flash init_boot COS_FILES_HERE\init_boot.img
%fastboot% flash modem COS_FILES_HERE\modem.img
%fastboot% flash recovery COS_FILES_HERE\recovery.img
%fastboot% flash vbmeta COS_FILES_HERE\vbmeta.img
%fastboot% flash vbmeta_system COS_FILES_HERE\vbmeta_system.img
%fastboot% flash vbmeta_vendor COS_FILES_HERE\vbmeta_vendor.img
%fastboot% flash vendor_boot COS_FILES_HERE\vendor_boot.img
:: Check if super.img exists
if exist "super.img" (
%fastboot% flash super super.img
) else (
echo super.img not found. Skipping super.img...
)
:: Reboot to fastbootd
%fastboot% reboot fastboot
echo. ******************* REBOOTING TO FASTBOOTD *******************
ECHO #################################
ECHO # Hit English on Phone #
ECHO #################################
pause
:: Excluded files list (these should not be flashed again)
set excluded_images=boot.img dtbo.img init_boot.img modem.img recovery.img vbmeta.img vbmeta_system.img vbmeta_vendor.img vendor_boot.img my_bigball.img my_carrier.img my_company.img my_engineering.img my_heytap.img my_manifest.img my_preload.img my_product.img my_region.img my_stock.img odm.img product.img system.img system_dlkm.img system_ext.img vendor.img vendor_dlkm.img
:: Loop through all .img files in COS_FILES_HERE but skip excluded images
for %%G in (COS_FILES_HERE\*.img) do (
echo %excluded_images% | findstr /i /c:"%%~nxG" >nul
if errorlevel 1 (
echo Flashing %%~nG...
%fastboot% flash --slot=all "%%~nG" "%%G"
)
)
:: Define partitions list outside the IF block
set "partitions=my_bigball my_carrier my_engineering my_heytap my_manifest my_product my_region my_stock odm product system system_dlkm system_ext vendor vendor_dlkm my_company my_preload"
:: Check if super.img exists, if not, delete, create & flash logical partitions
if not exist "super.img" (
for %%P in (%partitions%) do (
%fastboot% delete-logical-partition %%P_a
%fastboot% delete-logical-partition %%P_b
%fastboot% delete-logical-partition %%P_a-cow
%fastboot% delete-logical-partition %%P_b-cow
%fastboot% create-logical-partition %%P_a 1
%fastboot% create-logical-partition %%P_b 1
%fastboot% flash %%P COS_FILES_HERE\%%P.img
)
) else (
echo super.img found. Logical partition flashes skipped...
)
echo.********************** CHECK ABOVE FOR ERRORS **************************
echo.************** IF ERRORS, DO NOT BOOT INTO SYSTEM **********************
:: Ask if user wants full Chinese bloat or not
choice /C YN /M "Do you want full chinese bloat?:"
if errorlevel 2 (
echo ****************** FLASHING OOS .305 my_preload ******************
%fastboot% delete-logical-partition my_preload_a
%fastboot% delete-logical-partition my_preload_b
%fastboot% delete-logical-partition my_preload_a-cow
%fastboot% delete-logical-partition my_preload_b-cow
%fastboot% create-logical-partition my_preload_a 1
%fastboot% create-logical-partition my_preload_b 1
%fastboot% flash my_preload
I am wanting to convert a bat noscript into bash and I want to ensure this is right.
If someone could review the changes and let me know if this is proper that would be absolutely amazing.
Commenting Below the Original code then Converted code
----------------------------------------------
@echo off
noscript COS Regional Flasher
echo.**********************************************************************
echo.
echo. Oneplus 13 - COS Regional Flasher
echo. Originally two noscripts by FTH PHONE 1902 and Venkay
echo. modified by docnok63 and Jonas Salo
echo.
@echo off
cd %~dp0
set fastboot=Platform-Tools\fastboot.exe
if not exist "%fastboot%" echo "%fastboot%" not found. & pause & exit /B 1
set file=vendor_boot
echo.************************ START FLASH ************************
%fastboot% --set-active=a
:: Flash the fastboot images first
%fastboot% flash boot COS_FILES_HERE\boot.img
%fastboot% flash dtbo COS_FILES_HERE\dtbo.img
%fastboot% flash init_boot COS_FILES_HERE\init_boot.img
%fastboot% flash modem COS_FILES_HERE\modem.img
%fastboot% flash recovery COS_FILES_HERE\recovery.img
%fastboot% flash vbmeta COS_FILES_HERE\vbmeta.img
%fastboot% flash vbmeta_system COS_FILES_HERE\vbmeta_system.img
%fastboot% flash vbmeta_vendor COS_FILES_HERE\vbmeta_vendor.img
%fastboot% flash vendor_boot COS_FILES_HERE\vendor_boot.img
:: Check if super.img exists
if exist "super.img" (
%fastboot% flash super super.img
) else (
echo super.img not found. Skipping super.img...
)
:: Reboot to fastbootd
%fastboot% reboot fastboot
echo. ******************* REBOOTING TO FASTBOOTD *******************
ECHO #################################
ECHO # Hit English on Phone #
ECHO #################################
pause
:: Excluded files list (these should not be flashed again)
set excluded_images=boot.img dtbo.img init_boot.img modem.img recovery.img vbmeta.img vbmeta_system.img vbmeta_vendor.img vendor_boot.img my_bigball.img my_carrier.img my_company.img my_engineering.img my_heytap.img my_manifest.img my_preload.img my_product.img my_region.img my_stock.img odm.img product.img system.img system_dlkm.img system_ext.img vendor.img vendor_dlkm.img
:: Loop through all .img files in COS_FILES_HERE but skip excluded images
for %%G in (COS_FILES_HERE\*.img) do (
echo %excluded_images% | findstr /i /c:"%%~nxG" >nul
if errorlevel 1 (
echo Flashing %%~nG...
%fastboot% flash --slot=all "%%~nG" "%%G"
)
)
:: Define partitions list outside the IF block
set "partitions=my_bigball my_carrier my_engineering my_heytap my_manifest my_product my_region my_stock odm product system system_dlkm system_ext vendor vendor_dlkm my_company my_preload"
:: Check if super.img exists, if not, delete, create & flash logical partitions
if not exist "super.img" (
for %%P in (%partitions%) do (
%fastboot% delete-logical-partition %%P_a
%fastboot% delete-logical-partition %%P_b
%fastboot% delete-logical-partition %%P_a-cow
%fastboot% delete-logical-partition %%P_b-cow
%fastboot% create-logical-partition %%P_a 1
%fastboot% create-logical-partition %%P_b 1
%fastboot% flash %%P COS_FILES_HERE\%%P.img
)
) else (
echo super.img found. Logical partition flashes skipped...
)
echo.********************** CHECK ABOVE FOR ERRORS **************************
echo.************** IF ERRORS, DO NOT BOOT INTO SYSTEM **********************
:: Ask if user wants full Chinese bloat or not
choice /C YN /M "Do you want full chinese bloat?:"
if errorlevel 2 (
echo ****************** FLASHING OOS .305 my_preload ******************
%fastboot% delete-logical-partition my_preload_a
%fastboot% delete-logical-partition my_preload_b
%fastboot% delete-logical-partition my_preload_a-cow
%fastboot% delete-logical-partition my_preload_b-cow
%fastboot% create-logical-partition my_preload_a 1
%fastboot% create-logical-partition my_preload_b 1
%fastboot% flash my_preload
OOS_FILES_HERE\my_preload.img
echo ********* Debloat image flashed, Hit any key to continue *********
pause
) else (
echo ********************* CHINESE BLOAT ALREADY FLASHED **************************
echo ********* Keeping bloated my_preload, Hit any key to continue *********
pause
)
:: If super.img was not flashed, exit here but keep window open
if not exist "super.img" (
choice /C YN /M "Do you want to wipe data?:"
if errorlevel 2 (
echo *********************** NO NEED TO WIPE DATA ****************************
echo ***** Flashing complete. Hit any key to reboot the phone to Android *****
pause
%fastboot% reboot
exit /B 0
)
if errorlevel 1 (
echo ****************** FLASHING COMPLETE *****************
echo Wipe data by tapping Format Data on the screen, enter the code, and press format data.
echo Phone will automatically reboot into Android after wipe is done.
pause
exit /B 0
)
)
:: Ask if flashing from ColorOS (press Y for yes or N for no)
echo Are you flashing from ColorOS or Want to WIPE DATA?? (y/n)
choice /c YN /n > nul
:: Check if the user pressed 'y' or 'n'
if errorlevel 2 (
echo *********************** NO NEED TO WIPE DATA ****************************
echo ***** Flashing complete. Hit any key to reboot the phone to Android *****
pause
%fastboot% reboot
) else if errorlevel 1 (
echo ****************** FLASHING COMPLETE *****************
echo Wipe data by tapping Format Data on the screen, enter the code, and press format data.
echo Phone will automatically reboot into Android after wipe is done.
)
pause
----------------------------------------------
----------------------------------------------
Converted code
----------------------------------------------
----------------------------------------------
#!/bin/bash
# Set noscript (not directly equivalent in bash, but can be simulated)
echo "COS Regional Flasher"
echo "**********************************************************************"
echo ""
echo " Oneplus 13 - COS Regional Flasher "
echo " Originally two noscripts by FTH PHONE 1902 and Venkay"
echo " modified by docnok63 and Jonas Salo"
echo ""
# Get the directory of the noscript
SCRIPT_DIR=$(dirname "$0")
# Set fastboot path
FASTBOOT="$SCRIPT_DIR/Platform-Tools/fastboot"
# Check if fastboot exists
if [ ! -x "$FASTBOOT" ]; then
echo "Error: $FASTBOOT not found."
exit 1
fi
# Set file (not used in the original noscript, so keeping it as a variable)
FILE="vendor_boot"
echo "************************ START FLASH ************************"
# Flash the fastboot images first
$FASTBOOT --set-active=a
$FASTBOOT flash boot COS_FILES_HERE/boot.img
$FASTBOOT flash dtbo COS_FILES_HERE/dtbo.img
$FASTBOOT flash init_boot COS_FILES_HERE/init_boot.img
$FASTBOOT flash modem COS_FILES_HERE/modem.img
$FASTBOOT flash recovery COS_FILES_HERE/recovery.img
$FASTBOOT flash vbmeta COS_FILES_HERE/vbmeta.img
$FASTBOOT flash vbmeta_system COS_FILES_HERE/vbmeta_system.img
$FASTBOOT flash vbmeta_vendor COS_FILES_HERE/vbmeta_vendor.img
$FASTBOOT flash vendor_boot COS_FILES_HERE/vendor_boot.img
# Check if super.img exists
if [ -f "super.img" ]; then
$FASTBOOT flash super super.img
else
echo "super.img not found. Skipping super.img..."
fi
# Reboot to fastbootd
$FASTBOOT reboot fastboot
echo " ******************* REBOOTING TO FASTBOOTD *******************"
echo "#################################"
echo "# Hit English on Phone #"
echo "#################################"
read -p "Press Enter to continue..."
# Excluded files list
EXCLUDED_IMAGES="boot.img dtbo.img init_boot.img modem.img recovery.img vbmeta.img vbmeta_system.img vbmeta_vendor.img vendor_boot.img my_bigball.img my_carrier.img my_company.img my_engineering.img my_heytap.img my_manifest.img my_preload.img my_product.img my_region.img my_stock.img odm.img product.img system.img system_dlkm.img system_ext.img vendor.img
echo ********* Debloat image flashed, Hit any key to continue *********
pause
) else (
echo ********************* CHINESE BLOAT ALREADY FLASHED **************************
echo ********* Keeping bloated my_preload, Hit any key to continue *********
pause
)
:: If super.img was not flashed, exit here but keep window open
if not exist "super.img" (
choice /C YN /M "Do you want to wipe data?:"
if errorlevel 2 (
echo *********************** NO NEED TO WIPE DATA ****************************
echo ***** Flashing complete. Hit any key to reboot the phone to Android *****
pause
%fastboot% reboot
exit /B 0
)
if errorlevel 1 (
echo ****************** FLASHING COMPLETE *****************
echo Wipe data by tapping Format Data on the screen, enter the code, and press format data.
echo Phone will automatically reboot into Android after wipe is done.
pause
exit /B 0
)
)
:: Ask if flashing from ColorOS (press Y for yes or N for no)
echo Are you flashing from ColorOS or Want to WIPE DATA?? (y/n)
choice /c YN /n > nul
:: Check if the user pressed 'y' or 'n'
if errorlevel 2 (
echo *********************** NO NEED TO WIPE DATA ****************************
echo ***** Flashing complete. Hit any key to reboot the phone to Android *****
pause
%fastboot% reboot
) else if errorlevel 1 (
echo ****************** FLASHING COMPLETE *****************
echo Wipe data by tapping Format Data on the screen, enter the code, and press format data.
echo Phone will automatically reboot into Android after wipe is done.
)
pause
----------------------------------------------
----------------------------------------------
Converted code
----------------------------------------------
----------------------------------------------
#!/bin/bash
# Set noscript (not directly equivalent in bash, but can be simulated)
echo "COS Regional Flasher"
echo "**********************************************************************"
echo ""
echo " Oneplus 13 - COS Regional Flasher "
echo " Originally two noscripts by FTH PHONE 1902 and Venkay"
echo " modified by docnok63 and Jonas Salo"
echo ""
# Get the directory of the noscript
SCRIPT_DIR=$(dirname "$0")
# Set fastboot path
FASTBOOT="$SCRIPT_DIR/Platform-Tools/fastboot"
# Check if fastboot exists
if [ ! -x "$FASTBOOT" ]; then
echo "Error: $FASTBOOT not found."
exit 1
fi
# Set file (not used in the original noscript, so keeping it as a variable)
FILE="vendor_boot"
echo "************************ START FLASH ************************"
# Flash the fastboot images first
$FASTBOOT --set-active=a
$FASTBOOT flash boot COS_FILES_HERE/boot.img
$FASTBOOT flash dtbo COS_FILES_HERE/dtbo.img
$FASTBOOT flash init_boot COS_FILES_HERE/init_boot.img
$FASTBOOT flash modem COS_FILES_HERE/modem.img
$FASTBOOT flash recovery COS_FILES_HERE/recovery.img
$FASTBOOT flash vbmeta COS_FILES_HERE/vbmeta.img
$FASTBOOT flash vbmeta_system COS_FILES_HERE/vbmeta_system.img
$FASTBOOT flash vbmeta_vendor COS_FILES_HERE/vbmeta_vendor.img
$FASTBOOT flash vendor_boot COS_FILES_HERE/vendor_boot.img
# Check if super.img exists
if [ -f "super.img" ]; then
$FASTBOOT flash super super.img
else
echo "super.img not found. Skipping super.img..."
fi
# Reboot to fastbootd
$FASTBOOT reboot fastboot
echo " ******************* REBOOTING TO FASTBOOTD *******************"
echo "#################################"
echo "# Hit English on Phone #"
echo "#################################"
read -p "Press Enter to continue..."
# Excluded files list
EXCLUDED_IMAGES="boot.img dtbo.img init_boot.img modem.img recovery.img vbmeta.img vbmeta_system.img vbmeta_vendor.img vendor_boot.img my_bigball.img my_carrier.img my_company.img my_engineering.img my_heytap.img my_manifest.img my_preload.img my_product.img my_region.img my_stock.img odm.img product.img system.img system_dlkm.img system_ext.img vendor.img
vendor_dlkm.img"
# Loop through all .img files in COS_FILES_HERE but skip excluded images
for IMG in COS_FILES_HERE/*.img; do
IMG_NAME=$(basename "$IMG")
if ! echo "$EXCLUDED_IMAGES" | grep -iq "$IMG_NAME"; then
echo "Flashing $IMG_NAME..."
$FASTBOOT flash --slot=all "$IMG_NAME" "$IMG"
fi
done
# Define partitions list outside the IF block
PARTITIONS="my_bigball my_carrier my_engineering my_heytap my_manifest my_product my_region my_stock odm product system system_dlkm system_ext vendor vendor_dlkm my_company my_preload"
# Check if super.img exists, if not, delete, create & flash logical partitions
if [ ! -f "super.img" ]; then
for P in $PARTITIONS; do
$FASTBOOT delete-logical-partition "$P"_a
$FASTBOOT delete-logical-partition "$P"_b
$FASTBOOT delete-logical-partition "$P"_a-cow
$FASTBOOT delete-logical-partition "$P"_b-cow
$FASTBOOT create-logical-partition "$P"_a 1
$FASTBOOT create-logical-partition "$P"_b 1
$FASTBOOT flash "$P" COS_FILES_HERE/"$P".img
done
else
echo "super.img found. Logical partition flashes skipped..."
fi
echo "********************** CHECK ABOVE FOR ERRORS **************************
echo "************** IF ERRORS, DO NOT BOOT INTO SYSTEM **********************"
# Ask if user wants full Chinese bloat or not
read -p "Do you want full chinese bloat? (y/n): " CHOICE
CHOICE=$(echo "$CHOICE" | tr '[:upper:]' '[:lower:]')
if [ "$CHOICE" = "y" ]; then
echo "***************** FLASHING OOS .305 my_preload ******************"
$FASTBOOT delete-logical-partition my_preload_a
$FASTBOOT delete-logical-partition my_preload_b
$FASTBOOT delete-logical-partition my_preload_a-cow
$FASTBOOT delete-logical-partition my_preload_b-cow
$FASTBOOT create-logical-partition my_preload_a 1
$FASTBOOT create-logical-partition my_preload_b 1
$FASTBOOT flash my_preload OOS_FILES_HERE/my_preload.img
echo "******** Debloat image flashed, Hit any key to continue *********"
read -p ""
else
echo "******************** CHINESE BLOAT ALREADY FLASHED **************************"
echo "******** Keeping bloated my_preload, Hit any key to continue *********"
read -p ""
fi
# If super.img was not flashed, exit here but keep window open
if [ ! -f "super.img" ]; then
read -p "Do you want to wipe data? (y/n): " CHOICE
CHOICE=$(echo "$CHOICE" | tr '[:upper:]' '[:lower:]')
if [ "$CHOICE" = "y" ]; then
echo "*********************** NO NEED TO WIPE DATA ****************************"
echo "***** Flashing complete. Hit any key to reboot the phone to Android *****"
read -p ""
$FASTBOOT reboot
exit 0
elif [ "$CHOICE" = "n" ]; then
echo "***************** FLASHING COMPLETE *****************"
echo "Wipe data by tapping Format Data on the screen, enter the code, and press format data."
echo "Phone will automatically reboot into Android after wipe is done."
read -p ""
exit 0
fi
fi
# Ask if flashing from ColorOS (press Y for yes or N for no)
read -p "Are you flashing from ColorOS or Want to WIPE DATA?? (y/n): " CHOICE
CHOICE=$(echo "$CHOICE" | tr '[:upper:]' '[:lower:]')
# Check if the user pressed 'y' or 'n'
if [ "$CHOICE" = "y" ]; then
echo "*********************** NO NEED TO WIPE DATA ****************************"
echo "***** Flashing complete. Hit any key to reboot the phone to Android *****"
read -p ""
$FASTBOOT reboot
elif [ "$CHOICE" = "n" ]; then
echo "***************** FLASHING COMPLETE *****************"
echo "Wipe data by tapping Format Data on the screen, enter the code, and press format data."
echo "Phone will automatically reboot into Android after wipe is done."
fi
read -p "Press Enter to exit..."
----------------------------------------------
https://redd.it/1kg5ege
@r_bash
# Loop through all .img files in COS_FILES_HERE but skip excluded images
for IMG in COS_FILES_HERE/*.img; do
IMG_NAME=$(basename "$IMG")
if ! echo "$EXCLUDED_IMAGES" | grep -iq "$IMG_NAME"; then
echo "Flashing $IMG_NAME..."
$FASTBOOT flash --slot=all "$IMG_NAME" "$IMG"
fi
done
# Define partitions list outside the IF block
PARTITIONS="my_bigball my_carrier my_engineering my_heytap my_manifest my_product my_region my_stock odm product system system_dlkm system_ext vendor vendor_dlkm my_company my_preload"
# Check if super.img exists, if not, delete, create & flash logical partitions
if [ ! -f "super.img" ]; then
for P in $PARTITIONS; do
$FASTBOOT delete-logical-partition "$P"_a
$FASTBOOT delete-logical-partition "$P"_b
$FASTBOOT delete-logical-partition "$P"_a-cow
$FASTBOOT delete-logical-partition "$P"_b-cow
$FASTBOOT create-logical-partition "$P"_a 1
$FASTBOOT create-logical-partition "$P"_b 1
$FASTBOOT flash "$P" COS_FILES_HERE/"$P".img
done
else
echo "super.img found. Logical partition flashes skipped..."
fi
echo "********************** CHECK ABOVE FOR ERRORS **************************
echo "************** IF ERRORS, DO NOT BOOT INTO SYSTEM **********************"
# Ask if user wants full Chinese bloat or not
read -p "Do you want full chinese bloat? (y/n): " CHOICE
CHOICE=$(echo "$CHOICE" | tr '[:upper:]' '[:lower:]')
if [ "$CHOICE" = "y" ]; then
echo "***************** FLASHING OOS .305 my_preload ******************"
$FASTBOOT delete-logical-partition my_preload_a
$FASTBOOT delete-logical-partition my_preload_b
$FASTBOOT delete-logical-partition my_preload_a-cow
$FASTBOOT delete-logical-partition my_preload_b-cow
$FASTBOOT create-logical-partition my_preload_a 1
$FASTBOOT create-logical-partition my_preload_b 1
$FASTBOOT flash my_preload OOS_FILES_HERE/my_preload.img
echo "******** Debloat image flashed, Hit any key to continue *********"
read -p ""
else
echo "******************** CHINESE BLOAT ALREADY FLASHED **************************"
echo "******** Keeping bloated my_preload, Hit any key to continue *********"
read -p ""
fi
# If super.img was not flashed, exit here but keep window open
if [ ! -f "super.img" ]; then
read -p "Do you want to wipe data? (y/n): " CHOICE
CHOICE=$(echo "$CHOICE" | tr '[:upper:]' '[:lower:]')
if [ "$CHOICE" = "y" ]; then
echo "*********************** NO NEED TO WIPE DATA ****************************"
echo "***** Flashing complete. Hit any key to reboot the phone to Android *****"
read -p ""
$FASTBOOT reboot
exit 0
elif [ "$CHOICE" = "n" ]; then
echo "***************** FLASHING COMPLETE *****************"
echo "Wipe data by tapping Format Data on the screen, enter the code, and press format data."
echo "Phone will automatically reboot into Android after wipe is done."
read -p ""
exit 0
fi
fi
# Ask if flashing from ColorOS (press Y for yes or N for no)
read -p "Are you flashing from ColorOS or Want to WIPE DATA?? (y/n): " CHOICE
CHOICE=$(echo "$CHOICE" | tr '[:upper:]' '[:lower:]')
# Check if the user pressed 'y' or 'n'
if [ "$CHOICE" = "y" ]; then
echo "*********************** NO NEED TO WIPE DATA ****************************"
echo "***** Flashing complete. Hit any key to reboot the phone to Android *****"
read -p ""
$FASTBOOT reboot
elif [ "$CHOICE" = "n" ]; then
echo "***************** FLASHING COMPLETE *****************"
echo "Wipe data by tapping Format Data on the screen, enter the code, and press format data."
echo "Phone will automatically reboot into Android after wipe is done."
fi
read -p "Press Enter to exit..."
----------------------------------------------
https://redd.it/1kg5ege
@r_bash
Reddit
From the bash community on Reddit
Explore this post and more from the bash community
Can someone help whipping up a quick, compact oneliner to diff / compare config files with old versions after updates?
I want to see the changes from the old to the new config files on Debian (ucf-*, dpkg-new) or Arch (original name vs pacnew).
If I take Debian, I can easily find the files to compare with with ` sudo find /etc/ \( -name '*.dpkg-*' -o -name '*.ucf-*' \)`. So far, so good. On Arch, it wouldn't be much different with pacnew files. The file to compare them with (with `diff -uN`) would be the find result minus the file extension (everything after the last dot).
Somehow, I can't get this to work in a compact oneliner. Can someone help me out here? I don't want to write a multiline noscript with variables, just a quick oneliner.
https://redd.it/1kgsyyl
@r_bash
I want to see the changes from the old to the new config files on Debian (ucf-*, dpkg-new) or Arch (original name vs pacnew).
If I take Debian, I can easily find the files to compare with with ` sudo find /etc/ \( -name '*.dpkg-*' -o -name '*.ucf-*' \)`. So far, so good. On Arch, it wouldn't be much different with pacnew files. The file to compare them with (with `diff -uN`) would be the find result minus the file extension (everything after the last dot).
Somehow, I can't get this to work in a compact oneliner. Can someone help me out here? I don't want to write a multiline noscript with variables, just a quick oneliner.
https://redd.it/1kgsyyl
@r_bash
Reddit
From the bash community on Reddit
Explore this post and more from the bash community
Stupid Associative Array tricks
The Associative Array in Bash can be used to tag a variable and its core value with any amount of additional information. An associative array is created with the declare built-in by the
$ declare -A ASSOCARRAY
$ declare -p ASSOCARRAY
declare -A ASSOCARRAY=()
While ordinary variables can be promoted to Indexed Arrays by assignment to the variable using array notation, attempts to do so to create an associative array fail by only promoting to a indexed array and setting element zero(0).
$ declare VAR=value
$ declare -p VAR
declare -- VAR=value
$ VAR[member]=issue
$ declare -p VAR
declare -a VAR=([0]=issue)
This is due to the index of the array notation being interpretted in an arithmetic context in which all non-numeric objects become the numeric value zero(0), resulting in
$ VAR[member]=issue
being semanticly identical to
$ VAR[0]=issue
and promoting the variable VAR to an indexed array.
There are no other means, besides the -A argument to declare, to create an associative array. They cannot be created by assigning to a non-existent variable name.
Once an associative array variable exists, it can be assigned to and referenced just as any other array variable with the added ability to assign to arbitrary strings as "indices".
$ declare -A VAR
$ declare -p VAR
declare -A VAR
$ VAR=value
$ declare -p VAR
declare -A VAR=([0]="value" )
$ VAR[1]=one
$ VAR[string]=something
$ declare -p VAR
declare -A VAR=([string]="something" [1]="one" [0]="value" )
They can be the subject of a naked reference:
$ echo $VAR
value
or with an array reference
$ echo ${VAR[1]}
one
An application of this could be creating a URL variable for a remote resource and tagging it with the checksums of that resource once it is retrieved.
$ declare -A WORDS=https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words
$ WORDS[crc32]=6534cce8
$ WORDS[md5]=722a8ad72b48c26a0f71a2e1b79f33fd
$ WORDS[sha256]=1ec8230beef2a7c955742b089fc3cea2833866cf5482bf018d7c4124eef104bd
$ declare -p WORDS
declare -A WORDS=([0]="https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words" [crc32]="6534cce8" [md5]="722a8ad72b48c26a0f71a2e1b79f33fd" [sha256]="1ec8230beef2a7c955742b089fc3cea2833866cf5482bf018d7c4124eef104bd" )
The root value of the variable, the zero(0) index, can still be referenced normally
$ wget $WORDS
and it will behave only as the zeroth index value. Later, however, it can be referenced with the various checksums to check the integrity of the retrieved file.
$ [[ "$(crc32 words)" == "${WORDS[crc32]}" ]] || echo 'crc32 failed'
$ [[ "$(md5sum words | cut -f 1)" == "${WORDS[md5]}" ]] || echo 'md5 failed'
$ [[ "$(sha256sum words | cut -f 1 -d ' ')" == "${WORDS[sha256]}" ]] || echo 'sha5 failed'
If none of the failure messages were printed, each of the programs regenerated the same checksum as that which was stored along with the URL in the Bash associative array variable WORDS.
We can prove it by corrupting one and trying again.
$ WORDS[md5]='corrupted'
$ [[ "$(md5sum words | cut -f 1)" == "${WORDS[md5]}" ]] || echo 'md5 failed'
md5 failed
The value of the md5 member no longer matches what the md5sum program generates.
The associative array variable used in the above manner can be used with all of the usual associative array dereference mechanisms. For instance, getting the list of all of the keys and filtering out the root member effectively retrieves a list of all of the hashing algorithms with which the resource has been tagged.
$ echo ${!WORDS[@]} | sed -E 's/(^| )0( |$)/ /'
crc32 md5 sha256
This list could now be used with a looping function to dynamicly allow any hashing program to be used.
verifyhashes () {
local -i retval=0
local -n var="${1}"
local file="${2}"
for hash in $(sed -E 's/(^| )0(
The Associative Array in Bash can be used to tag a variable and its core value with any amount of additional information. An associative array is created with the declare built-in by the
-A argument:$ declare -A ASSOCARRAY
$ declare -p ASSOCARRAY
declare -A ASSOCARRAY=()
While ordinary variables can be promoted to Indexed Arrays by assignment to the variable using array notation, attempts to do so to create an associative array fail by only promoting to a indexed array and setting element zero(0).
$ declare VAR=value
$ declare -p VAR
declare -- VAR=value
$ VAR[member]=issue
$ declare -p VAR
declare -a VAR=([0]=issue)
This is due to the index of the array notation being interpretted in an arithmetic context in which all non-numeric objects become the numeric value zero(0), resulting in
$ VAR[member]=issue
being semanticly identical to
$ VAR[0]=issue
and promoting the variable VAR to an indexed array.
There are no other means, besides the -A argument to declare, to create an associative array. They cannot be created by assigning to a non-existent variable name.
Once an associative array variable exists, it can be assigned to and referenced just as any other array variable with the added ability to assign to arbitrary strings as "indices".
$ declare -A VAR
$ declare -p VAR
declare -A VAR
$ VAR=value
$ declare -p VAR
declare -A VAR=([0]="value" )
$ VAR[1]=one
$ VAR[string]=something
$ declare -p VAR
declare -A VAR=([string]="something" [1]="one" [0]="value" )
They can be the subject of a naked reference:
$ echo $VAR
value
or with an array reference
$ echo ${VAR[1]}
one
An application of this could be creating a URL variable for a remote resource and tagging it with the checksums of that resource once it is retrieved.
$ declare -A WORDS=https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words
$ WORDS[crc32]=6534cce8
$ WORDS[md5]=722a8ad72b48c26a0f71a2e1b79f33fd
$ WORDS[sha256]=1ec8230beef2a7c955742b089fc3cea2833866cf5482bf018d7c4124eef104bd
$ declare -p WORDS
declare -A WORDS=([0]="https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words" [crc32]="6534cce8" [md5]="722a8ad72b48c26a0f71a2e1b79f33fd" [sha256]="1ec8230beef2a7c955742b089fc3cea2833866cf5482bf018d7c4124eef104bd" )
The root value of the variable, the zero(0) index, can still be referenced normally
$ wget $WORDS
and it will behave only as the zeroth index value. Later, however, it can be referenced with the various checksums to check the integrity of the retrieved file.
$ [[ "$(crc32 words)" == "${WORDS[crc32]}" ]] || echo 'crc32 failed'
$ [[ "$(md5sum words | cut -f 1)" == "${WORDS[md5]}" ]] || echo 'md5 failed'
$ [[ "$(sha256sum words | cut -f 1 -d ' ')" == "${WORDS[sha256]}" ]] || echo 'sha5 failed'
If none of the failure messages were printed, each of the programs regenerated the same checksum as that which was stored along with the URL in the Bash associative array variable WORDS.
We can prove it by corrupting one and trying again.
$ WORDS[md5]='corrupted'
$ [[ "$(md5sum words | cut -f 1)" == "${WORDS[md5]}" ]] || echo 'md5 failed'
md5 failed
The value of the md5 member no longer matches what the md5sum program generates.
The associative array variable used in the above manner can be used with all of the usual associative array dereference mechanisms. For instance, getting the list of all of the keys and filtering out the root member effectively retrieves a list of all of the hashing algorithms with which the resource has been tagged.
$ echo ${!WORDS[@]} | sed -E 's/(^| )0( |$)/ /'
crc32 md5 sha256
This list could now be used with a looping function to dynamicly allow any hashing program to be used.
verifyhashes () {
local -i retval=0
local -n var="${1}"
local file="${2}"
for hash in $(sed -E 's/(^| )0(
|$)/ /' <<< "${!var@}"); do
prog=''
if which ${hash} &>/dev/null; then
prog="${hash}"
elif which ${hash}sum &>/dev/null; then
prog="${hash}sum"
else
printf 'Hash type %s not supported.\n' "${hash}" >&2
fi
[ -n "${prog}" ] \
&& [ "$(${prog} "${file}" | cut -f 1 -d ' ')" != "${var[${hash}}" ]] \
&& printf '%s failed!\n' "${hash}" >&2 \
&& retval=1
done
return $retval
}
$ verifyhashes WORDS words
$ echo $?
0
This function uses the relatively new Bash syntax of the named reference (`local -n`). This allows me to pass in the name of the variable the function is to operate with, but inside the function, I have access to it via a single variable named "var", and `var` retains all of the traits of its named parent variable, because it effectively is the named variable.
This function is complicated by the fact that some programs add the suffix "-sum" to the name of their algorithm, and some don't. And some output their hash followed by white space followed by the file name, and some don't. This mechanism handles both cases. Any hashing algorithm which follows the pattern of <algo> or <algo>sum for the name of their generation program, takes the name of the file on which to operate, and which produces a single line of output which starts with the resultant hash can be used with the above design pattern.
With nothing output, all hashes passed and the return value was zero. Let's add a nonsense hash type.
$ WORDS[hash]=browns
$ verifyhashes WORDS words
Hash type hash not supported.
$ echo $?
0
When the key 'hash' is encountered for which no program named 'hash' or 'hashsum' can be found in the environment, the error message is sent to stderr, but it does not result in a failure return value. However, if we corrupt a valid hash type:
$ WORDSmd5=corrupted
$ verifyhashes WORDS words
md5 failed!
$ echo $?
1
When a given hash fails, a message is sent to stderr, and the return value is non-zero.
This technique can also be used to create something akin to a structure in the C language. Conceptually, if we had a C struct like:
struct person
{
char * firstname;
char middleinitial;
char * lastname;
uint8t age;
char * phonenumber;
};
We could create a variable of that type and initialize it like so:
struct person owner = { "John", 'Q', "Public", 25, "123-456-7890" };
Or, using the designated initializer syntax:
struct person owner = {
.firstname = "John",
.middleinitial = 'Q',
.lastname = "Public",
.age = 25,
.phonenumber = "123-456-7890"
};
In Bash, we can just use the associative array initializer to achieve much the same convenience.
declare -A owner=(
first_name="John"
middle_initial='Q'
last_name="Public"
age=25
phone_number="123-456-7890"
)
Of course, we also have all of the usual Bash syntactic restrictions. No commas. No space around the equals sign. Have to use array index notation, not struct member notation, but the effect is the same, all of the data is packaged together as a single unit.
$ declare -p owner
declare -A owner=(middle_initial="Q" last_name="Public" first_name="John" phone_number="123-456-7890" age="25" )
$ echo "${ownerfirst_name}'s phone number is ${ownerphone_number}."
John's phone number is 123-456-7890.
Here we do see one drawback of the Bash associative array. Unlike an indexed array where the key list syntax will always output the valid keys in ascending numerical order, the associative array key order is essentially random. Even from noscript run to noscript run, the order can change, so if it matters, they should be sorted manually.
And it goes without saying that an associative array is ideal for storing a bunch of key-value
prog=''
if which ${hash} &>/dev/null; then
prog="${hash}"
elif which ${hash}sum &>/dev/null; then
prog="${hash}sum"
else
printf 'Hash type %s not supported.\n' "${hash}" >&2
fi
[ -n "${prog}" ] \
&& [ "$(${prog} "${file}" | cut -f 1 -d ' ')" != "${var[${hash}}" ]] \
&& printf '%s failed!\n' "${hash}" >&2 \
&& retval=1
done
return $retval
}
$ verifyhashes WORDS words
$ echo $?
0
This function uses the relatively new Bash syntax of the named reference (`local -n`). This allows me to pass in the name of the variable the function is to operate with, but inside the function, I have access to it via a single variable named "var", and `var` retains all of the traits of its named parent variable, because it effectively is the named variable.
This function is complicated by the fact that some programs add the suffix "-sum" to the name of their algorithm, and some don't. And some output their hash followed by white space followed by the file name, and some don't. This mechanism handles both cases. Any hashing algorithm which follows the pattern of <algo> or <algo>sum for the name of their generation program, takes the name of the file on which to operate, and which produces a single line of output which starts with the resultant hash can be used with the above design pattern.
With nothing output, all hashes passed and the return value was zero. Let's add a nonsense hash type.
$ WORDS[hash]=browns
$ verifyhashes WORDS words
Hash type hash not supported.
$ echo $?
0
When the key 'hash' is encountered for which no program named 'hash' or 'hashsum' can be found in the environment, the error message is sent to stderr, but it does not result in a failure return value. However, if we corrupt a valid hash type:
$ WORDSmd5=corrupted
$ verifyhashes WORDS words
md5 failed!
$ echo $?
1
When a given hash fails, a message is sent to stderr, and the return value is non-zero.
This technique can also be used to create something akin to a structure in the C language. Conceptually, if we had a C struct like:
struct person
{
char * firstname;
char middleinitial;
char * lastname;
uint8t age;
char * phonenumber;
};
We could create a variable of that type and initialize it like so:
struct person owner = { "John", 'Q', "Public", 25, "123-456-7890" };
Or, using the designated initializer syntax:
struct person owner = {
.firstname = "John",
.middleinitial = 'Q',
.lastname = "Public",
.age = 25,
.phonenumber = "123-456-7890"
};
In Bash, we can just use the associative array initializer to achieve much the same convenience.
declare -A owner=(
first_name="John"
middle_initial='Q'
last_name="Public"
age=25
phone_number="123-456-7890"
)
Of course, we also have all of the usual Bash syntactic restrictions. No commas. No space around the equals sign. Have to use array index notation, not struct member notation, but the effect is the same, all of the data is packaged together as a single unit.
$ declare -p owner
declare -A owner=(middle_initial="Q" last_name="Public" first_name="John" phone_number="123-456-7890" age="25" )
$ echo "${ownerfirst_name}'s phone number is ${ownerphone_number}."
John's phone number is 123-456-7890.
Here we do see one drawback of the Bash associative array. Unlike an indexed array where the key list syntax will always output the valid keys in ascending numerical order, the associative array key order is essentially random. Even from noscript run to noscript run, the order can change, so if it matters, they should be sorted manually.
And it goes without saying that an associative array is ideal for storing a bunch of key-value
pairs as a mini-database. It is the equivalent to the hash table or dictionary data types of other languages.
\# EOF
https://redd.it/1kh6y9x
@r_bash
\# EOF
https://redd.it/1kh6y9x
@r_bash
Reddit
From the bash community on Reddit
Explore this post and more from the bash community
Just looking for general suggestions about my bootstrap noscript
Hi, I'm not looking for any advice in particular, just want to share my noscript and learn from people more experienced than me.
It's just a noscript I wrote some time ago to install some packages on my other Linux machines that evolved into a bootstrap for my system. Let me know what you think about it.
Here is the noscript
https://redd.it/1khfjq7
@r_bash
Hi, I'm not looking for any advice in particular, just want to share my noscript and learn from people more experienced than me.
It's just a noscript I wrote some time ago to install some packages on my other Linux machines that evolved into a bootstrap for my system. Let me know what you think about it.
Here is the noscript
https://redd.it/1khfjq7
@r_bash
GitHub
dotfiles/drxboot-arch.sh at main · druxorey/dotfiles
A minimalist repository of my dotfiles for arch linux and a bootstrap to automate its installation - druxorey/dotfiles
More Stupid Associative Array Tricks with Dynamic Array Names (Tiny Database)
Here's a somewhat contrived example of using named references and also using dynamically created variables - sort fo like an array of associative arrays. It also simulates daat entry from a terminal and will also run using terminal daat entered by hand, but it shows a good mix of named references and also dynamic variable definition, wihch i use a fair amout when getting variables set in side a configuration file such as:
options="-a -b -c"
directory="${HOME}/data"
file="some_data_file.data"
I can read the config file and set dynamic variables using the names. Reading and splitting them with a read and using IFS='=', rather than using an eval. I can also give them values by doing normal variable expansion using an echo:
declare ${config_var}=$( echo "${rvalue}" )
Anyway here's a fun little (well, kinda long with comments, maybe overengineered too) demo noscript I hacked together to show some os the dynamic naming and also using the `local -n` along with `${!variable}`.
#!/usr/bin/env bash
#------------------------------------------------------------------------------
# Bash Dynamic Array Names in Memory Database Example
#------------------------------------------------------------------------------
# This noscript demonstrates advanced Bash programming concepts by implementing
# a simple in-memory database using arrays. Key concepts demonstrated include:
#
# 1. Dynamic Variable Names
# - Uses bash's indirect reference capabilities
# - Shows how to create and manage variables dynamically
# - Demonstrates proper use of 'declare' for array creation
#
# 2. Associative Arrays
# - Each record is stored as an associative array (person_N)
# - Shows how to properly initialize and manage associative arrays
# - Demonstrates key-value pair storage and retrieval
#
# 3. Name References (nameref)
# - Uses 'declare -n' for creating references to arrays
# - Shows proper scoping and cleanup of namerefs
# - Demonstrates why namerefs need to be recreated in loops
#
# 4. Record Management
# - Implements basic CRUD operations (Create, Read, Update, Delete)
# - Uses a status array (person_index) to track record state
# - Shows soft-delete functionality (marking records as deleted)
#
# 5. Input Handling
# - Demonstrates file denoscriptor manipulation
# - Shows how to handle both interactive and automated input
# - Implements proper input validation
#
# Usage Examples:
# ./test -i # Interactive mode: Enter data manually
# ./test -t # Test mode: Uses predefined test data
#
# Database Structure:
# person_N - Associative array for each record (N = index)
# person_index - Tracks record status (E=exists, D=deleted)
# person_attributes - Defines the schema (field names)
# person_attr_display - Maps internal names to display names
# Will store state of each person record
# E = active employee, D = deleted employee
# Other flags could be added for indicating other states
declare -a person_index=()
# Define the attributes each person record will have
# This array defines the "schema" for our person records
# Simply add an attribute name to extend the table
declare -a person_attributes=(
"employee_id" # Unique identifier
"LastName" # Family name
"FirstName" # Given name
"email" # Contact email
)
# Display name mapping for prettier output
declare -A person_attr_display=(
[employee_id]="Employee ID"
[LastName]="Last Name"
[FirstName]="First Name"
[email]="Email"
)
# Test data for demonstration purposes and simulating user terminal input
TEST_DATA=$(cat << 'DATA'
Doe
John
john.doe@example.com
y
Smith
Jane
jane.smith@example.com
y
Here's a somewhat contrived example of using named references and also using dynamically created variables - sort fo like an array of associative arrays. It also simulates daat entry from a terminal and will also run using terminal daat entered by hand, but it shows a good mix of named references and also dynamic variable definition, wihch i use a fair amout when getting variables set in side a configuration file such as:
options="-a -b -c"
directory="${HOME}/data"
file="some_data_file.data"
I can read the config file and set dynamic variables using the names. Reading and splitting them with a read and using IFS='=', rather than using an eval. I can also give them values by doing normal variable expansion using an echo:
declare ${config_var}=$( echo "${rvalue}" )
Anyway here's a fun little (well, kinda long with comments, maybe overengineered too) demo noscript I hacked together to show some os the dynamic naming and also using the `local -n` along with `${!variable}`.
#!/usr/bin/env bash
#------------------------------------------------------------------------------
# Bash Dynamic Array Names in Memory Database Example
#------------------------------------------------------------------------------
# This noscript demonstrates advanced Bash programming concepts by implementing
# a simple in-memory database using arrays. Key concepts demonstrated include:
#
# 1. Dynamic Variable Names
# - Uses bash's indirect reference capabilities
# - Shows how to create and manage variables dynamically
# - Demonstrates proper use of 'declare' for array creation
#
# 2. Associative Arrays
# - Each record is stored as an associative array (person_N)
# - Shows how to properly initialize and manage associative arrays
# - Demonstrates key-value pair storage and retrieval
#
# 3. Name References (nameref)
# - Uses 'declare -n' for creating references to arrays
# - Shows proper scoping and cleanup of namerefs
# - Demonstrates why namerefs need to be recreated in loops
#
# 4. Record Management
# - Implements basic CRUD operations (Create, Read, Update, Delete)
# - Uses a status array (person_index) to track record state
# - Shows soft-delete functionality (marking records as deleted)
#
# 5. Input Handling
# - Demonstrates file denoscriptor manipulation
# - Shows how to handle both interactive and automated input
# - Implements proper input validation
#
# Usage Examples:
# ./test -i # Interactive mode: Enter data manually
# ./test -t # Test mode: Uses predefined test data
#
# Database Structure:
# person_N - Associative array for each record (N = index)
# person_index - Tracks record status (E=exists, D=deleted)
# person_attributes - Defines the schema (field names)
# person_attr_display - Maps internal names to display names
# Will store state of each person record
# E = active employee, D = deleted employee
# Other flags could be added for indicating other states
declare -a person_index=()
# Define the attributes each person record will have
# This array defines the "schema" for our person records
# Simply add an attribute name to extend the table
declare -a person_attributes=(
"employee_id" # Unique identifier
"LastName" # Family name
"FirstName" # Given name
"email" # Contact email
)
# Display name mapping for prettier output
declare -A person_attr_display=(
[employee_id]="Employee ID"
[LastName]="Last Name"
[FirstName]="First Name"
[email]="Email"
)
# Test data for demonstration purposes and simulating user terminal input
TEST_DATA=$(cat << 'DATA'
Doe
John
john.doe@example.com
y
Smith
Jane
jane.smith@example.com
y
Johnson
Robert
robert.johnson@example.com
y
Williams
Mary
mary.williams@example.com
y
Brown
James
james.brown@example.com
n
DATA
)
# Function to generate unique employee IDs
# Combines the record index with a random number to ensure uniqueness
# Args: $1 - The record index (1-based)
generate_employee_number() {
printf "%d%06d" "$(( $1 + 1 ))" "$((RANDOM % 1000000))"
}
# Function to get the current number of records
# Used for both array sizing and new record creation
get_index() {
local current_idx
current_idx=${#person_index[@]}
echo "$current_idx"
}
# Function to create a new person record
# Args: $1 - The index for the new record
# Creates a new associative array and marks it as active
create_person() {
local current_idx=$1
declare -gA "person_${current_idx}"
person_index+=("E")
}
# Function to convert from 1-based (user) index to 0-based (internal) index
# Args: $1 - User-facing index (1-based)
# Returns: Internal array index (0-based) or -1 if invalid
to_internal_index() {
local user_idx=$1
if [[ "$user_idx" =~ ^[1-9][0-9]*$ ]] && ((user_idx <= $(get_index))); then
echo "$((user_idx - 1))"
else
echo "-1"
fi
}
# Function to mark a record as deleted
# Implements soft-delete by setting status flag to 'D'
# Args: $1 - User-facing index (1-based)
delete_person() {
local user_idx=$1
local internal_idx
internal_idx=$(to_internal_index "$user_idx")
if [[ $internal_idx -ge 0 ]]; then
person_index[$internal_idx]="D"
return 0
else
echo "Error: Invalid person number $user_idx" >&2
return 1
fi
}
# Function to check if a record exists and is active
# Args: $1 - Internal index (0-based)
# Returns: true if record exists and is active, false otherwise
is_person_active() {
local idx=$1
[[ $idx -lt $(get_index) && "${person_index[$idx]}" == "E" ]]
}
# Function to update a person's attribute
# Uses nameref to directly modify the associative array
# Args: $1 - Array name to update
# $2 - Attribute name
# $3 - New value
update_person_attribute() {
local -n person_array_name=$1
local attr=$2
local value=$3
person_array_name[$attr]="$value"
}
# Function to display all active person records
# Demonstrates:
# - Proper nameref handling in loops
# - Format string usage for consistent output
# - Conditional record filtering (skipping deleted)
display_people() {
local fmt=" %-12s: %s\n"
local separator="------------------------"
local report_separator="\n$separator\n%s\n$separator\n"
printf "\n$report_separator" "Active Personnel Records"
for idx in "${!person_index[@]}"; do
# Skip if person is marked as deleted
! is_person_active "$idx" && continue
printf "$report_separator" "Person $((idx+1))"
# Create new nameref for each iteration to ensure proper binding
local -n person="person_${idx}"
# Display attributes with proper labels
for attr in "${person_attributes[@]}"; do
local display_name="${person_attr_display[$attr]:-$attr}"
local value
value="${person[$attr]}"
printf "$fmt" "$display_name" "$value"
done
done
printf "$report_separator\n" "End of Report"
}
# Function to handle data entry for a new person
# Args: $1 - File denoscriptor to read input from
# Demonstrates:
# - File denoscriptor manipulation for input
# - Dynamic array creation and population
# - Proper error checking and validation
enter_data()
Robert
robert.johnson@example.com
y
Williams
Mary
mary.williams@example.com
y
Brown
James
james.brown@example.com
n
DATA
)
# Function to generate unique employee IDs
# Combines the record index with a random number to ensure uniqueness
# Args: $1 - The record index (1-based)
generate_employee_number() {
printf "%d%06d" "$(( $1 + 1 ))" "$((RANDOM % 1000000))"
}
# Function to get the current number of records
# Used for both array sizing and new record creation
get_index() {
local current_idx
current_idx=${#person_index[@]}
echo "$current_idx"
}
# Function to create a new person record
# Args: $1 - The index for the new record
# Creates a new associative array and marks it as active
create_person() {
local current_idx=$1
declare -gA "person_${current_idx}"
person_index+=("E")
}
# Function to convert from 1-based (user) index to 0-based (internal) index
# Args: $1 - User-facing index (1-based)
# Returns: Internal array index (0-based) or -1 if invalid
to_internal_index() {
local user_idx=$1
if [[ "$user_idx" =~ ^[1-9][0-9]*$ ]] && ((user_idx <= $(get_index))); then
echo "$((user_idx - 1))"
else
echo "-1"
fi
}
# Function to mark a record as deleted
# Implements soft-delete by setting status flag to 'D'
# Args: $1 - User-facing index (1-based)
delete_person() {
local user_idx=$1
local internal_idx
internal_idx=$(to_internal_index "$user_idx")
if [[ $internal_idx -ge 0 ]]; then
person_index[$internal_idx]="D"
return 0
else
echo "Error: Invalid person number $user_idx" >&2
return 1
fi
}
# Function to check if a record exists and is active
# Args: $1 - Internal index (0-based)
# Returns: true if record exists and is active, false otherwise
is_person_active() {
local idx=$1
[[ $idx -lt $(get_index) && "${person_index[$idx]}" == "E" ]]
}
# Function to update a person's attribute
# Uses nameref to directly modify the associative array
# Args: $1 - Array name to update
# $2 - Attribute name
# $3 - New value
update_person_attribute() {
local -n person_array_name=$1
local attr=$2
local value=$3
person_array_name[$attr]="$value"
}
# Function to display all active person records
# Demonstrates:
# - Proper nameref handling in loops
# - Format string usage for consistent output
# - Conditional record filtering (skipping deleted)
display_people() {
local fmt=" %-12s: %s\n"
local separator="------------------------"
local report_separator="\n$separator\n%s\n$separator\n"
printf "\n$report_separator" "Active Personnel Records"
for idx in "${!person_index[@]}"; do
# Skip if person is marked as deleted
! is_person_active "$idx" && continue
printf "$report_separator" "Person $((idx+1))"
# Create new nameref for each iteration to ensure proper binding
local -n person="person_${idx}"
# Display attributes with proper labels
for attr in "${person_attributes[@]}"; do
local display_name="${person_attr_display[$attr]:-$attr}"
local value
value="${person[$attr]}"
printf "$fmt" "$display_name" "$value"
done
done
printf "$report_separator\n" "End of Report"
}
# Function to handle data entry for a new person
# Args: $1 - File denoscriptor to read input from
# Demonstrates:
# - File denoscriptor manipulation for input
# - Dynamic array creation and population
# - Proper error checking and validation
enter_data()
{
local fd=$1
local current_index
while true; do
current_index=$(get_index)
create_person "$current_index"
# Create a reference to the current person's associative array
declare -n current_person="person_${current_index}"
# Set employee ID
current_person[employee_id]=$(generate_employee_number "$((current_index + 1))")
# Read other attributes
for attr in "${person_attributes[@]}"; do
local display_name="${person_attr_display[$attr]:-$attr}"
case "$attr" in
"employee_id") continue ;;
esac
read -u "$fd" -p "Enter $display_name: " value
if [[ $? -eq 0 ]]; then
update_person_attribute "person_${current_index}" "$attr" "$value"
fi
done
if read -u "$fd" -p "Add another person? (y/n): " continue; then
[[ $continue != "y" ]] && break
else
break
fi
done
}
# Function to run in test mode with predefined data
test_mode() {
echo "Running in test mode with dummy data..."
# Create temporary file denoscriptor (3) for test data
exec 3< <(echo "$TEST_DATA")
enter_data 3
exec 3<&- # Close the temporary file denoscriptor
}
# Function to run in interactive mode with user input
interactive_mode() {
echo "Running in interactive mode..."
enter_data 0 # Use standard input (fd 0)
}
# Main noscript logic
case "$1" in
"-t")
test_mode
;;
"-i")
interactive_mode
;;
*)
echo "Usage: $0 [-t|-i]"
echo " -t Run with test data"
echo " -i Run with terminal input"
exit 1
;;
esac
# Display all active records
display_people
# Demonstrate "deleting" records by changing their status
echo "Deleting records employee number 2 and number 4"
delete_person 2 # Mark second person as deleted
delete_person 4 # Mark fourth person as deleted
# Display again - deleted records won't show
display_people
echo
echo "Show the actual variable definitions, including the dynamic arrays"
declare -p | grep person
Here's the output:
(python-3.10-PA-dev) [unixwzrd@xanax: test]$ ./test -t
Running in test mode with dummy data...
------------------------
Active Personnel Records
------------------------
------------------------
Person 1
------------------------
Employee ID : 2027296
Last Name : Doe
First Name : John
Email : john.doe@example.com
------------------------
Person 2
------------------------
Employee ID : 3028170
Last Name : Smith
First Name : Jane
Email : jane.smith@example.com
------------------------
Person 3
------------------------
Employee ID : 4014919
Last Name : Johnson
First Name : Robert
Email : robert.johnson@example.com
------------------------
Person 4
------------------------
Employee ID : 5024071
Last Name : Williams
First Name : Mary
Email : mary.williams@example.com
------------------------
Person 5
------------------------
Employee ID : 6026645
Last Name : Brown
First Name : James
Email : james.brown@example.com
------------------------
End of Report
------------------------
Deleting records employee number 2 and number 4
------------------------
Active Personnel Records
------------------------
------------------------
Person 1
------------------------
Employee ID : 2027296
Last Name : Doe
local fd=$1
local current_index
while true; do
current_index=$(get_index)
create_person "$current_index"
# Create a reference to the current person's associative array
declare -n current_person="person_${current_index}"
# Set employee ID
current_person[employee_id]=$(generate_employee_number "$((current_index + 1))")
# Read other attributes
for attr in "${person_attributes[@]}"; do
local display_name="${person_attr_display[$attr]:-$attr}"
case "$attr" in
"employee_id") continue ;;
esac
read -u "$fd" -p "Enter $display_name: " value
if [[ $? -eq 0 ]]; then
update_person_attribute "person_${current_index}" "$attr" "$value"
fi
done
if read -u "$fd" -p "Add another person? (y/n): " continue; then
[[ $continue != "y" ]] && break
else
break
fi
done
}
# Function to run in test mode with predefined data
test_mode() {
echo "Running in test mode with dummy data..."
# Create temporary file denoscriptor (3) for test data
exec 3< <(echo "$TEST_DATA")
enter_data 3
exec 3<&- # Close the temporary file denoscriptor
}
# Function to run in interactive mode with user input
interactive_mode() {
echo "Running in interactive mode..."
enter_data 0 # Use standard input (fd 0)
}
# Main noscript logic
case "$1" in
"-t")
test_mode
;;
"-i")
interactive_mode
;;
*)
echo "Usage: $0 [-t|-i]"
echo " -t Run with test data"
echo " -i Run with terminal input"
exit 1
;;
esac
# Display all active records
display_people
# Demonstrate "deleting" records by changing their status
echo "Deleting records employee number 2 and number 4"
delete_person 2 # Mark second person as deleted
delete_person 4 # Mark fourth person as deleted
# Display again - deleted records won't show
display_people
echo
echo "Show the actual variable definitions, including the dynamic arrays"
declare -p | grep person
Here's the output:
(python-3.10-PA-dev) [unixwzrd@xanax: test]$ ./test -t
Running in test mode with dummy data...
------------------------
Active Personnel Records
------------------------
------------------------
Person 1
------------------------
Employee ID : 2027296
Last Name : Doe
First Name : John
Email : john.doe@example.com
------------------------
Person 2
------------------------
Employee ID : 3028170
Last Name : Smith
First Name : Jane
Email : jane.smith@example.com
------------------------
Person 3
------------------------
Employee ID : 4014919
Last Name : Johnson
First Name : Robert
Email : robert.johnson@example.com
------------------------
Person 4
------------------------
Employee ID : 5024071
Last Name : Williams
First Name : Mary
Email : mary.williams@example.com
------------------------
Person 5
------------------------
Employee ID : 6026645
Last Name : Brown
First Name : James
Email : james.brown@example.com
------------------------
End of Report
------------------------
Deleting records employee number 2 and number 4
------------------------
Active Personnel Records
------------------------
------------------------
Person 1
------------------------
Employee ID : 2027296
Last Name : Doe
First Name : John
Email : john.doe@example.com
------------------------
Person 3
------------------------
Employee ID : 4014919
Last Name : Johnson
First Name : Robert
Email : robert.johnson@example.com
------------------------
Person 5
------------------------
Employee ID : 6026645
Last Name : Brown
First Name : James
Email : james.brown@example.com
------------------------
End of Report
------------------------
Show the actual variable definitions, including the dynamic arrays
declare -A person_0=([FirstName]="John" [email]="john.doe@example.com [LastName]="Doe" [employee_id]="2027296" )
declare -A person_1=([FirstName]="Jane" [email]="jane.smith@example.com" [LastName]="Smith" [employee_id]="3028170" )
declare -A person_2=([FirstName]="Robert" [email]="robert.johnson@example.com" [LastName]="Johnson" [employee_id]="4014919" )
declare -A person_3=([FirstName]="Mary" [email]="mary.williams@example.com" [LastName]="Williams" [employee_id]="5024071" )
declare -A person_4=([FirstName]="James" [email]="james.brown@example.com" [LastName]="Brown" [employee_id]="6026645" )
declare -A person_attr_display=([FirstName]="First Name" [email]="Email" [LastName]="Last Name" [employee_id]="Employee ID" )
declare -a person_attributes=([0]="employee_id" [1]="LastName" [2]="FirstName" [3]="email")
declare -a person_index=([0]="E" [1]="D" [2]="E" [3]="D" [4]="E")
https://redd.it/1khh7xb
@r_bash
Email : john.doe@example.com
------------------------
Person 3
------------------------
Employee ID : 4014919
Last Name : Johnson
First Name : Robert
Email : robert.johnson@example.com
------------------------
Person 5
------------------------
Employee ID : 6026645
Last Name : Brown
First Name : James
Email : james.brown@example.com
------------------------
End of Report
------------------------
Show the actual variable definitions, including the dynamic arrays
declare -A person_0=([FirstName]="John" [email]="john.doe@example.com [LastName]="Doe" [employee_id]="2027296" )
declare -A person_1=([FirstName]="Jane" [email]="jane.smith@example.com" [LastName]="Smith" [employee_id]="3028170" )
declare -A person_2=([FirstName]="Robert" [email]="robert.johnson@example.com" [LastName]="Johnson" [employee_id]="4014919" )
declare -A person_3=([FirstName]="Mary" [email]="mary.williams@example.com" [LastName]="Williams" [employee_id]="5024071" )
declare -A person_4=([FirstName]="James" [email]="james.brown@example.com" [LastName]="Brown" [employee_id]="6026645" )
declare -A person_attr_display=([FirstName]="First Name" [email]="Email" [LastName]="Last Name" [employee_id]="Employee ID" )
declare -a person_attributes=([0]="employee_id" [1]="LastName" [2]="FirstName" [3]="email")
declare -a person_index=([0]="E" [1]="D" [2]="E" [3]="D" [4]="E")
https://redd.it/1khh7xb
@r_bash
Reddit
From the bash community on Reddit
Explore this post and more from the bash community
This media is not supported in your browser
VIEW IN TELEGRAM
I built this simple tool to hide folders on Linux using a password-protected CLI + TUI completely written in bash.
https://redd.it/1khrb3q
@r_bash
https://redd.it/1khrb3q
@r_bash
rsync: how do I get a list without dirs/ with no change into them?
Hi, I am understanding how to drive within Fat32: using -anchuv
(The permits are changed yes or yes in Fat32 and this does not happen in ext4, but anyway those differences do not generate a copy if I made a copy in reverse from destination to origin).
Now I see a list that scares, full of directories but without files under them.
This means that nothing of them will be copied. Then I would like those directories without changes ... Let's say that only files are displayed.
I made a screenshot in which you see that list of directories without files to copy ... https://imgbox.com/y4VTZtTX
I would like that list of meaningless directories.
Thank you and regards!
https://redd.it/1khu7zo
@r_bash
Hi, I am understanding how to drive within Fat32: using -anchuv
(The permits are changed yes or yes in Fat32 and this does not happen in ext4, but anyway those differences do not generate a copy if I made a copy in reverse from destination to origin).
Now I see a list that scares, full of directories but without files under them.
This means that nothing of them will be copied. Then I would like those directories without changes ... Let's say that only files are displayed.
I made a screenshot in which you see that list of directories without files to copy ... https://imgbox.com/y4VTZtTX
I would like that list of meaningless directories.
Thank you and regards!
https://redd.it/1khu7zo
@r_bash
Imgbox
imgbox - fast, simple image host
Use imgbox to upload, host and share all your images. It's simple, free and blazing fast!
I found why rsync copy with time diff in FAT32 vs. copy without diff in ext4
hi I'd like to tell you what happend when I use FAT 32 copy with rsync vs. the same in ext4:
the clue is ls -l --full-time : there is a little diff in time of 1/2 second in sourc-dest. in FAT32 when I use rsync -anhuv.
When I use dirdiff there is green and red leaves... and diff shows nothing: the file is the same sourc. dest. but time is diff.: 1/2 sec. of time.
in ext4 rsync copy preserve full-time (and perm. too)
so in FAT32 I should use rsync -anchuv -c is mandatory!!!
in FAT32 -p and -t don't work! so -c is mandatory.
in ext4 -c isn't necesary: -p -t work fine.
that's all
Regards!
https://redd.it/1ki87j3
@r_bash
hi I'd like to tell you what happend when I use FAT 32 copy with rsync vs. the same in ext4:
the clue is ls -l --full-time : there is a little diff in time of 1/2 second in sourc-dest. in FAT32 when I use rsync -anhuv.
When I use dirdiff there is green and red leaves... and diff shows nothing: the file is the same sourc. dest. but time is diff.: 1/2 sec. of time.
in ext4 rsync copy preserve full-time (and perm. too)
so in FAT32 I should use rsync -anchuv -c is mandatory!!!
in FAT32 -p and -t don't work! so -c is mandatory.
in ext4 -c isn't necesary: -p -t work fine.
that's all
Regards!
https://redd.it/1ki87j3
@r_bash
Reddit
From the bash community on Reddit
Explore this post and more from the bash community
Javanoscript in BASH
I recently had a client that created a whole interpretative language add-on in a DOM object to allow Product Managers to influence backend code, so as not to require developers to write integrations. This seemed like so much of a fun idea, that I felt it required to create a minimalistic JavaScript interpreter to prove that, once again, BASH can pretty much do everything a "normal" development language can.
Yes. I know. I don't care. I had fun :)
https://github.com/elemantalcode/jash for the win!
https://redd.it/1kipfdy
@r_bash
I recently had a client that created a whole interpretative language add-on in a DOM object to allow Product Managers to influence backend code, so as not to require developers to write integrations. This seemed like so much of a fun idea, that I felt it required to create a minimalistic JavaScript interpreter to prove that, once again, BASH can pretty much do everything a "normal" development language can.
Yes. I know. I don't care. I had fun :)
https://github.com/elemantalcode/jash for the win!
https://redd.it/1kipfdy
@r_bash
GitHub
GitHub - elemantalcode/jash: Javanoscript Interpreter in BASH
Javanoscript Interpreter in BASH. Contribute to elemantalcode/jash development by creating an account on GitHub.
Multiple sourcing issue
Hi, I have a problem in my project, the problem is in my library,
i have directory called lib, containing multiple .sh files
├── application
│ │
│ └── main.sh
│
├── lib
│ ├── arrays.sh
│ ├── files.sh
│ ├── math.sh
│ └── out.sh
in `out. sh` file I have some function for error handling, also contains some `readonly` variables , and other files in the library use the error handling function in `out. sh`
example:
`application/main.sh:`
source "../lib/out.sh"
source "../lib/array.sh"
do_something || error_handle .......
`lib/arrays.sh:`
source "out.sh"
func1(){
# some code
}
`lib/out.sh:`
readonly __STDOUT=1
readonly __STDERR=2
readonly __ERROR_MESSAGE_ENABLE=0
error_handle(){
# some code
}
now the problem is when I run the project, it tells me that the `$__STDOUT` and the other vars are `readonly` and cannot be modified, because the variables are declared twice, first by my app when it source the `out. sh` , second when `arrays. sh` source `out. sh` , so my question, how to solve it in the best way, I already think about make variables not read only, but I feel it is not the best way, because we still have multiple sourcing, and I want to keep all library functions to have access to error handling functions
https://redd.it/1kj9wya
@r_bash
Hi, I have a problem in my project, the problem is in my library,
i have directory called lib, containing multiple .sh files
├── application
│ │
│ └── main.sh
│
├── lib
│ ├── arrays.sh
│ ├── files.sh
│ ├── math.sh
│ └── out.sh
in `out. sh` file I have some function for error handling, also contains some `readonly` variables , and other files in the library use the error handling function in `out. sh`
example:
`application/main.sh:`
source "../lib/out.sh"
source "../lib/array.sh"
do_something || error_handle .......
`lib/arrays.sh:`
source "out.sh"
func1(){
# some code
}
`lib/out.sh:`
readonly __STDOUT=1
readonly __STDERR=2
readonly __ERROR_MESSAGE_ENABLE=0
error_handle(){
# some code
}
now the problem is when I run the project, it tells me that the `$__STDOUT` and the other vars are `readonly` and cannot be modified, because the variables are declared twice, first by my app when it source the `out. sh` , second when `arrays. sh` source `out. sh` , so my question, how to solve it in the best way, I already think about make variables not read only, but I feel it is not the best way, because we still have multiple sourcing, and I want to keep all library functions to have access to error handling functions
https://redd.it/1kj9wya
@r_bash
Reddit
From the bash community on Reddit
Explore this post and more from the bash community
How to better structure this gitfzf noscript
This is my new noscript for getting fzf list of all git repos in my home directory and applying git commands with respect to the the repo selected.
I want a better structure of this noscript minimizing the loc and adding more functionality.
#!/usr/bin/env bash
# use fzf for selecting the repos from the desktop
# and then select one, apply a command such as status, sync with remote, etc.
localrepo(){ # searches the .git dir in $1
if -z $REPOS ; then
export REPOS="$(dirname $(find $1 -name ".git" -type d))"
fi
echo $REPOS | tr ' ' '\n'
}
reposelection(){ # fzf selection from allrepo
local selected
local repo
repo=\localrepo $1``
selected=$( printf "$repo" | fzf +m --height 50% --style full --input-label ' Destination ' --preview 'tree -C {}')
echo $selected
}
gitfnc(){
gitcmda=( "branch_commit"="branch -vva" )
gitcmd=("status" "status -uno" "log" "branch -vva" "commit" "add" "config") # simple git commands
selected=$( printf '%s\n' "${gitcmd@}" | fzf +m --height 50% --style full --input-label ' Destination ' --preview 'tree -C {}')
echo $selected
}
repo=\reposelection $HOME``
cmd="cmd"
while [ ! -z "$cmd" ]; do
cmd=\gitfnc
if [ -z "$repo" || -z "$cmd" ]; then
exit 0
fi
printf "\n git -C $repo $cmd"
git -C $repo $cmd
done
https://redd.it/1kjei14
@r_bash
This is my new noscript for getting fzf list of all git repos in my home directory and applying git commands with respect to the the repo selected.
I want a better structure of this noscript minimizing the loc and adding more functionality.
#!/usr/bin/env bash
# use fzf for selecting the repos from the desktop
# and then select one, apply a command such as status, sync with remote, etc.
localrepo(){ # searches the .git dir in $1
if -z $REPOS ; then
export REPOS="$(dirname $(find $1 -name ".git" -type d))"
fi
echo $REPOS | tr ' ' '\n'
}
reposelection(){ # fzf selection from allrepo
local selected
local repo
repo=\localrepo $1``
selected=$( printf "$repo" | fzf +m --height 50% --style full --input-label ' Destination ' --preview 'tree -C {}')
echo $selected
}
gitfnc(){
gitcmda=( "branch_commit"="branch -vva" )
gitcmd=("status" "status -uno" "log" "branch -vva" "commit" "add" "config") # simple git commands
selected=$( printf '%s\n' "${gitcmd@}" | fzf +m --height 50% --style full --input-label ' Destination ' --preview 'tree -C {}')
echo $selected
}
repo=\reposelection $HOME``
cmd="cmd"
while [ ! -z "$cmd" ]; do
cmd=\gitfnc
if [ -z "$repo" || -z "$cmd" ]; then
exit 0
fi
printf "\n git -C $repo $cmd"
git -C $repo $cmd
done
https://redd.it/1kjei14
@r_bash
Reddit
From the bash community on Reddit
Explore this post and more from the bash community
pickleBerry a TUI based file manager all written as a shell noscript
Running in terminal - kitty
home directory
Moving through directories
help menu
https://redd.it/1kjx88r
@r_bash
Running in terminal - kitty
home directory
Moving through directories
help menu
https://redd.it/1kjx88r
@r_bash