I Need tcl programm for sorting, but i should not use lsort operation.
i have tried this. but no luck
set list1 {1 6 5 4}
set list2 {}
for {set i 0} {$i < [llength $list1]} {incr i} {
set temp [lindex $list1 $i]
for {set z [expr $i+1]} {$z < [llength $list1]} {incr z} {
set temp2 [lindex $list1 $z]
#puts "$temp,$temp2,$list1,$i,$z"
#puts $temp2
if {$temp < $temp2} {
} else {
puts "$i,$z"
set list1 [lreplace $list1 $i $i $temp2]
puts "> $list1"
set list1 [lreplace $list1 $z $z $temp]
puts ":: $list1"
}
}
}
puts $list1
Thanks
Ranjith
Well, you can implement all sorts of sorting algorithms if you want. Just treat Tcl's lists like an array in any number of other languages:
Create with lrepeat if necessary, though with a sorting algorithm you're usually given the list to sort.
Read the value at a particular index with lindex.
Write the value at a particular index with lset.
As a hint, here is how to swap the elements at two indices, $i and $j. It uses a temporary variable, tmp:
set tmp [lindex $list1 $i]
lset list1 $i [lindex $list1 $j]
lset list1 $j $tmp
Related
I want to insert one string at the certain line ,and I know the line number.
ex.
#Aa_version = Aa/45.21-a32_1
#Aa_version = Aa/47.21-a33_1
Aa_version = Aa/45.27-a57_2 ->I can get this line number n
and I want to insert one line Aa/49.27-a54_1 at line n+1
and put Aa_version = Aa/45.27-a57_2 -> #Aa_version = Aa/45.27-a57_2
output is like
#Aa_version = Aa/45.21-a32_1
#Aa_version = Aa/47.21-a33_1
#Aa_version = Aa/45.27-a57_2
Aa_version = Aa/49.27-a54_1
and my code is
set Aa ""
set fp [open $file "r+"]
set lines [split [read $fp] \n]
set idx [lsearch -regexp $lines {^Aa_version} ]
regexp {Aa(.+)} [lindex $lines $idx] Aa_version
set old_version "#$Aa_version"
set newAa [gets stdin]
set new_version "Aa_version =$newAa "
puts $old_version ->replace $Aa_version
puts $new_version
close $fp
How can I put them at the correct line
thanks
I think it's easier and cleaner to handle the input file a line at a time and look at each one in turn instead of reading it all in one single go and then finding, altering and inserting elements in a list:
set fp [open $file]
while {[gets $fp line] >= 0} {
# If the line starts with Aa_version...
if {[string match "Aa_version*" $line]} {
# Comment it out
puts "#$line"
# And read the new version and write it out
set newAa [gets stdin]
puts "Aa_version = $newAa"
# Copy the rest of the file to standard output and exit the loop
chan copy $fp stdout
break
} else {
puts $line
}
}
close $fp
But if you want to keep the current list based approach, lreplace is your friend:
set fp [open $file]
set lines [split [read $fp] \n]
close $fp
set idx [lsearch -glob $lines "Aa_version*"]
# If a match was found...
if {$idx >= 0} {
# Read the new verson
set newAa [gets stdin]
# Replace the version element with two new elements:
# Commented out previous version, and new version
set lines [lreplace $lines $idx $idx \
"#[lindex $lines $idx]" "Aa_version = $newAa"]
}
puts [join $lines \n]
I'm trying to iterate the value using for each in TCL
This how i give input
./a.sh value1,value2
in the a.sh script
set var [lindex $argv 0]
set values [split $var ","]
foreach {set i 0} {$values} {
puts "iterated once $i"
}
i want the iteration to happen twice since there are two values passed . but instead it is iterating only once
please help me on this
thanks in advance
The foreach command, in its simplest form, takes a variable name, a list value, and a script to run for each element of the list. What you were passing was… weird in several ways at once. Look, here's a correct way of doing it:
set values [split $var ","]
foreach item $values {
puts "iterated: $item"
}
If you want to count through, you should set up your own counter:
set values [split $var ","]
foreach item $values {
set i [incr counter]
puts "iterated #$i: $item"
}
That can be shortened to this:
foreach item [split $var ","] {
puts "iterated #[incr counter]: $item"
}
I would like to group my rows of inputs according to the first string of many comma-separated.
Basically, there will be 3 groups, which are "Motifs", "Chromatin_Structure" and "Protein_Binding"
The important output is the third one after 2 "|".
There might be some duplicates too like K562. The duplicates are not needed.
If the strings are not present, simply put a ". (dot)"
Input:
Motifs|PWM|Sox17|,Motifs|PWM|Sox8|,Chromatin_Structure|DNase-seq|K562|Znf4g7d3,Chromatin_Structure|DNase-seq|K562|,Chromatin_Structure|DNase-seq|TCF7L2|Znfe103c6,Protein_Binding|ChIP-seq|CTCF|HeLa-S3|,Protein_Binding|ChIP-seq|CTCF|HeLa-S3|
.
Motifs|PWM|TCF11|
Protein_Binding|ChIP-seq|MAFF|HepG2|
Desired output:
Sox17,Sox8 K562,TCF7L2 CTCF
. . .
TCF11 . .
. . MAFF
Codes that I tried.
sed 's/Motifs|PWM|//'
Appreciate your helps!
Perl one-liner (Using the term loosely):
$ perl -F, -lane '
my (%groups, #output);
for my $grp (#F) {
my #x = split /\|/, $grp;
$groups{$x[0]}{$x[2]} = 1;
}
for my $n (qw/Motifs Chromatin_Structure Protein_Binding/) {
if (exists $groups{$n}) {
push #output, join(",", sort keys %{$groups{$n}});
} else {
push #output, ".";
}
}
print join("\t", #output);' input.csv
Sox17,Sox8 K562,TCF7L2 CTCF
. . .
TCF11 . .
. . MAFF
And because I think it's woefully underappreciated as a scripting language, a tcl version:
#!/usr/bin/env tclsh
proc main {} {
while {[gets stdin line] >= 0} {
foreach grp [split $line ,] {
set x [split $grp |]
dict set groups [lindex $x 0] [lindex $x 2] 1
}
foreach n {Motifs Chromatin_Structure Protein_Binding} {
if {[dict exists $groups $n]} {
lappend output [join [dict keys [dict get $groups $n]] ,]
} else {
lappend output .
}
}
puts [join $output \t]
unset groups output
}
}
main
$ ./example.tcl < input.csv
Sox17,Sox8 K562,TCF7L2 CTCF
. . .
TCF11 . .
. . MAFF
I need a script to modify the same data in multiple servers. For now the for loop generate the command lines, but i'm experiencing some problems with expect and pssh.
The for loop:
<code>
for ((var1=1;var1<=14; var1++))
{
cda stm add "$var1/$var2/$var3" ss 1
for ((var2=1;var2<=8;var2++))
{
cda stm add "$var1/$var2/$var3" ss 1
for ((var3=1; var3<64; var3++))
{
cda stm add "$var1/$var2/$var3" ss 1
}
}
}
</code>
I'm using pssh instead ssh in expect script.
The full code:
<code>
#!/usr/bin/expect
set timeout 20
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
for ((var1=1;var1<=14; var1++))
{
cda stm add "$var1/$var2/$var3" ss 1
for ((var2=1;var2<=8;var2++))
{
cda stm add "$var1/$var2/$var3" ss 1
for ((var3=1; var3<64; var3++))
{
cda stm add "$var1/$var2/$var3" ss 1
}
}
}
spawn pssh "$user\#$ip"
expect "yes/no" {
send "yes\r"
expect "*?assword" { send "[lindex $argv 2]\r" }
} "*?assword" { send "[lindex $argv 2]\r" }
expect "SH"
interact
</code>
I'm getting the following error:
<code>
wrong # args: should be "for start test next command"
while executing
"for ((var1=1"
(file "./ssh" line 9)
</code>
for loops in expect and Tcl have to look like this:
for {set var1 1} {$var1<=14} {incr var1} {
commands...
}
In other words, the for command requires four arguments: the start code, the condition, the "next" code and the loop body. Note that newlines are command separators in Tcl, so the open brace for the loop body must be on the same line as the for command (or you must use a backslash-newline line continuation).
You used bash/ksh syntax instead.
Apart from the syntax error in for loop, there's a logical problem with the code snippet in the question:
var2 isn't available for printing in the outermost loop (for the first time).
var3 isn't available for printing in the 2nd loop (for the first time).
Here's probably what you need:
for {set var1 1} {$var1 <=14} {incr var1} {
puts "LEVEL_1: $var1"
# cda stm add "$var1" ss 1
for {set var2 1} {$var2 <=8} {incr var2} {
puts "LEVEL_2: $var1/$var2"
# cda stm add "$var1/$var2" ss 1
for {set var3 1} {$var3 <64} {incr var3} {
puts "LEVEL_3: $var1/$var2/$var3"
# cda stm add "$var1/$var2/$var3" ss 1
}
}
}
For example, if the output ( in expect_out(buffer) )is
blah
blh blah
asdjsudfsdf
how can I store the 2nd line to a variable? so far I have this:
foreach line [split $expect_out(buffer) "\n"] {
if [lindex $line 1] {
set variable $line
}
}
But this does not work, it says the variable variable is undefined. I tried adding a counter, but that didn't work either. There has to be an easier way!
yes there is an easier way:
set lines [split $expect_out(buffer) \n]
set variable [lindex $lines 1]
or in one line
set variable [lindex [split $expect_out(buffer) \n] 1]
Keep mindful you know what Tcl commands return: split returns a list. You then use lindex to find the 2nd element of the list.