How to check if a string is empty - if-statement

I need to check if a string (user input on an entry box) is empty or not and do some action based on that condition once I push the "Play" button.
This is my script:
entry .eInput -width 50 -relief sunken -justify center -textvariable Input
button .bAction -text "Play" -width 30 -height 5 -activebackground green -font "-12"
bind .bAction <1> {
if {$Input ne ""}
{ puts "string is not empty"}
else { puts "string is empty"}
}
But when I push the "Play" button this is the error:
wrong # args: no script following "$Input ne """ argument
wrong # args: no script following "$Input ne """ argument
while executing
"if {$Input ne ""}"
(command bound to event)
Any idea on how to fix it?

The problem is that you don't pass enough arguments to if
if {$Input ne ""}
Is not valid. In Tcl commands end with a newline.
Try to use
if {$Input ne ""} then {
puts "string is not empty"
} else {
puts "string is empty"
}

Related

NSIS - Error trying to compare two strings

I'm trying to compare a string with a variable with have exactly the same value and isn't working.
The flow is:
1. Open a file that has just one line of Node.Js version;
2. Read the line and save into $NODE_VERSION
3. Close file
4. Check if $NODE_VERSION is equal "v8.11.3" -- and this always returns false.
I had already:
1. Create another variable and set in hardcoded the same values for both and compare.
2. Compare the $NODE_VERSION with the string "v8.11.3"
3. Compare "1" = "1" and works.
4. Use If/EndIf
5. Use StrCmp
Var /GLOBAL NODE_VERSION<br/>
Function .onInit<br/>
ExecWait "node --version > C:\Windows\nodeversion.txt"<br/>
ClearErrors<br/>
FileOpen $0 "C:\Windows\nodeversion.txt" r<br/>
IfErrors done<br/>
FileRead $0 $NODE_VERSION<br/>
FileClose $0<br/>
StrCmp $NODE_VERSION "v8.11.3" 0 nobla<br/>
Messagebox MB_OK "not true, or maybe"<br/>
nobla:<br/>
Messagebox MB_OK "not true"<br/>
Messagebox MB_OK $NODE_VERSION<br/>
${If} $NODE_VERSION == "v8.11.3"<br/>
Call uninstallNode<br/>
Goto FinishInit<br/>
${EndIf}<br/>
I want to get into a true statement
FileRead includes newline-characters in the returned string and you must remove them when you are looking for a exact string match.
!include "LogicLib.nsh"
!include "StrFunc.nsh"
${StrTrimNewLines} ; Tell StrFunc.nsh to define this function for us
Section
FileOpen $0 "$windir\nodeversion.txt" r
FileRead $0 $1
${StrTrimNewLines} $1 $1
FileClose $0
MessageBox mb_ok "Line 1=|$1|"
${If} "v8.11.3" == "$1"
; ...
${EndIf}
SectionEnd

extra words after "else" clause in "if" command for TCL code

I have a tcl code where -
if { $xyz == 43 } {
# 1. Set some variables
set my_filename_log "somename_log.txt"
if {[file exists $my_filename_log ]} {
file delete -force $my_filename_log
}
} # end of xyz = 43
I am getting error as - TCLERROR: wrong # args: extra words after "else" clause in "if" command while compiling -
if { $xyz == 43 } {
# 1. Set some variables...
Not sure what is wrong with this code, checked tcl documentation didnt find anything
In Tcl, for else if condition should be written without space as elseif. Otherwise you will get the error you have mentioned.
if {$xyz==1} {
puts "Yes"
} else if {$xyz==2} {
puts "No"
}
wrong # args: extra words after "else" clause in "if" command
In your code, you have used
} # end of xyz = 43
at the last line. You have to use ; to mark the end of the statements and then you can start the comments with #.
So, your code should be written as,
if { $xyz == 43 } {
# 1. Set some variables
set my_filename_log "somename_log.txt"
if {[file exists $my_filename_log ]} {
file delete -force $my_filename_log
}
}; # end of xyz = 43

Powershell: How to Validate Range between 1 to 15 and it should accepts pattern as 1,2,3,4,5 to 15

I am a newbie in Power Shell Scripting.
I am trying to Achieve a functionality, that should accepts inputs from user in below criteria
Only Digits
Range Between 1 to 15
Should accept String of Array with comma Separated values ex: 1,2,3,4,14,15
Can Contain space in between commas
Values should not be duplicated
The Returned values must be Array
Till now, I have tried
Function Validate-Choice{
[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory=$True)]
[ValidateRange(1,15)]
[string[]]$Item
)
Process {$Item}
}
Validate-Choice 1,2,3,4,5,6,7,8,9,10,11,13 # Similar Way i want O/p
Out Put:
1
2
3
4
5
6
7
8
9
10
11
13
$ReadInput = Read-Host -prompt "Please Choose from the list [1/2/3/4/5/6/7/8/9/10/11/12/13/14] You can select multiple Values EX: 1, 2, 3 -- "
$userchoices = Validate-Choice -item $ReadInput
$userchoices
If read the same input from Host Getting Below Error
Validate-Choice : Cannot validate argument on parameter 'Item'. The argument cannot be validated because
its type "String" is not the same type (Int32) as the maximum and minimum limits of the parameter. Make sure the argument is of type Int32 and then try the command again. At line:10 char:21
+ Validate-Choice '1,2,3,4,5,6,7,8,9,10,11,13'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Validate-Choice], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Validate-Choice
And also i am trying with different Regex patterns. But failing
Function Test-Something {
[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory=$True)]
[ValidatePattern('(?:\s*\d{1,15}[1-15]\s*(?:,|$))+$')]
[string[]]$Item
)
Process { $Item }
}
The above functions are partially resulting.
Can any one please help me here..!?
This would probably be easiest if you just changed the parameter type to [int[]] then your ValidateRange attribute does most of the work. It doesn't handle duplicates though. Turns out you can't use [ValidateScript()] as #PetSerAl points out. So that leaves checking the parameter the old fashioned way, in a begin block:
Function Test-Something {
[cmdletbinding()]
Param(
[Parameter(Position=0, Mandatory)]
[ValidateRange(1, 15)]
[int[]]$Item
)
Begin {
$ht = #{}
foreach ($i in $Item) {
if ($ht.ContainsKey("$i")) {
throw "Parameter Item must contain unique values - duplicate '$i'"
}
else {
$ht["$i"] = $i
}
}
}
Process {
[string]$Item
}
}
Test-Something (1,2,3,4,3)
Note that this won't work if you make the Item parameter accept pipeline input. In the pipeline input case, $Item will not be set in the begin block.
Naren_Ch
Your 1st usage of the advanced function (AF)
Validate-Choice 1,2,3,4,5,6,7,8,9,10,11,13 # Similar Way i want O/p
is correct - you are inputting data as expected by the AF.
Now, look at the example reading the input from the host:
$ReadInput = Read-Host -prompt "Please Choose from the list [1/2/3/4/5/6/7/8/9/10/11/12/13/14] You can select multiple Values EX: 1, 2, 3 -- "
When you do this, $ReadInput is a string, and in this case, it's a string full of commas!
Consequently, your data inputted to the AF will result in error caused by validation code, written by yourself.
To correct the situation, just do this:
$ReadInput = (Read-Host -prompt "Please Choose etc...") -split ','
$userchoices = Validate-Choice -item $ReadInput
You must remember that data read by Read-Host is a string (just 1 string).

awk: process input from pipe, insert result before pattern in output file

I am trying to process an input stream through a simple awk function, and insert the results before an anchor pattern in an existing file. My function works fine, but I can't convince awk to write the results where and how I want them. The input is being piped as XML from hunspell to my script, which accepts as an argument the output file.
hunspell -L -H ./text.xml | ./parse.awk ./output.xml
#!/usr/bin/awk -f
#
function buildObjs()
{
a["x"]=$4*mils; a["y"]=-$5*mils; a["w"]=$6*mils; a["h"]=$7*mils
print "## element" NR+1 " [x]="a["x"]" [y]="a["y"]" [width]="a["w"]" [height]="a["h"]
print "set fsize("NR+1") {FALSE}"
print "set fmargin("NR+1") {FALSE}"
print "set fmaster("NR+1") {TRUE}"
print "set ftype("NR+1") {box}"
print "set fname("NR+1") {"a["w"],a["h"]"}"
print "set fatt("NR+1") {1}"
print "set dplObjectSetup("NR+1",TRA) {"a["x"],a["y"]"}"
print "set fnum("NR+1") {}"
return 0
}
BEGIN {
FS = "[\" ]+"
mils = "0.3527"
for (i = 0; i < ARGC; i++) {
# Use this block to identify the output file we need to write to.
if (ARGV[i] ~ /output.+/) {
outFile=ARGV[i]
delete ARGV[i]
}
}
while ((getline line < outFile) > 0) {
if (line ~ !/set lineno \{.+\}/) {
print line
}
}
close(outFile)
}
{
buildObjs()
}
END {
print "set lineno {"NR+2"}"
while ((getline line < outFile) > 0) {
if (line ~ /set mode \{.+\}/) {
print line
} else
print line
}
close(outFile)
}
The anchor pattern I'm looking for in the outFile is "set lineno {3}". In the stream from text.xml, there are 20 lines, and the buildObjs function loops on every line of input, meaning I'm defining 20 objects in the output file, iterating the object count with NR+1 as I go. The number of lines to be processed will vary from job to job. As a bonus question, the anchor pattern needs to be updated so that "set lineno {3}" becomes "set lineno {NR+2}" with whatever NR+2 resolves to for that job. Presumably I will do this in the END block, but right now I just need to get my new objects into the output file.
So in summary, stream input into my awk script, process it with my function, insert the result before a pattern in an existing file, update the anchor pattern and close the modified file. This is my first substantial use of awk outside of one-liners embedded in bash scripts. Any help is most appreciated.
EDIT: I've updated the code to reflect comments from Jonathan.
EDIT2: Updated the END block. This code now does what I want.
EDIT3: Here are a few lines being piped into my script:
<w box="45.2044 92.54 61.5253 9.503" xoffset="6.27627 12.6244 19.4125 25.5994 32.2113 38.159 48.5253">Technews</w>
<w box="407.31 91.6 107.774 10.443" xoffset="1.8024 6.7944 12.1711 18.8203 29.2284 36.5576 43.2034 50.0661 56.641 61.0251 68.0434 74.6553 85.0634 92.0651 97.1957 100.274">Issue256:June27th</w>
<w box="67.923 132.463 32.747 7.337" xoffset="6.259 14.168 19.129 21.747">DALIM</w>
An excerpt from the outFile:
# file.encoding: UTF-8
# sun.jnu.encoding: UTF-8
set toolVersion {1.20}
set ftype(0) {pgs}
set fsize(0) {FALSE}
set fmargin(0) {FALSE}
set fsize(1) {TRUE}
set fmargin(1) {TRUE}
set fmaster(1) {FALSE}
set ftype(1) {pgs}
set fname(1) {}
set fatt(1) {0}
set dplObjectSetup(1,TRA) {}
set fnum(1) {}
>> New data inserted here.
set lineno {2} << This number updated to reflect the total number of objects.
set mode {1}
set preservePDF {1}
set preservePDFAction {Continue}
So, as I hope this clarifies, after processing the piped input, I'm inserting additional blocks starting with "set fsize()" and ending with "set fnum()", incrementing as I go, and summing the total number of blocks in "set lineno {}" before finally appending the trailing lines after "set lineno {}".
Here's a modified version of your executable awk script that produces the ordering you want:
#!/usr/bin/awk -f
BEGIN { FS="[{}]"; mils="0.3527"; built=1 }
FNR==NR {
if( $1 !~ /set lineno/ ) {
if( lineno != "" ) { footer[++cnt]=$0; if(cnt==3) { FS = "[\" ]+" } }
else print
}
else { lineno=$2 }
next
}
FNR!=NR && NF > 0 { built += buildObjs( built+1 ) }
END {
print "set lineno {" built "}"
for(i=1;i<=cnt;i++ ) {
print footer[i]
}
}
function buildObjs( n )
{
x=$4*mils; y=-$5*mils; w=$6*mils; h=$7*mils
print "## element" n " [x]=" x " [y]=" y " [width]=" w " [height]=" h
print "set fsize(" n ") {FALSE}"
print "set fmargin(" n ") {FALSE}"
print "set fmaster(" n ") {TRUE}"
print "set ftype(" n ") {box}"
print "set fname(" n ") {" w " " h "}"
print "set fatt(" n ") {1}"
print "set dplObjectSetup(" n ",TRA) {" x " " y "}"
print "set fnum(" n ") {}"
return 1
}
When put into a file called awko it would be run like:
hunspell -L -H ./text.xml | ./awko ./output.xml -
I don't have hunspell installed, so I tested this by running the Edit3 piped output from a file via cat:
cat ./pipeddata | ./awko ./output.xml -
Notice the - at after the output file. It's telling awk to read from stdin as the 2nd input to the awk script, which lets me deal with the first file with the standard FNR==NR { do stuff; next } logic.
Here's the breakdown:
For personal preferences, I moved the buildObjs() function to the end of the script. Notice I added a n argument to it - NR won't be used in the output. I dropped the a array because it didn't seem to be necessary and changed it's return from 0 to 1.
In the BEGIN block, setup output.xml file parsing, and mils
Whenever the FILENAME changes to -, change FS for parsing that input. The piped data FS could instead be set on the command line between the output file and the -.
When FNR==NR handle the first file
Basically, print the "header" info when your anchor hasn't been read
When the anchor is read, store it's value in lineno
After the anchor is read, store the last of the output file into the footer array in cnt order. Knowing there are only 3 lines at the end, I "cheated" to adjust the FS before the first record is read from STDIN.
When FNR!=NR and the line isn't blank (NF>0), process the piped input, incrementing built and passing it with a offset of 1 as an arg to buildObjs() ( as built starts with a value of 0 ).
In the END, the set lineno line is reconstructed/printed with the sum of lineno and built.
Then the footer from the first file is printed in order based on the cnt variable
Using the cat form, I get following:
# file.encoding: UTF-8
# sun.jnu.encoding: UTF-8
set toolVersion {1.20}
set ftype(0) {pgs}
set fsize(0) {FALSE}
set fmargin(0) {FALSE}
set fsize(1) {TRUE}
set fmargin(1) {TRUE}
set fmaster(1) {FALSE}
set ftype(1) {pgs}
set fname(1) {}
set fatt(1) {0}
set dplObjectSetup(1,TRA) {}
set fnum(1) {}
## element2 [x]=32.6389 [y]=-21.7 [width]=3.35171 [height]=0
set fsize(2) {FALSE}
set fmargin(2) {FALSE}
set fmaster(2) {TRUE}
set ftype(2) {box}
set fname(2) {3.35171 0}
set fatt(2) {1}
set dplObjectSetup(2,TRA) {32.6389 -21.7}
set fnum(2) {}
## element3 [x]=32.3073 [y]=-38.0119 [width]=3.68325 [height]=0
set fsize(3) {FALSE}
set fmargin(3) {FALSE}
set fmaster(3) {TRUE}
set ftype(3) {box}
set fname(3) {3.68325 0}
set fatt(3) {1}
set dplObjectSetup(3,TRA) {32.3073 -38.0119}
set fnum(3) {}
## element4 [x]=46.7197 [y]=-11.5499 [width]=2.58776 [height]=0
set fsize(4) {FALSE}
set fmargin(4) {FALSE}
set fmaster(4) {TRUE}
set ftype(4) {box}
set fname(4) {2.58776 0}
set fatt(4) {1}
set dplObjectSetup(4,TRA) {46.7197 -11.5499}
set fnum(4) {}
set lineno {4}
set mode {1}
set preservePDF {1}
set preservePDFAction {Continue}
Seems like your buildObj() function logic needs some attention to get things just the way you want (I suspect the indexes you've chosen need shifting).
The main action { buildObjs(); PROFIT??; } (for whatever PROFIT?? means) is acted on for each line since there is no pattern before it. You should add a pattern to recognize where you want the output added:
/set lineno \{3\}/ { buildObjs() }
{ print }
The first recognizes the pattern ({ is an awk regex metacharacter, so it must be escaped), and the second ensures that the line is printed after the output from buildObjs(). Reverse the lines if you want the set lineno {3} to come first, of course.

How to match the variable in switch with contents of a list?

I have a doubt concerning the use of switch in tcl. Mainly, I was wondering if it was possible to make something like:
switch myvar {
list1 {
puts "myvar matches contents of list1"; }
list2 {
puts "myvar matches contents of list2"; }
default {
puts "myvar doesn't match any content of any list"; }
}
In here, list1 and list2 would be either a list or array of strings containing the names of different files.
Is this even possible without making a very detailed regexp search?
Thanks!
You can rewrite it as an if elseif else construct easily, as Brian Fenton already said (and simplify it with the 'in' operator too.
if {$myvar in $list1} {
puts "myvar matches content of list"
} elseif {$myvar in $list2} {
puts "myvar matches content of list2"
} elseif {
puts "myvar doesn't match any content of any list"
}
You could of course wrap up the code and write your own switch version that does what you want, after all, this is Tcl...
proc listswitch {item conditions} {
if {[llength $conditions] % 2} {
return -code error "Conditions must be pairs"
}
set code ""
foreach {cond block} $conditions {
if {$cond eq "default"} {
set code $block
break
} elseif {$item in $cond} {
set code $block
break
}
}
if {$code ne ""} {
uplevel 1 $code
}
}
listswitch 10 {
{10 20 30 50} {
puts "Match in list 1" }
{50 20 90 11} {
puts "Match in list 2"
}
default {
puts "No match"
}
}
You need to worry a little if you want to match filenames literally, or what kind of equality your interested in though. There are some subtle things there, like case insensitive filesystems, different directory separators, absolute vs. relative and even stuff like filesystem encodings which might change the outcome.
Nice question Jason. At first, I thought you wanted a way to compare the contents of two lists. But I think you want to check if the string is a member of the lists. I don't see any easy way to do that with switch, so what I would do is very simply to use lsearch.
if {[lsearch $list1 $myvar ] != -1} {
puts "myvar matches contents of list1"; }
} elseif {[lsearch $list2 $myvar ] != -1} {
puts "myvar matches contents of list2"; }
} else
puts "myvar doesn't match any content of any list"; }
}