How to split a variable in two args with exec in TCL? - bash

I want to create a simple Console in Tcl/Tk
I have two problems. First changing every * with a [glob *] but also, when my entry contains "ls -a" it doesn't understand that ls is the command and -a the first arg.
How can I manage to do that ?
Thanks
proc execute {} {
# ajoute le contenu de .add_frame.add_entry
set value [.add_frame.add_entry get]
if {[string compare "$value" ""] == 1} {
.text insert end "\n\n% $value\n"
.text insert end [exec $value]
.add_frame.add_entry delete 0 end
}
}
frame .add_frame
label .add_frame.add_label -text "Nouvel élément : "
entry .add_frame.add_entry
button .add_frame.add_button -text "Executer" -command execute
button .add_frame.exit_button -text "Quitter" -command exit
bind .add_frame.add_entry <Return> execute
bind .add_frame.add_entry <KP_Enter> execute
bind . <Escape> exit
bind . <Control-q> exit
pack .add_frame.add_label -side left
pack .add_frame.exit_button -side right
pack .add_frame.add_button -side right
pack .add_frame.add_entry -fill x -expand true
pack .add_frame -side top -fill x
text .text
.text insert end "% Tcl/Tk Console"
pack .text -side bottom -fill both -expand true

The simple answer in Tcl 8.5 is to use this:
exec {*}$value
In 8.4 and before, that syntax didn't exist. That meant that many people wrote this:
eval exec $value
But in reality, the safe version was one of these:
eval exec [lrange $value 0 end]
eval [linsert $value 0 exec]
Of course, if the $value is coming straight from the user then you're better off using a system shell to evaluate it since more users expect that sort of syntax:
exec /usr/bin/bash -c $value

Related

Invalid Command and files not populating using treeview in TCL

In my endeavour to create a simple treeview GUI I have come across tree.tcl which is available in the active tcl 8.6 installation.
I've managed to adapt the code to fit my purpose (hardy changed anything) and when I run the code in the same way as the demos for Active TCL 8.6 (via widget) are run the code is running as expected (though I've not tried making any selection of node within tree).
However that is not the case once I copy the code over into my gui application.
The structure is as expected but when I try to expand the nodes I get:
I get invalid command error
ERROR: invalid command name "populateTree"
command bound to event:
"populateTree .fc.tv.tree [.fc.tv.tree focus]"
Now for some reason none of the files within the folders are read i.e. all file types are recognised as Directories hence everything under nodes shown as "dummy"
I'd also like to add filter to only read a specific file type, i.e. *.txt, if I do so then even folders are not read.
i.e foreach f [lsort -dictionary [glob -nocomplain -dir $path *]] to
foreach f [lsort -dictionary [glob -nocomplain -dir $path *.txt]]
I'd be obliged if someone could help.
# temp dir to mimic Network dir
set ::dir "C:/Dev"
proc populateRoots {tree} {
populateTree $tree [$tree insert {} end -text "Network File" \
-values [list $::dir directory]]
}
## Code to populate a node of the tree
proc populateTree {tree node} {
if {[$tree set $node type] ne "directory"} {
return
}
set path [$tree set $node fullpath]
$tree delete [$tree children $node]
foreach f [lsort -dictionary [glob -nocomplain -dir $path *]] {
set type [file type $f]
set id [$tree insert $node end -text [file tail $f] \
-values [list $f $type]]
if {$type eq "directory"} {
## Make it so that this node is openable
$tree insert $id 0 -text dummy ;# a dummy
$tree item $id -text [file tail $f]/
} elseif {$type eq "file"} {
set size [file size $f]
set ttime [file mtime $f]
## Format the file size nicely
if {$size >= 1024*1024*1024} {
set size [format %.1f\ GB [expr {$size/1024/1024/1024.}]]
} elseif {$size >= 1024*1024} {
set size [format %.1f\ MB [expr {$size/1024/1024.}]]
} elseif {$size >= 1024} {
set size [format %.1f\ kB [expr {$size/1024.}]]
} else {
append size " bytes"
}
$tree set $id size $size
}
}
# Stop this code from rerunning on the current node
$tree set $node type processedDirectory
}
# ## Create the tree and set it up
ttk::treeview $tw.tree -columns {fullpath type size date time} -displaycolumns {size date time} \
-yscroll "$tw.vsb set" -xscroll "$tw.hsb set"
ttk::scrollbar $tw.vsb -orient vertical -command "$tw.tree yview"
ttk::scrollbar $tw.hsb -orient horizontal -command "$tw.tree xview"
$tw.tree heading \#0 -text "Directory Structure"
$tw.tree heading size -text "File Size"
$tw.tree column size -stretch 0 -width 70
populateRoots $tw.tree
bind $tw.tree <<TreeviewOpen>> {populateTree %W [%W focus]}
# ## Arrange the tree and its scrollbars in the toplevel
lower [ttk::frame $tw.dummy]
pack $tw.dummy -fill both -expand 1
grid $tw.tree $tw.vsb -sticky nsew -in $tw.dummy
grid $tw.hsb -sticky nsew -in $tw.dummy
grid columnconfigure $tw.dummy 0 -weight 1
grid rowconfigure $tw.dummy 0 -weight 1
Thanks in advance,
George
The issue was that the procedure populateTree needed to be ::populateTree so it could be found within the namespace.
PS: I still can't print selection to console...

Bash error code doesn't show in my shell prompt [duplicate]

I've been trying to customize my Bash prompt so that it will look like
[feralin#localhost ~]$ _
with colors. I managed to get constant colors (the same colors every time I see the prompt), but I want the username ('feralin') to appear red, instead of green, if the last command had a nonzero exit status. I came up with:
\e[1;33m[$(if [[ $? == 0 ]]; then echo "\e[0;31m"; else echo "\e[0;32m"; fi)\u\e[m#\e[1;34m\h \e[0;35m\W\e[1;33m]$ \e[m
However, from my observations, the $(if ...; fi) seems to be evaluated once, when the .bashrc is run, and the result is substituted forever after. This makes the name always green, even if the last exit code is nonzero (as in, echo $?). Is this what is happening? Or is it simply something else wrong with my prompt? Long question short, how do I get my prompt to use the last exit code?
As you are starting to border on a complex PS1, you might consider using PROMPT_COMMAND. With this, you set it to a function, and it will be run after each command to generate the prompt.
You could try the following in your ~/.bashrc file:
PROMPT_COMMAND=__prompt_command # Function to generate PS1 after CMDs
__prompt_command() {
local EXIT="$?" # This needs to be first
PS1=""
local RCol='\[\e[0m\]'
local Red='\[\e[0;31m\]'
local Gre='\[\e[0;32m\]'
local BYel='\[\e[1;33m\]'
local BBlu='\[\e[1;34m\]'
local Pur='\[\e[0;35m\]'
if [ $EXIT != 0 ]; then
PS1+="${Red}\u${RCol}" # Add red if exit code non 0
else
PS1+="${Gre}\u${RCol}"
fi
PS1+="${RCol}#${BBlu}\h ${Pur}\W${BYel}$ ${RCol}"
}
This should do what it sounds like you want. Take a look a my bashrc's sub file if you want to see all the things I do with my __prompt_command function.
If you don't want to use the prompt command there are two things you need to take into account:
getting the value of $? before anything else. Otherwise it'll be overridden.
escaping all the $'s in the PS1 (so it's not evaluated when you assign it)
Working example using a variable
PS1="\$(VALU="\$?" ; echo \$VALU ; date ; if [ \$VALU == 0 ]; then echo zero; else echo nonzero; fi) "
Working example without a variable
Here the if needs to be the first thing, before any command that would override the $?.
PS1="\$(if [ \$? == 0 ]; then echo zero; else echo nonzero; fi) "
Notice how the \$() is escaped so it's not executed right away, but each time PS1 is used. Also all the uses of \$?.
Compact solution:
PS1='... $(code=${?##0};echo ${code:+[error: ${code}]})'
This approach does not require PROMPT_COMMAND (apparently this can be slower sometimes) and prints [error: <code>] if the exit code is non-zero, and nothing if it's zero:
... > false
... [error: 1]> true
... >
Change the [error: ${code}] part depending on your liking, with ${code} being the non-zero code to print.
Note the use of ' to ensure the inline $() shell gets executed when PS1 is evaluated later, not when the shell is started.
As bonus, you can make it colorful in red by adding \e[01;31m in front and \e[00m after to reset:
PS1='... \e[01;31m$(code=${?##0};echo ${code:+[error: ${code}]})\e[00m'
--
How it works:
it uses bash parameter substitution
first, the ${?##0} will read the exit code $? of the previous command
the ## will remove any 0 pattern from the beginning, effectively making a 0 result an empty var (thanks #blaskovicz for the trick)
we assign this to a temporary code variable as we need to do another substitution, and they can't be nested
the ${code:+REPLACEMENT} will print the REPLACEMENT part only if the variable code is set (non-empty)
this way we can add some text and brackets around it, and reference the variable again inline: [error: ${code}]
I wanted to keep default Debian colors, print the exact code, and only print it on failure:
# Show exit status on failure.
PROMPT_COMMAND=__prompt_command
__prompt_command() {
local curr_exit="$?"
local BRed='\[\e[0;91m\]'
local RCol='\[\e[0m\]'
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u#\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
if [ "$curr_exit" != 0 ]; then
PS1="[${BRed}$curr_exit${RCol}]$PS1"
fi
}
The following provides a leading green check mark when the exit code is zero and a red cross in all other cases. The remainder is a standard colorized prompt. The printf statements can be modified to present the two states that were originally requested.
PS1='$(if [ $? -eq 0 ]; then printf "\033[01;32m""\xE2\x9C\x93"; else printf "\033[01;31m""\xE2\x9C\x95"; fi) \[\e[00;32m\]\u#\h\[\e[00;30m\]:\[\e[01;33m\]\w\[\e[01;37m\]\$ '
Why didn't I think about that myself? I found this very interesting and added this feature to my 'info-bar' project. Eyes will turn red if the last command failed.
#!/bin/bash
eyes=(O o ∘ ◦ ⍤ ⍥) en=${#eyes[#]} mouth='_'
face () { # gen random face
[[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW
if [[ $1 ]]; then printf "${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}"
else printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"
fi
}
info () { error=$?
[[ -d .git ]] && { # If in git project folder add git status to info bar output
git_clr=('GIT' $(git -c color.ui=always status -sb)) # Colored output 4 info
git_tst=('GIT' $(git status -sb)) # Simple output 4 test
}
printf -v line "%${COLUMNS}s" # Set border length
date=$(printf "%(%a %d %b %T)T") # Date & time 4 test
test=" O_o $PWD ${git_tst[*]} $date o_O " # Test string
step=$[$COLUMNS-${#test}]; [[ $step -lt 0 ]] && step=0 # Count spaces
line="$GRN${line// /-}$DEF\n" # Create lines
home="$BLD$BLU$PWD$DEF" # Home dir info
date="$DIM$date$DEF" # Colored date & time
#------+-----+-------+--------+-------------+-----+-------+--------+
# Line | O_o |homedir| Spaces | Git status | Date| o_O | Line |
#------+-----+-------+--------+-------------+-----+-------+--------+
printf "$line $(face) $home %${step}s ${git_clr[*]} $date $(face) \n$line" # Final info string
}
PS1='${debian_chroot:+($debian_chroot)}\n$(info)\n$ '
case "$TERM" in xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)} $(face 1) \w\a\]$PS1";;
esac
Improved demure answer:
I think this is important because the exit status is not always 0 or 1.
if [ $EXIT != 0 ]; then
PS1+="${Red}${EXIT}:\u${RCol}" # Add red if exit code != 0
else
PS1+="${Gre}${EXIT}:\u${RCol}" # Also displays exit status
fi
To preserve the original prompt format (not just colors),
you could append following to the end of file ~/.bashrc:
PS1_ORIG=$PS1 # original primary prompt value
PROMPT_COMMAND=__update_prompt # Function to be re-evaluated after each command is executed
__update_prompt() {
local PREVIOUS_EXIT_CODE="$?"
if [ $PREVIOUS_EXIT_CODE != 0 ]; then
local RedCol='\[\e[0;31m\]'
local ResetCol='\[\e[0m\]'
local replacement="${RedCol}\u${ResetCol}"
# Replace username color
PS1=${PS1_ORIG//]\\u/]$replacement}
## Alternative: keep same colors, append exit code
#PS1="$PS1_ORIG[${RedCol}error=$PREVIOUS_EXIT_CODE${ResetCol}]$ "
else
PS1=$PS1_ORIG
fi
}
See also the comment about the alternative approach that preserves username color and just appends an error code in red to the end of the original prompt format.
You can achieve a similar result to include a colored (non-zero) exit code in a prompt, without using subshells in the prompt nor prompt_command.
You color the exit code portion of the prompt, while having it only appear when non-zero.
Core 2$ section of the prompt: \\[\\033[0;31;4m\\]\${?#0}\\[\\033[0;33m\\]\$ \\[\\033[0m\\]
Key elements:
return code, if not 0: \${?#0} (specificly "removes prefix of 0")
change color without adding to calculated prompt-width: \\[\\033[0;31m\\]
\\[ - begin block
\\033 - treat as 0-width, in readline calculations for cmdline editing
[0;31;4m - escape code, change color, red fg, underline
\\] - end block
Components:
\\[\\033[0;31;4m\\] - set color 0;31m fg red, underline
\${?#0} - display non-zero status (by removing 0 prefix)
\\[\\033[0;33m\\] - set color 0;33m fg yellow
\$ - $ or # on EUID
\\[\\033[0m\\] - reset color
The full PS1 I use (on one host):
declare -x PS1="\\[\\033[0;35m\\]\\h\\[\\033[1;37m\\] \\[\\033[0;37m\\]\\w \\[\\033[0;33m\\]\\[\\033[0;31;4m\\]\${?#0}\\[\\033[0;33m\\]\$ \\[\\033[0m\\]"
Note: this addresses a natural extension to this question, in a more enduring way then a comment.
Bash
function my_prompt {
local retval=$?
local field1='\u#\h'
local field2='\w'
local field3='$([ $SHLVL -gt 1 ] && echo \ shlvl:$SHLVL)$([ \j -gt 0 ] && echo \ jobs:\j)'"$([ ${retval} -ne 0 ] && echo \ exit:$retval)"
local field4='\$'
PS1=$'\n'"\e[0;35m${field1}\e[m \e[0;34m${field2}\e[m\e[0;31m${field3}\e[m"$'\n'"\[\e[0;36m\]${field4}\[\e[m\] "
}
PROMPT_COMMAND="my_prompt; ${PROMPT_COMMAND}"
Zsh
PROMPT=$'\n''%F{magenta}%n#%m%f %F{blue}%~%f%F{red}%(2L. shlvl:%L.)%(1j. jobs:%j.)%(?.. exit:%?)%f'$'\n''%F{cyan}%(!.#.$)%f '
Images of prompt

Accessing global label in function using pathname in Tk

I am using following code to change text of a label from within a function. For this I have to use a variable name for a label.
#! /usr/bin/wish8.6
set mylabel [label .a_lab -text "INITIAL TEXT"]
button .a_button -text "Change" -command changer
pack .a_lab -fill both -expand 1
pack .a_button -fill both -expand 1
proc changer {} {
# $::mylabel config -text "changed!";# works;
$::.a_lab config -text "NEW TEXT!" ;# does not work;
}
wm geometry . 300x200+300+300
Can I not use only its pathname (.a_lab) to change its text? I want second statement in the changer function above to work. Thanks for your help.
To use the pathname, remove the $:: prefix. The prefix is used to substitute a variable value for a variable name. A pathname is a command name and should be used as it is.

Pastebinit script will not word wrap

I found this "googlizer" script that launches my browser and searches using whatever is highlighted on my system as the google search string. I've modified it using pastebinit package here on Ubuntu so that it takes what is highlighted, pipes it to pastebinit (using paste.ubuntu.com), and finally pipes the pastebin url back to the clipboard.
It works well except for the fact that the pasted text comes back a single line.
Here is is my script:
#!/bin/sh
exec wish "$0" "$#"
wm withdraw .
if { [catch { set word [selection get "STRING"] }] != 0 } { exit 0 }
regsub -all "( |\n)+" "$word" " " googleWord
exec echo \"$googleWord\" | pastebinit -b http://paste.ubuntu.com | xclip -selection clipboard
exit 0
How can I preserve the original pre-script formatting?

BASH scripts : whiptail file select

I've found a great little program that will allow me to add user friendly GUI's to my Bash Scripts;
whiptail
However the whiptail man page isn't all that helpful and doesn't provide any examples. After doing some google searches I understand how to create a simple yes/no menu using whiptail:
#! /bin/bash
# http://archives.seul.org/seul/project/Feb-1998/msg00069.html
if (whiptail --title "PPP Configuration" --backtitle "Welcome to SEUL" --yesno "
Do you want to configure your PPP connection?" 10 40 )
then
echo -e "\nWell, you better get busy!\n"
elif (whiptail --title "PPP Configuration" --backtitle "Welcome to
SEUL" --yesno " Are you sure?" 7 40)
then
echo -e "\nGood, because I can't do that yet!\n"
else
echo -e "\nToo bad, I can't do that yet\n"
fi
But what I would really like to build a file select menu using whiptail to replace some old code I have in a few different backup/restore bash scripts I have:
#!/bin/bash
#This script allows you to select a file ending in the .tgz extension (in the current directory)
echo "Please Select the RESTORE FILE you would like to restore: "
select RESTOREFILE in *.tgz; do
break #Nothing
done
echo "The Restore File you selected was: ${RESTOREFILE}"
I assume this has to be done via the '--menu' option of whiptail, but I am not sure how to go about it?
Any pointers?
Or can you point me in the direction of some whiptail examples?
Build an array of file names and menu select tags:
i=0
s=65 # decimal ASCII "A"
for f in *.tgz
do
# convert to octal then ASCII character for selection tag
files[i]=$(echo -en "\0$(( $s / 64 * 100 + $s % 64 / 8 * 10 + $s % 8 ))")
files[i+1]="$f" # save file name
((i+=2))
((s++))
done
A method like this will work even if there are filenames with spaces. If the number of files is large, you may have to devise another tag strategy.
Using alpha characters for the tags lets you press a letter to jump to the item. Numeric tags don't seem to do that. If you don't need that behavior, then you can eliminate some complexity.
Display the menu:
whiptail --backtitle "Welcome to SEUL" --title "Restore Files" \
--menu "Please select the file to restore" 14 40 6 "${files[#]}"
If the exit code is 255, the dialog was canceled.
if [[ $? == 255 ]]
then
do cancel stuff
fi
To catch the selection in a variable, use this structure (substitute your whiptail command for "whiptail-command"):
result=$(whiptail-command 2>&1 >/dev/tty)
Or
result=$(whiptail-command 3>&2 2>&1 1>&3-)
The variable $result will contain a letter of the alphabet that corresponds to a file in the array. Unfortunately, Bash prior to version 4 doesn't support associative arrays. You can calculate the index into the array of the file from the letter like this (notice the "extra" single quote):
((index = 2 * ( $( printf "%d" "'$result" ) - 65 ) + 1 ))
Example:
Welcome to SEUL
┌──────────┤ Restore Files ├───────────┐
│ Please select the file to restore │
│ │
│ A one.tgz ↑ │
│ B two.tgz ▮ │
│ C three.tgz ▒ │
│ D another.tgz ▒ │
│ E more.tgz ▒ │
│ F sp ac es.tgz ↓ │
│ │
│ │
│ <Ok> <Cancel> │
│ │
└──────────────────────────────────────┘
Whiptail is a lightweight reimplementation of the most popular features of dialog, using the Newt library. I did a quick check, and many features in Whiptail seem to behave like their counterparts in dialog. So, a dialog tutorial should get you started. You can find one here but Google is your friend of course. On the other hand, the extended example probably contains a lot of inspiration for your problem.
This function is part of my function repository for whiptail
# ----------------------------------------------------------------------
# File selection dialog
#
# Arguments
# 1 Dialog title
# 2 Source path to list files and directories
# 3 File mask (by default *)
# 4 "yes" to allow go back in the file system.
#
# Returns
# 0 if a file was selected and loads the FILE_SELECTED variable
# with the selected file.
# 1 if the user cancels.
# ----------------------------------------------------------------------
function dr_file_select
{
local TITLE=${1:-$MSG_INFO_TITLE}
local LOCAL_PATH=${2:-$(pwd)}
local FILE_MASK=${3:-"*"}
local ALLOW_BACK=${4:-no}
local FILES=()
[ "$ALLOW_BACK" != "no" ] && FILES+=(".." "..")
# First add folders
for DIR in $(find $LOCAL_PATH -maxdepth 1 -mindepth 1 -type d -printf "%f " 2> /dev/null)
do
FILES+=($DIR "folder")
done
# Then add the files
for FILE in $(find $LOCAL_PATH -maxdepth 1 -type f -name "$FILE_MASK" -printf "%f %s " 2> /dev/null)
do
FILES+=($FILE)
done
while true
do
FILE_SELECTED=$(whiptail --clear --backtitle "$BACK_TITLE" --title "$TITLE" --menu "$LOCAL_PATH" 38 80 30 ${FILES[#]} 3>&1 1>&2 2>&3)
if [ -z "$FILE_SELECTED" ]; then
return 1
else
if [ "$FILE_SELECTED" = ".." ] && [ "$ALLOW_BACK" != "no" ]; then
return 0
elif [ -d "$LOCAL_PATH/$FILE_SELECTED" ] ; then
if dr_file_select "$TITLE" "$LOCAL_PATH/$FILE_SELECTED" "$FILE_MASK" "yes" ; then
if [ "$FILE_SELECTED" != ".." ]; then
return 0
fi
else
return 1
fi
elif [ -f "$LOCAL_PATH/$FILE_SELECTED" ] ; then
FILE_SELECTED="$LOCAL_PATH/$FILE_SELECTED"
return 0
fi
fi
done
}
The use is simple
if dr_file_select "Please, select a file" /home/user ; then
echo "File Selected: \"$FILE_SELECTED\"."
else
echo "Cancelled!"
fi
I've tried following, which worked:
whiptail --title "PPP Config" --backtitle "Welcome to SEUL" --menu YourTitle 20 80 10 `for x in $(ls -1 *.tgz); do echo $x "-"; done`
you might change this into a multiple-liner as well, i've added checking for empty list:
MYLIST=`for x in $(ls -1 *.tgz); do echo $x "-"; done`
WC=`echo $MYLIST | wc -l`
if [[WC -ne 0]]; then
whiptail --title "PPP Config" --backtitle "Welcome to SEUL" --menu YourTitle 20 80 10 $MYLIST
fi
you need to adjust the numbers in order to get a cleaninterface. And you may replace the "-" by anything else if you want to. But if you don't, you will see 2 entries per line.
By the way: The selected entry is printed onto stderr.
This could need some more improving, but for a basic idea I think it's enough.
This seems to be one of the top results when you search for whiptail, and none of the previous results worked for me. This is what I wound up using:
#! /bin/bash
shopt -s nullglob
dir=`pwd`
cd /path/to/files
arr=(*.tgz)
for ((i=0; i<${#arr[#]}; i++)); do j=$((2*$i+2)); a[j]="${arr[$i]}"; a[j+1]=""; done
a[0]=""
# Next line has extra spaces at right to try to center it:
a[1]="Please make a selection from the files below "
result=$(whiptail --ok-button "OK button text" --cancel-button "Cancel Button Text" --title "Title Text" --backtitle "Text at upper left corner of page" --menu "Menu Text (not used??)" 30 160 24 "${a[#]}" 2>&1 >/dev/tty)
if [[ $? = 0 ]]
then
# ge 5 in next line should be length of file extension including . character, plus 1
[ ${#result} -ge 5 ] && outfile="/path/to/files/$result" || echo "Selection not made"
fi
cd "$dir"
$result will be empty if no valid selection was made. I added a dummy selection at the top of the list that returns an empty string as a result, so that you won't accidentally select the wrong file by accidentally hitting Enter right after the menu comes up. If you don't want that, then in the "for" line remove the +2 in "do j=$((2*$i+2))" and also the following two lines that set a[0] and a[1] explicitly.
The confusing thing about whiptail is that when reading from an array in a situation like this it expects two data items per line, both of which are displayed, the first being the result you want returned if the line is expected (which in some situations might be a letter or a number) and the second being whatever descriptive text you may want. That's why for the first line I use a[0] to give an empty string as the result, and a[1] as the descriptive text, but from there on the first item in the pair contains the filename (which is what I actually want returned) and the second is an empty string, since I don't want to display any text other than the filename on those lines.
Also a previous post said whiptail returned an error code of 255 if the cancel button was pressed, but that was not the case for the version I have - it returns 1. So I just test for an error code of 0 and if it is I assume it may be a valid entry, then I test for a valid string length (more than just the number of characters in the file extension, including the . character) to be sure.

Resources