#!/bin/bash # fileMenu - Generic file menu based on dates, using dialog # (C) 2024-2025 Felix Hauri - felix@f-hauri.ch # Version: 0.0.3 -- Last update: Fri May 2 13:47:25 2025 # Sort list of *backup* paths (files or dirs), render date # based on `locale date_fmt`, then run dialog menu selector, # defaulting to most recent path. # Intented to be used in a script, this function populate a variable # instead of produce any output. # Support format dates like: /path/to/name?YYYY?MM?DD[?HH[?MM]][.suffix] # or /path/to/name?YYYYMMDD[HH[MM]][.suffix] # # Note this don't use bash's RE, only two forks to `locale` and `date` # and use limited number of loop (max 4) not regarding length of list. # # Syntaxe: # fileMenu <entry> [entry] [entry]... # Sample: # if filemenu myVar 'Choose backup' /path/to/backups_*; then ...; fi # DIALOG=dialog fileMenu myVar 'DB to restore` /path/to/file-????????.sql # DIALOG=gdialog fileMenu myVar 'Count files in' /path/to/backups_????-??-?? # DIALOG=dialog _fM_date_fmt='%a %d %b %y, %Hh.' fileMenu myVar 'Choose... fileMenu() { local -n _fM_result=$1 local _fM_title=$2 _fM_tmpStr _fM_date_fmt _fM_argDialog _fM_sel _fM_dates shift 2 # $_fM_date_fmt is date format for menu, may be defined before running func : ${_fM_date_fmt:=$(locale date_fmt||echo '%c')} # _fM_files is arguments, _fM_sep separators for date: 'YYYYY/MM/DD HH:MM' local -a _fM_files=("$@") _fM_sep=('/' '/' ' ' ':') _fM_dates=("${_fM_files[@]##*[a-z][^a-zA-Z0-9]}") # Drop begin: /path/bknam_ _fM_dates=(${_fM_dates[@]%.*}) # Drop any suffix case ${_fM_dates[0]} in # case format in 'YYYYMMDD[HH[MM]]': [12][0-9][0-9][0-9][01][0-9]* ) # Add separators _fM_sel=3 for _fM_tmpStr in \ [12][0-9][0-9][0-9]{[01][0-9]{[0-3][0-9]{[0-2][0-9],},},}; do _fM_dates=("${_fM_dates[@]/$_fM_tmpStr/&${_fM_sep[_fM_sel--]}}") done ;; * ) # case format in, for sample: ' YYYY-MM-DD_HHhMM' for _fM_tmpStr in "${_fM_sep[@]}"; do # Replace separators _fM_dates=("${_fM_dates[@]/[^0-9\/: ]/$_fM_tmpStr}") done ;; esac # Convert normalized dates "YYYY/MM/DD HH:MM" to UNIXEPOCH mapfile -t _fM_dates < <( date -f - +'%s' < <( printf '%s\n' "${_fM_dates[@]}" )) # Prepare dialog command's arguments as an array containing both: # id (in files's array) and rendered date: (0 'mon 1 apr' 1 'thu 2 apr' ..) printf -v _fM_tmpStr "%d\n%%40(${_fM_date_fmt//%/%%})T\\n" ${!_fM_files[@]} mapfile -t _fM_argDialog < <(printf "$_fM_tmpStr" "${_fM_dates[@]}") # Run $DIALOG comnmad and store result (id in files's array) _fM_sel=$( ${DIALOG:-whiptail} --default-item $(( ${#_fM_files[@]} - 1 )) \ --menu "$_fM_title" $((${LINES:-24}-3)) $((${COLUMNS:-80}-3)) \ $((${LINES:-24}-11)) "${_fM_argDialog[@]}" 2>&1 > /dev/tty ) if [[ -n $_fM_sel ]]; then _fM_result=${_fM_files[$_fM_sel]} else _fM_result= ; return 1 fi }