Select menu in bash - bash

For some reason, my menu renders like this:
1) RUNTHROUGH 6) option 4
2) QUIT 7) option leg
3) NEW SEARCH PATERN 8) option arm
4) CLEAR SEARCHES 9) option whatever
5) option 1 10) option blabla
,but I would like it to be aligned properly, like this:
1) RUNTHROUGH 6) option 4
2) QUIT 7) option leg
3) NEW SEARCH PATERN 8) option arm
4) CLEAR SEARCHES 9) option whatever
5) option 1 10) option blabla
How can I do that?
I am populating menu from two arrays:
One is constructed from label variables like this:
MENU_OPTIONS=( "$SCRIPT_RUNTHROUGH" "$PROGRAM_QUIT" "$PATTERN_NEW" "$PATTERN_CLEAR" )
where variable has value like this, with color escapes
SCRIPT_RUNTHROUGH=$'\e[1;31mRUNTHROUGH\e[0;33m'
PROGRAM_QUIT=$'\e[1;31mQUIT\e[0;33m'
PATTERN_NEW=$'\e[1;31mNEW SEARCH PATERN\e[0;33m'
PATTERN_CLEAR=$'\e[1;31mCLEAR SEARCHES\e[0;33m'
and another array entries is coming from file:
readarray -t entries < ./file
content of file is raw text data:
option 1
option 4
option leg
option arm
option whatever
option blabla
so select block goes like:
select opt in "${MENU_OPTIONS[#]}" "${entries[#]}";
do
case "$opt" in
...)
;;

Bash mostly count the char in each option, regardless they are "displayable" or not. So, the escape code used to highlight some of your items false the display.
Here is an example:
sh$ for i in {1..12}; do OPTIONS[$i]=$(printf "option-%02d" $i); done
sh$ select i in "${OPTIONS[#]}"; do echo $i ; done
1) option-01 4) option-04 7) option-07 10) option-10
2) option-02 5) option-05 8) option-08 11) option-11
3) option-03 6) option-06 9) option-09 12) option-12
This is as expected. But if I highlight the 4th item:
sh$ OPTIONS[4]=$'\e[1;31moption-04\e[0;33m'
sh$ select i in "${OPTIONS[#]}"; do echo $i ; done
1) option-01 4) option-04 7) option-07 10) option-10
2) option-02 5) option-05 8) option-08 11) option-11
3) option-03 6) option-06 9) option-09 12) option-12
WORKAROUND
Well ... it's more a hack than a workaround. Really.
I add some extra spaces at the end of every option in order to widen the columns. Instead, I add a few \b at the end of the highlighted option (6 gave good results on my tests). I can't be more precise, since I wasn't able to clearly determine the rule to follow. You probably have to be prepared for trial and error...(thing are even more complicated by the fact Bash uses a mix of tab and space to indent the various options)
Anyway, this display properly on my system:
sh$ for i in {1..12}; do OPTIONS[$i]=$(printf "option-%02d " $i); done
# ^^^^^^^^
sh$ OPTIONS[4]=$'\e[1;31moption-04\e[0;33m\b\b\b\b\b\b'
# ^^^^^^^^^^^^
sh$ select i in "${OPTIONS[#]}"; do echo $i ; done
1) option-01 5) option-05 9) option-09
2) option-02 6) option-06 10) option-10
3) option-03 7) option-07 11) option-11
4) option-04 8) option-08 12) option-12
EDIT: I was puzzled by the fact in my example, the menu was off by 8 char. Doesn't match with the number of bytes in the various escape codes.
But, given the code from GNU bash 4.2 :
(execute_cmd.c)
static int
displen (s)
const char *s;
{
#if defined (HANDLE_MULTIBYTE)
wchar_t *wcstr;
size_t wclen, slen;
wcstr = 0;
slen = mbstowcs (wcstr, s, 0);
if (slen == -1)
slen = 0;
wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (slen + 1));
mbstowcs (wcstr, s, slen + 1);
wclen = wcswidth (wcstr, slen);
free (wcstr);
return ((int)wclen);
#else
return (STRLEN (s));
#endif
}
It appears that if Bash is compiled with multibyte support, it converts the raw string using mbstowcs in order to calculate its length using wcswidth. Given my system has UTF-8 LC_CTYPE, probably mbstowcs misconverted the escape code as some Unicode character...

Related

Syntax to generate a Syntax in SPSS

I’m trying to construct a Syntax to generate a Syntax in SPSS, but I’m having some issues…
I have an excel file with metadata and I would like to use it in order to make a syntax to extract information from it (like this, if I have a huge database, I just need to keep the excel updated – add/delete variables, etc. - and then run a syntax to extract the needed information for a new syntax).
I also noticed the produced syntax has always around 15Mb, which is a lot (applied to more than 500 lines)!
I don’t use Python due to run syntax in different computers and/or configurations.
Any ideas? Can anyone please help me?
Thank you in advance.
Example:
(test.xlsx – sheet 1)
Var Code Label List Var_label (concatenate Var+Label)
V1 3 Sex 1 V1 “Sex”
V2 1 Work 2 V2 “Work”
V3 3 Country 3 V3 “Country”
V4 1 Married 2 V4 “Married”
V5 1 Kids 2 V5 “Kids”
V6 2 Satisf1 4 V6 “Satisf1”
V7 2 Satisf2 4 V7 “Satisf2”
(information from other file)
List = 1
1 “Male”
2 “Female”
List = 2
1 “Yes”
2 “No”
List = 3
1 “Europe”
2 “America”
3 “Asia”
4 “Africa”
5 “Oceania”
List = 4
1 “Very unsatisfied”
10 “Very satisfied”
I want to make a Syntax that generates a new syntax to apply “VARIABLE LABELS” and “VALUE LABELS”. So, I thought about something like this:
GET DATA
/TYPE=XLSX
/FILE="test.xlsx"
/SHEET=name 'sheet 1'
/CELLRANGE=FULL
/READNAMES=ON
/DATATYPEMIN PERCENTAGE=95.0.
EXECUTE.
STRING vlb (A15) labels (A150) value (A12) lab (A1500) point (A2) separate (A50) space (A2) list1 (A100) list2 (A100).
SELECT IF (Code=1).
COMPUTE vlb = "VARIABLE LABELS".
COMPUTE labels = CONCAT (RTRIM(Var_label)," ").
COMPUTE point = ".".
COMPUTE value = "VALUE LABELS".
COMPUTE lab = CONCAT (RTRIM(Var)," ").
COMPUTE list1 = '1 " Yes "'.
COMPUTE list2 = '2 "No".'.
COMPUTE space = " ".
COMPUTE separate="************************************************.".
WRITE OUTFILE = "list_01.sps" / vlb.
WRITE OUTFILE = "list_01.sps" /labels.
WRITE OUTFILE = "list_01.sps" /point.
WRITE OUTFILE = "list_01.sps" /value.
WRITE OUTFILE = "list_01.sps" /lab.
WRITE OUTFILE = "list_01.sps" /list1.
WRITE OUTFILE = "list_01.sps" /list2.
WRITE OUTFILE = "list_01.sps" /space.
WRITE OUTFILE = "list_01.sps" /separate.
WRITE OUTFILE = "list_01.sps" /space.
If there is only one variable with same list (ex: V1), it works ok. However, if there is more than one variable having the same list, it reproduces the codes as much times as number of variables (Ex: V2, V4 and V5).
What I have (Ex: V2, V4 and V5), after running code above:
VARIABLE LABELS
V2 "Work"
.
VALUE LABELS
V2
1 " Yes "
2 " No "
************************************************.
VARIABLE LABELS
V4 "Married"
.
VALUE LABELS
V4
1 " Yes "
2 " No "
************************************************.
VARIABLE LABELS
V5 "Kids"
.
VALUE LABELS
V5
1 " Yes "
2 " No "
************************************************.
What I would like to have:
VARIABLE LABELS
V2 "Work"
V4 "Married"
V5 "Kids"
.
VALUE LABELS
V2 V4 V5
1 " Yes "
2 " No "
I think there are probably ways to automate the whole process better, including the use of your second data source. But for the scope of this question I will suggest a way to get what you asked for specifically.
The key is to build the command with special conditions for first and last lines:
string cmd1 cmd2 (a200).
sort cases by code.
match files /file=* /first=first /last=last /by code. /* marking first and last lines.
do if first.
compute cmd1="VARIABLE LABELS".
compute cmd2="VALUE LABELS".
end if.
if not first cmd1=concat(rtrim(cmd1), " /"). /* "/" only appears from the second varname.
compute cmd1=concat(rtrim(cmd1), " ", Var_label).
compute cmd2=concat(rtrim(cmd2), " ", Var).
do if last.
compute cmd1=concat(rtrim(cmd1), " .").
compute cmd2=concat(rtrim(cmd2), " ", ' 1 " Yes " 2 "No". ').
end if.
exe.
The commands are now ready, but we don't want to get them mixed up so we'll stack them one under the other, and only then write them out:
add files /file=* /rename cmd1=cmd /file=* /rename cmd2=cmd.
exe.
WRITE OUTFILE = "var definitions.sps" / cmd .
exe.
EDIT:
Note that the code above assumes you've already run a select cases if code = ... and that there is a single code in all the remaining lines.
Note also I added an exe. command at the end - without running that the new syntax will appear empty.

How to read variable length application identifiers in .zpl datamatrix in VB6?

According to GS1 standards (http://www.databar-barcode.info/application-identifiers/) a variable length field in a barcode should have a break sign to signal when it ends.
Code for the .zpl printer in my test is as follows:
^BY200,200^FT250,860^BXN,12,200,0,0,6,~
^FH\^FD\7E10012345678912345678910123\7E1151606013712\7E1^FS
This was written according to (http://www.servopack.de/support/zebra/ZPLbasics.pdf) and when I scan it into Notepad++ I see that the breaks are applied in the code as shown in the picture below.
But when I try to scan it in my VB6 application it doesn't appear to catch the break sign and writes everything from 10 (batch number) and forward into the batchnumber instead of breaking before 15 (expiration date).
My code looks like this:
ElseIf Left(Data, 2) = AI_BATCH Or Left(Data, 6) = "<GS>10" Or Left(Data, 3) = "~10" Then
If Left(Data, 2) = AI_BATCH Then
Data = Mid(Data, 3)
ElseIf Left(Data, 6) = "<GS>10" Then
Data = Mid(Data, 7)
ElseIf Left(Data, 3) = "~10" Then
Data = Mid(Data, 4)
End If
' Calculate length
While Mid(Data, AI_BATCH_LEN + 1, 1) <> "" And Mid(Data, AI_BATCH_LEN + 1, 1) <> "~" And Mid(Data, AI_BATCH_LEN + 1, 1) <> "<"
AI_BATCH_LEN = AI_BATCH_LEN + 1
Wend
gs1.batch = Trim(Left(Data, AI_BATCH_LEN))
Data = Mid(Data, 1 + AI_BATCH_LEN)
Thanks in advance.
You seem to be looking for the 2 ASCII characters 'G' and 'S' but you should be looking for the single 'GS' character - GS is ASCII control character 29 (Group Separator).
This character is not printable as a letter so Notepad++ (and the font its using) substitute the graphical glyph you see.
Use chrw$(29) to locate this character:
x = "Hello" & chrw$(29) & "World"
?x
HelloWorld
?left$(x, instr(x, chrw$(29)) - 1)
Hello

Very simple set value of array cell, program very slow when he writes on specify column

I am using a continuous and old professional program. My program builds several simple data arrays and writes the array to an excel cell like this:
Sheets("toto").Cells(4,i) = "blabla"
But for one value of i, the write time is very long and I don't understand why.
Here is my code :
...
For No_Bug = 0 To Indtab - 1
If mesComments(No_Bug) <> "" Then
Sheets(feuille_LBT).Cells(Ligne_Bug, 1) = Ligne_Bug - 5
Sheets(feuille_LBT).Cells(Ligne_Bug, 2) = mesID_Test(No_Bug)
Sheets(feuille_LBT).Cells(Ligne_Bug, 3) = mesResultats(No_Bug)
Sheets(feuille_LBT).Cells(Ligne_Bug, 4) = mesComments(No_Bug)
Sheets(feuille_LBT).Cells(Ligne_Bug, 5).FormulaLocal = mesScreens(No_Bug)
Sheets(feuille_LBT).Cells(Ligne_Bug, 6) = 2 'If I comment only this line, the programm is fast, ifnot the programm is very slow (~1, 2 secondes per loop), What the hell ??? xD
Sheets(feuille_LBT).Cells(Ligne_Bug, 7) = 1
End If
...
Is this cell referenced from other cells? Check if any complicated computations related with this cell.

I want AutoIt to activate a particular tab in Firefox. How can this be done?

I have several tabs open in Firefox. I want AutoIt to activate a particular tab in Firefox. How can this be done?
Give the whole browser window focus, then use the send command to repeatedly send it cntl-tab until the window's title is the name of the tab you want (with - Mozilla Firefox at the end).
There's a UDF (User Defined Functions -include file) called FF.au3. Looks like the function you want is _FFTabSetSelected(), good luck!
Below is an example of Jeanne Pindar's method. This is the way I would do it.
#include <array.au3>
Opt("WinTitleMatchMode", 2)
activateTab("Gmail")
Func activateTab($targetWindowKeyphrase)
WinActivate("- Mozilla Firefox")
For $i = 0 To 100
If StringInStr(WinGetTitle(WinActive("")),$targetWindowKeyphrase) Then
MsgBox(0,"Found It", "The tab with the key phrase " & $targetWindowKeyphrase & " is now active.")
Return
EndIf
Send("^{TAB}")
Sleep(200)
Next
EndFunc
Here you go...
AutoItSetOption("WinTitleMatchMode", 2)
$searchString = "amazon"
WinActivate("Mozilla Firefox")
For $i = 0 To 100
Send("^" & $i)
Sleep(250)
If Not(StringInStr(WinGetTitle("[ACTIVE]"), $searchString) = 0) Then
MsgBox(0, "Done", "Found it!")
ExitLoop
EndIf
Next
Just delete the MsgBox and you're all set!
As Copas said, use FF.au3. Function _FFTabSetSelected($regex,"label") will select first tab with name matching given $regex.
Nop... The script is buggy ^^'... no need to count to 100, and there is a problem with the "send" after it:
If you send ctrl + number
=>the number can't be bigger than 9... Because ten is a number with 2 caracters, Firefox can't activate tab 10 with shortcut.
And by the way when the script is working there is a moment he release the ctrl key.. It don't send ten, but ctrl and 1 end zero ... and splash !!! It just send the number in the window.
So we need to learn to the script that the second time he's back to $i = 0 or one, all the tabs was seen, no need to continue, even if the text you're searching for was not found.
So I made my own script based on the old one:
##
AutoItSetOption("WinTitleMatchMode", 2)
$searchString = "The string you're looking for"
Local $o = 0
WinActivate("The Name of the process where you're searching")
For $i = 0 To 9
Send("^" & $i)
Sleep(250)
if ($i = 9) Then
$o += 1
EndIf
If not (StringInStr(WinGetTitle("[ACTIVE]"), $searchString) = 0) Then
MsgBox("","","Found it !") ;your action, the text was found.
ExitLoop
ElseIf ($o = 1) Then
MsgBox("","","All tab seen, not found...") ;your action, the text was not found, even after looking all title.
ExitLoop
EndIf
Next
##
I haven't touched AutoIt in years, but IIRC it will be:
setMousePos(x, y) // tab position
click("left")

What other languages have features and/or libraries similar to Perl's format?

I may be in the minority here, but I very much enjoy Perl's formats. I especially like being able to wrap a long piece of text within a column ("~~ ^<<<<<<<<<<<<<<<<" type stuff). Are there any other programming languages that have similar features, or libraries that implement similar features? I am especially interested in any libraries that implement something similar for Ruby, but I'm also curious about any other options.
I seem to recall something similar in Fortran when I used it many years ago (however, it may well have have been a third-party library).
As for other options in Perl, have a look at Perl6::Form.
The form function replaces format in Perl6. Damian Conway in "Perl Best Practices" recommends using Perl6::Form with Perl5 citing the following issues with format....
statically defined
rely on global variables for configuration and pkg variables for data they format on
uses named filehandles (only)
not recursive or re-entrant
Here is a Perl6::Form variation on the Ruby example by Robert Gamble....
use Perl6::Form;
my ( $month, $day, $year ) = qw'Sep 18 2001';
my ( $num, $numb, $location, $toe_size );
for ( "Market", "Home", "Eating Roast Beef", "Having None", "On the way home" ) {
push #$numb, ++$num;
push #$location, $_;
push #$toe_size, $num * 3.5;
}
print form
' Piggy Locations for {>>>}{>>}, {<<<<}',
$month, $day, $year ,
"",
' Number: location toe size',
' --------------------------------------',
'{]}) {[[[[[[[[[[[[[[[} {].0} ',
$numb, $location, $toe_size;
FormatR provides Perl-like formats for Ruby.
Here is an example from the documentation:
require "formatr"
include FormatR
top_ex = <<DOT
Piggy Locations for #<< ##, ####
month, day, year
Number: location toe size
-------------------------------------------
DOT
ex = <<TOD
#) #<<<<<<<<<<<<<<<< ##.##
num, location, toe_size
TOD
body_fmt = Format.new (top_ex, ex)
body_fmt.setPageLength(10)
num = 1
month = "Sep"
day = 18
year = 2001
["Market", "Home", "Eating Roast Beef", "Having None", "On the way home"].each {|location|
toe_size = (num * 3.5)
body_fmt.printFormat(binding)
num += 1
}
Which produces:
Piggy Locations for Sep 18, 2001
Number: location toe size
-------------------------------------------
1) Market 3.50
2) Home 7.00
3) Eating Roast Beef 10.50
4) Having None 14.00
5) On the way home 17.50
There is the Lisp (format ...) function. It supports looping, conditionals, and a whole bunch of other fun stuff.
for example (copied from above link):
(defparameter *english-list*
"~{~#[~;~a~;~a and ~a~:;~#{~a~#[~;, and ~:;, ~]~}~]~}")
(format nil *english-list* '()) ;' ==> ""
(format nil *english-list* '(1)) ;' ==> "1"
(format nil *english-list* '(1 2)) ;' ==> "1 and 2"
(format nil *english-list* '(1 2 3)) ;' ==> "1, 2, and 3"
(format nil *english-list* '(1 2 3 4));' ==> "1, 2, 3, and 4"

Resources