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.
Related
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...
disk/file1:
parameter wanted = 108,
From file1 I want to get value of wanted i.e 108 and store it in val. For this I used
val=`sed -n 's/.*wanted = \(.*\)\,.*/\1/p' disk/file1`
and it's working.
Now I want set this path to a variable and pass it same as above. But its not happening.
Code:
set pa=disk/file1
val=`sed -n 's/.*wanted = \(.*\)\,.*/\1/p' $pa`
How to pass $ values in ` `?
If you're using bash or similar, don't use set to assign values to variables (that was a (t)csh syntax). In fact, there's no keyword for it:
pa=disk/file1
What set does when it sees parameter it can't parse is it assigns it to the next positional parameter. For example,
set pa=disk/file1
sets $1 to pa=disk/file1. (Easily verifiable by echo "$1".)
I want to use an if \ else statement to determine which cmdlet to run while keeping the same params for both commands:
For example I have this call:
New-AzureRmResourceGroupDeployment `
-ResourceGroupName $ResourceGroup `
-TemplateFile $TemplateUri `
-TemplateParameterFile $TemplateParamFile
But I want to use a variable to determine the verb:
$myVerb = if ($booleanTest) {"Test"} else {"New"}
[$myVerb]-AzureRmResourceGroupDeployment `
-ResourceGroupName $ResourceGroup `
-TemplateFile $TemplateUri `
-TemplateParameterFile $TemplateParamFile
OR something like this:
$command = if ($booleanTest) {"Test-AzureRmResourceGroupDeployment"} else {"New-AzureRmResourceGroupDeployment"}
$command `
-ResourceGroupName $ResourceGroup `
-TemplateFile $TemplateUri `
-TemplateParameterFile $TemplateParamFile
I tried the $command version but it failed with this:
At
C:\Users\Administrator\Dropbox\projects\deloitte\Suncore\Dev\scripts\az-d
eploy.ps1:36 char:13
+ -ResourceGroupName $ResourceGroup
+ ~~~~~~~~~~~~~~~~~~ Unexpected token '-ResourceGroupName' in expression or statement. At
C:\Users\Administrator\Dropbox\projects\deloitte\Suncore\Dev\scripts\az-d
eploy.ps1:36 char:32
+ -ResourceGroupName $ResourceGroup
+ ~~~~~~~~~~~~~~
To do exactly what you are describing you'd need to wrap the whole command as a string and then call it with Invoke-Expression. For Example:
$MyCommand = "$myVerb-AzureRmResourceGroupDeployment -ResourceGroupName $ResourceGroup -TemplateFile $TemplateUri"
Invoke-Expression $MyCommand
But I think this isn't a very clear way to write a script. A better option would be to use Splatting, which is where you create a hashtable of the parameters that you can then send the cmdlet via a special # character with the variable name. For example:
$AzureParams = #{
ResourceGroupName = $ResourceGroup
TemplateFile = $TemplateUri
TemplateParameterFile = $TemplateParamFile
}
If ($booleanTest) {
Test-AzureRmResourceGroupDeployment #AzureParams
} Else {
New-AzureRmResourceGroupDeployment #AzurParams
}
This also has the benefit of avoiding using the backtick character, which is generally encouraged as it's hard to spot and easy to break.
I don't recommend using this over the other answer but to directly answer your question, add the invocation operator &
$command = if ($booleanTest) {
"Test-AzureRmResourceGroupDeployment"
} else {
"New-AzureRmResourceGroupDeployment"
}
& $command `
-ResourceGroupName $ResourceGroup `
-TemplateFile $TemplateUri `
-TemplateParameterFile $TemplateParamFile
To complement the existing helpful answers:
Invoke-Expression should always be the last resort and is not needed here. With Invoke-Expression, it is tricky to get the quoting right, and its use can be a security risk (execution of arbitrary commands passed as a string, analogous to eval in POSIX-like shells).
Splatting (Get-Help about_Splatting) is always worth considering:
it is a more robust alternative to multi-line parameter-passing with line continuations, as Mark's answer explains.
it allows incremental, conditional construction of the set of parameter values as well as their use in multiple invocations.
In the case at hand, however, since only the command name is variable, Patrick's answer based on &, the call operator is simplest (see Get-Help about_Operators).
Generally, you need & whenever the command name is not an unquoted literal (e.g., notepad foo.txt works, but 'notepad' foo.txt doesn't).
To put it differently: you need &, if your command name is:
in quotes (whether '...' or "..."); e.g., 'notepad'
or is a variable reference; e.g., $cmdName
or is the result of an expression (e.g., ('get' + '-item')
& is needed in theses case in order to tell PowerShell that your intent is to invoke a command as opposed to evaluating an expression; without &, such tokens would be interpreted as starting an expression; see Get-Help about_Parsing to learn about PowerShell's two fundamental parsing modes, argument mode and expression mode.
While it may not be the most readable solution, you can therefore even combine an expandable string with an embedded subexpression ($(...) - again, see Get-Help about_Operators) to get away without the need for an aux. variable:
& "$( if ($booleanTest) {'Test'} else {'New'} )-AzureRmResourceGroupDeployment" `
-ResourceGroupName $ResourceGroup `
-TemplateFile $TemplateUri `
-TemplateParameterFile $TemplateParamFile
Using Splatting as suggested by Mathias R. Jessen:
Function Do-AzureRmResourceGroupDeployment ([ValidateSet("Test", "New")]$Verb, $ResourceGroupName, $TemplateFile, $TemplateParameterFile) {
$PSBoundParameters.Remove("Verb")
& "$Verb-AzureRmResourceGroupDeployment" #PSBoundParameters
}
I'm looking for some direction on how to read a file line by line, then copy the line based on a search criteria to a newly created file. Since my description is probably poor, I've tried to illustrate below:
Single Text File Sample:
Name=N0060093G
Name=N0060093H
Name=N400205PW
Name=N400205PX
Name=N966O85Q0
Name=N966O85Q1
The script would read each line and use the "###" after "Name=N", to create a new file name after the identifier, "###" to copy each appropriate line to the new file. So, lines "Name=N0060093G"and "Name=N0060093H" would go to "006.txt"; "Name=N400205PW" and "Name=N400205PX" would write to "400.txt", etc.
A RegEx style approach:
$File = 'test.txt'
Get-Content $File | ForEach {
If ($_ -match '^Name\=N(?<filename>\d{3}).*') {
$_ | Out-File -Append "$($Matches.Filename).txt" -WhatIf
}
}
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