So I thought I understood how the TCL upvar command worked, but for some reason I cannot seem to trace why this code generates an error -
#!/usr/bin/env tclsh
proc run_check {} {
set src dut.acore.B2.Vin
set conlist1 [list dut.acore.B2.Vin dut.acore.N1__Bidir_2__ddiscrete_5.Aout dut.acore.DCORE.CLK]
puts $conlist1
set conlist2 [_lremove $conlist1 $src true]
puts $conlist2
}
proc _lremove {listName val {byval false}} {
upvar 1 $listName list
puts $list
#if {$byval} {
# set list [lsearch -all -inline -not $list $val]
#} else {
# set list [lreplace $list $val $val]
#}
return $__list
}
######################################
##Run Command
######################################
puts [run_check]
On running this code, I get the following -
(base) ./remove.tcl
dut.acore.B2.Vin dut.acore.N1__Bidir_2__ddiscrete_5.Aout dut.acore.DCORE.CLK
can't read "list": no such variable
while executing
"puts $list"
(procedure "_lremove" line 4)
invoked from within
"_lremove $conlist1 $src true"
(procedure "run_check" line 6)
invoked from within
"run_check"
invoked from within
"puts [run_check]"
(file "./remove.tcl" line 35)
Any thoughts on this ??
When you call _lremove you need to pass it the variable name conlist1 but you are passing its value. Change
set conlist2 [_lremove $conlist1 $src true]
to
set conlist2 [_lremove conlist1 $src true]
Related
I am trying to compress a folder containing files and subfolders (with files) into a single zip. I'm limited to the core perl modules so I'm trying to work with IO::Compress::Zip. I want to remove the working directory file path but seem to end up with a blank first folder before my zipped folder, like there is a trailing "/" I haven't been able to get rid of.
use Cwd;
use warnings;
use strict;
use File::Find;
use IO::Compress::Zip qw(:all);
my $cwd = getcwd();
$cwd =~ s/[\\]/\//g;
print $cwd, "\n";
my $zipdir = $cwd . "\\source_folder";
my $zip = "source_folder.zip";
my #files = ();
sub process_file {
next if (($_ eq '.') || ($_ eq '..'));
if (-d && $_ eq 'fp'){
$File::Find::prune = 1;
return;
}
push #files, $File::Find::name if -f;
}
find(\&process_file, $cwd . "\\source_folder");
zip \#files => "$zip", FilterName => sub{ s|\Q$cwd|| } or die "zip failed: $ZipError\n";
I have also attempted using the option "CanonicalName => 1, " which appears to leave the filepath except the drive letter (C:).
Substitution with
s[^$dir/][]
did nothing and
s<.*[/\\]><>
left me with no folder structure at all.
What am I missing?
UPDATE
The Red level is unexpected and is what is not required, win explorer is not able to see beyond this level.
There are two issues with your script.
First, you are mixing Windows and Linux/Unix paths in the script. Let me illustrate
I've created a subdirectory called source_folder to match your script
$ dir source_folder
Volume in drive C has no label.
Volume Serial Number is 7CF0-B66E
Directory of C:\Scratch\source_folder
26/11/2018 19:48 <DIR> .
26/11/2018 19:48 <DIR> ..
26/11/2018 17:27 840 try.pl
01/06/2018 13:02 6,653 url
2 File(s) 7,493 bytes
When I run your script unmodified I get an apparently empty zip file when I view it in Windows explorer. But, if I use a command-line unzip, I see that source_folder.zip isn't empty, but it has non-standard filenames that are part Windows and part Linux/Unix.
$ unzip -l source_folder.zip
Archive: source_folder.zip
Length Date Time Name
--------- ---------- ----- ----
840 2018-11-26 17:27 \source_folder/try.pl
6651 2018-06-01 13:02 \source_folder/url
--------- -------
7491 2 files
The mix-and-match of windows & Unix paths is created in this line of your script
find(\&process_file, $cwd . "\\source_folder");
You are concatenating a Unix-style path in $cwd with a windows part "\source_folder".
Change the line to use a forward slash, rather than a backslash to get a consistent Unix-style path.
find(\&process_file, $cwd . "/source_folder");
The second problem is this line
zip \#files => "$zip",
FilterName => sub{ s|\Q$cwd|| },
BinmodeIn =>1
or die "zip failed: $ZipError\n";
The substitute, s|\Q$cwd||, needs an extra "/", like this s|\Q$cwd/|| to make sure that the path added to the zip archive is a relative path. So the line becomes
zip \#files => "$zip", FilterName => sub{ s|\Q$cwd/|| } or die "zip failed: $ZipError\n";
Once those two changes are made I can view the zip file in Explorer and get unix-style relative paths in when I use the command-line unzip
$ unzip -l source_folder.zip
Archive: source_folder.zip
Length Date Time Name
--------- ---------- ----- ----
840 2018-11-26 17:27 source_folder/try.pl
6651 2018-06-01 13:02 source_folder/url
--------- -------
7491 2 files
This works for me:
use Cwd;
use warnings;
use strict;
use File::Find;
use IO::Compress::Zip qw(:all);
use Data::Dumper;
my $cwd = getcwd();
$cwd =~ s/[\\]/\//g;
print $cwd, "\n";
my $zipdir = $cwd . "/source_folder";
my $zip = "source_folder.zip";
my #files = ();
sub process_file {
next if (($_ eq '.') || ($_ eq '..'));
if (-d && $_ eq 'fp') {
$File::Find::prune = 1;
return;
}
push #files, $File::Find::name if -f;
}
find(\&process_file, $cwd . "/source_folder");
print Dumper \#files;
zip \#files => "$zip", FilterName => sub{ s|\Q$cwd/|| } or die "zip failed: $ZipError\n";
I changed the path seperator to '/' in your call to find() and also stripped it in the FilterName sub.
console:
C:\Users\chris\Desktop\devel\experimente>mkdir source_folder
C:\Users\chris\Desktop\devel\experimente>echo 1 > source_folder/test1.txt
C:\Users\chris\Desktop\devel\experimente>echo 1 > source_folder/test2.txt
C:\Users\chris\Desktop\devel\experimente>perl perlzip.pl
C:/Users/chris/Desktop/devel/experimente
Exiting subroutine via next at perlzip.pl line 19.
$VAR1 = [
'C:/Users/chris/Desktop/devel/experimente/source_folder/test1.txt',
'C:/Users/chris/Desktop/devel/experimente/source_folder/test2.txt'
];
C:\Users\chris\Desktop\devel\experimente>tar -tf source_folder.zip
source_folder/test1.txt
source_folder/test2.txt
I wrote a little program which, in first case, slices a list of files and directory names so I get a list of files and a list of directories.
#!/usr/bin/expect -f
set mdproot "****"
set mySource "****"
set myDest "****"
set myDirs {}
set myFiles {}
set i 1
set y 0
lappend myDirs [lindex $argv 0]
foreach variable $argv {
lappend myDirs [lindex $argv $i+1]
lappend myFiles [lindex $argv $y+1]
incr y
incr y
incr i
incr i
}
set DIRECTORIES [lsearch -all -inline -not -exact $myDirs {}]
set FILES [lsearch -all -inline -not -exact $myFiles {}]
#puts " "
#puts $DIRECTORIES
#puts " "
#puts $FILES
foreach file $FILES dir $DIRECTORIES {
puts " Fichier : $file et repertoire : $dir"
spawn scp -p "$mySource/$file" "$myDest/$dir"
expect -re "(.*)assword: " {sleep 1; send -- "$mdproot\r" }
expect eof { return}
}
There are my lists:
$argv :
2017-11-30
2017-11-30_15-10-44_P_8294418.33_Q1
2017-11-30
2017-11-30_15-10-44_R_8294418.33_Q1
2018-03-07
2018-03-07_09-30-57_R_HOURS_Q1
2018-04-13
2018-04-13_13-23-25_R_HOURS_Q1
2018-05-02
2018-05-02_11-19-37_R_HOURS_Q1
2018-03-07
2018-3-7_9-30-57_P_HOURS_Q1
2018-04-13
2018-4-13_13-23-25_P_HOURS_Q1
2018-05-02
2018-5-2_11-19-37_P_HOURS_Q1
$DIRECTORIES :
2017-11-30
2017-11-30
2018-03-07
2018-04-13
2018-05-02
2018-03-07
2018-04-13
2018-05-02
$FILES :
2017-11-30_15-10-44_P_8294418.33_Q1
2017-11-30_15-10-44_R_8294418.33_Q1
2018-03-07_09-30-57_R_HOURS_Q1
2018-04-13_13-23-25_R_HOURS_Q1
2018-05-02_11-19-37_R_HOURS_Q1
2018-3-7_9-30-57_P_HOURS_Q1
2018-4-13_13-23-25_P_HOURS_Q1
2018-5-2_11-19-37_P_HOURS_Q1
Actually I have 2 problems (3 in the case we count how trash this code is).
First, when I run my program, I have my % indicator for each file I am copying and except the last one, they all stop before they get to 100%.
Then, I can see that the scp command isn't done on all the files, the program stops pretty much every time at the 4th file.
root#raspberrypi:~# ./recupFileName.sh
spawn scp -p /root/muonic_data/2017-11-30_15-10-44_P_8294418.33_Q1 marpic#192.168.110.90:/home/marpic/muonic_data/Data_Q1/2017-11-30
marpic#192.168.110.90's password:
2017-11-30_15-10-44_P_8294418.33_Q1 15% 68MB 6.9MB/s 00:53
spawn scp -p /root/muonic_data/2017-11-30_15-10-44_R_8294418.33_Q1 marpic#192.168.110.90:/home/marpic/muonic_data/Data_Q1/2017-11-30
marpic#192.168.110.90's password:
2017-11-30_15-10-44_R_8294418.33_Q1 41% 69MB 8.5MB/s 00:11
spawn scp -p /root/muonic_data/2018-03-07_09-30-57_R_HOURS_Q1 marpic#192.168.110.90:/home/marpic/muonic_data/Data_Q1/2018-03-07
marpic#192.168.110.90's password:
2018-03-07_09-30-57_R_HOURS_Q1 82% 51MB 7.2MB/s 00:01
spawn scp -p /root/muonic_data/2018-04-13_13-23-25_R_HOURS_Q1 marpic#192.168.110.90:/home/marpic/muonic_data/Data_Q1/2018-04-13
marpic#192.168.110.90's password:
2018-04-13_13-23-25_R_HOURS_Q1 100% 6940KB 6.8MB/s 00:01
As you can see, there should be 8 files copied with 100% accuracy, but there are no error messages so I don't know where to start my research.
EDIT :
I added the "set timeout -1" in my script, but now the script is copying only my first file with 100% accuracy, then stops. Any answers ?
root#raspberrypi:~# ./recupFileName.sh
Fichier : 2017-11-30_15-10-44_P_8294418.33_Q1 et repertoire : 2017-11-30
spawn scp -p /root/muonic_data/2017-11-30_15-10-44_P_8294418.33_Q1 marpic#192.168.110.90:/home/marpic/muonic_data/Data_Q1/2017-11-30
marpic#192.168.110.90's password:
2017-11-30_15-10-44_P_8294418.33_Q1 100% 437MB 7.5MB/s 00:58
root#raspberrypi:~#
The problem should be in expect eof. By default the timeout is 10 seconds so expect eof would return after 10 seconds though the scp is still running.
You can use a larger timeout.
Option #1:
# set the default `timeout'
set timeout 3600 ; # or -1 for no timeout
Option #2:
expect -timeout 3600 eof
Note that your
expect eof { return }
would exit the whole script so the foreach loop runs for only once, you need just
expect eof
I am trying to remove the old files in a dir if the count is more than 3 over SSH
Kindly suggest how to resolve the issue.
Please refer the code snippet
#!/usr/bin/perl
use strict;
use warnings;
my $HOME="/opt/app/latest";
my $LIBS="${HOME}/libs";
my $LIBS_BACKUP_DIR="${HOME}/libs_backups";
my $a;
my $b;
my $c;
my $d;
my $command =qq(sudo /bin/su - jenkins -c "ssh username\#server 'my $a=ls ${LIBS_BACKUP_DIR} | wc -l;my $b=`$a`;if ($b > 3); { print " Found More than 3 back up files , removing older files..";my $c=ls -tr ${LIBS_BACKUP_DIR} | head -1;my $d=`$c`;print "Old file name $d";}else { print "No of back up files are less then 3 .";} '");
print "$command\n";
system($command);
output:
sudo /bin/su - jenkins -c "ssh username#server 'my ; =ls /opt/app/latest/libs_backups | wc -l;my ; =``;if ( > 3); { print " Found More than 3 back up files , removing older files..";my ; =ls -tr /opt/app/latest/libs_backups | head -1;my ; =``;print "Old file name ";}else { print "No of back up files are less then 3 .";} '"
Found: -c: line 0: unexpected EOF while looking for matching `''
Found: -c: line 1: syntax error: unexpected end of file
If you have three levels of escaping, you're bound to get it wrong if you do it manually. Use String::ShellQuote's shell_quote instead.
Furthermore, avoid generating code. You're bound to get it wrong! Pass the necessary information using arguments, the environment or some other channel of communication instead.
There were numerous errors in the interior Perl script on top of the fact that you tried to execute a Perl script without actually invoking perl!
#!/usr/bin/perl
use strict;
use warnings;
use String::ShellQuote qw( shell_quote );
my $HOME = "/opt/app/latest";
my $LIBS = "$HOME/libs";
my $LIBS_BACKUP_DIR = "$HOME/libs_backups";
my $perl_script = <<'__EOI__';
use strict;
use warnings;
use String::ShellQuote qw( shell_quote );
my ($LIBS_BACKUP_DIR) = #ARGV;
my $cmd = shell_quote("ls", "-tr", "--", $LIBS_BACKUP_DIR);
chomp( my #files = `$cmd` );
if (#files > 3) {
print "Found more than 3 back up files. Removing older files...\n";
print "$_\n" for #files;
} else {
print "Found three or fewer backup files.\n";
}
__EOI__
my $remote_cmd = shell_quote("perl", "-e", $perl_script, "--", $LIBS_BACKUP_DIR);
my $ssh_cmd = shell_quote("ssh", 'username#server', "--", $remote_cmd);
my $local_cmd = shell_quote("sudo", "su", "-c", $ssh_ccmd);
system($local_cmd);
I created a new file and handling the dir check and deletion logic , scp file to remote server and executing in remote server , after completion removing the file.
#!/usr/bin/perl
use strict;
use warnings;
use File::Basename;
use File::Path;
use FindBin;
use File::Copy;
my $HOME="/opt/app/test/latest";
my $LIBS_BACKUP_DIR="${HOME}/libs_backups";
my $a="ls ${LIBS_BACKUP_DIR} | wc -l";
my $b=`$a`;
my $c="ls -tr ${LIBS_BACKUP_DIR} | head -1";
my $d=`$c`;
chomp($d);
print " count : $b\n";
if ($b > 3)
{
print " Found More than 3 back up files , removing older files..\n";
print "Old file name $d\n";
my $filepath="${LIBS_BACKUP_DIR}/$d";
rmtree $filepath;
}
else
{
print "No of back up files are less then 3 .\n";
}
The following is memory directory
Every directory need to enter it and modify the following three files :
hcell.list
SmicDR1T_cal40_log_ll_sali_p1mx_1tm_121825.drc
SmicSP1R_cal40_LL_sali_p1mtx_11182533.lvs
The above three file has content "TPSRAM_256X120" and I want to replace it with its own directory path name.
How should I do it?
SPRF_256X34 SPSRAM_128X30 SPSRAM_192X16 SPSRAM_240X48 SPSRAM_2944X72 SPSRAM_480X48 SPSRAM_512X8 SPSRAM_72X8 SPSRAM_960X60_WEM SROM_8192X8
command.log SPSRAM_1024X14 SPSRAM_128X64 SPSRAM_2048X17 SPSRAM_240X56 SPSRAM_304X128 SPSRAM_480X64 SPSRAM_5376X17 SPSRAM_8192X12 SPSRAM_960X8 SROM_960X26
filenames.log SPSRAM_1024X16 SPSRAM_152X8 SPSRAM_2048X8 SPSRAM_240X72 SPSRAM_32X64 SPSRAM_480X66 SPSRAM_5376X80 SPSRAM_8192X20 SPSRAM_960X80 TPSRAM_1920X9
mem_new.list SPSRAM_11520X28 SPSRAM_16384X34 SPSRAM_240X10 SPSRAM_240X8 SPSRAM_384X19 SPSRAM_480X96 SPSRAM_544X20 SPSRAM_8192X34 SPSRAM_960X96 TPSRAM_256X120
SPRF_240X20 SPSRAM_120X72 SPSRAM_16384X38 SPSRAM_240X152 SPSRAM_240X88 SPSRAM_4352X8 SPSRAM_496X44 SPSRAM_544X21 SPSRAM_8192X52 SROM_1024X16
SPRF_240X32 SPSRAM_120X80 SPSRAM_16384X40 SPSRAM_240X17 SPSRAM_240X9 SPSRAM_4480X8 SPSRAM_496X82 SPSRAM_5760X32 SPSRAM_8192X72 SROM_1440X14
SPRF_240X82 SPSRAM_120X88 SPSRAM_1920X56 SPSRAM_240X18 SPSRAM_240X96 SPSRAM_480X128 SPSRAM_496X86 SPSRAM_64X22 SPSRAM_8192X8 SROM_1888X26
SPRF_240X86 SPSRAM_1216X40 SPSRAM_1920X60 SPSRAM_240X22 SPSRAM_256X8 SPSRAM_480X144 SPSRAM_512X10 SPSRAM_64X24 SPSRAM_8192X9 SROM_4096X8
SPRF_240X86_WEM SPSRAM_1280X32 SPSRAM_1920X8 SPSRAM_240X34 SPSRAM_2688X8 SPSRAM_480X16 SPSRAM_512X17 SPSRAM_64X48 SPSRAM_960X24 SROM_512X16
SPRF_240X90 SPSRAM_128X16 SPSRAM_1920X9 SPSRAM_240X40 SPSRAM_2880X8 SPSRAM_480X32 SPSRAM_512X27 SPSRAM_720X12 SPSRAM_960X60 SROM_736X14
get all the directories:
set all_dir [glob -type d -nocomplain -dir $dirname *]
in foreach loop open your files: hcell.list, SmicDR1T_cal40_log_ll_sali_p1mx_1tm_121825.drc, SmicSP1R_cal40_LL_sali_p1mtx_11182533.lvs
set r [open [file join $dir hcell.list] r]
Now replace your content using regsub:
regsub "TPSRAM_256X120" $line [pwd]
To replace a value in a file with another, while writing that file back to the same file, you want code like this:
proc replaceValue {filename changeThis toThis {backupExtension ""}} {
set mapping [list $changeThis $toThis]
# Read and transform the file
set f [open $filename]
set content [string map $mapping [read $f]]
close $f
# Make backup if requested
if {$backupExtension ne ""} {
file rename $filename $filename$backupExtension
}
# Write the new contents back
set f [open $filename "w"]
puts -nonewline $f $content
close $f
}
This is only suitable for files up to a couple of hundred megabytes (assuming you've got plenty of memory) but it is easy.
Then, to apply the alteration to everything in a directory, use glob to list the directory contents, foreach to go over the list, and that procedure to apply the transformation.
# Glob patterns in quotes just because of Markdown formatting bug
foreach filename [glob -directory /the/base/directory "*/*.list" "*/*.drc" "*/*.lvs"] {
# Make backups into .bak
replaceValue $filename TPSRAM_256X120 $filename ".bak"
}
I'm pretty new to scripting in general. I'm writing an expect script that ssh'es into a Cisco switch, and runs the "show cdp neighbors" command to get a list of all the devices connected to the switch. I then save the output into a variable and exit the ssh session.
I have the username and password being set in the included file.
#!/usr/bin/expect -f
#exp_internal 1
source accountfile
set timeout 10
spawn $env(SHELL)
expect "#"
send "ssh $USERNAME#<hostname>\r"
expect {
"continue connecting" {
send_user "Adding host to ssh known hosts list...\n"
send "yes\n"
exp_continue
}
"Do you want to change the host key on disk" {
send_user "Changing host key on disk...\n"
send "yes\n"
exp_continue
}
"assword:" {
send "$PASSWORD\r"
}
}
expect "#"
send "term len 0\r"
expect "#"
send "show cdp neighbors\r"
expect "#"
set result $expect_out(buffer)
send "exit\r"
expect "#"
So then I want to take $result and look for lines that contain ' R ', and save those lines to a file (R with spaces on either side indicates a router, which is what I'm interested in)
The problem is that if the name of a connected device is long, it puts the name of the device on one line, and then the rest of the data about the device on the next line. So if I match the ' R ' string, I won't get the name of the device, since the name is on the previous line.
Device ID Local Intrfce Holdtme Capability Platform Port ID
...
<device_name_really_long>
Gig 2/0/52 171 R S I WS-C6509 Gig 3/14
<device_name2> Gig 2/0/1 131 H P M IP Phone Port 1
...
Any ideas? there's probably a regex that would do it, but I don't know squat about regex.
SOLVED: thanks to Glenn Jackman
I ended up having to add an expect condition to check if I had a full buffer, so my final code looks like this:
#!/usr/bin/expect
#exp_internal 1
match_max 10000
set expect_out(buffer) {}
set timeout 30
source accountfile
spawn $env(SHELL)
expect "#"
send "ssh $USERNAME#ol2110-3750stack.sw.network.local\r"
expect {
"continue connecting" {
send_user "Adding host to ssh known hosts list...\n"
send "yes\n"
exp_continue
}
"Do you want to change the host key on disk" {
send_user "Changing host key on disk...\n"
send "yes\n"
exp_continue
}
"assword:" {
send "$PASSWORD\r"
}
}
expect "#"
send "term len 0\r"
expect "#"
send "show cdp neighbors\r"
set result ""
expect {
{full_buffer} {
puts "====== FULL BUFFER ======"
append result $expect_out(buffer)
exp_continue
}
"#" {
append result $expect_out(buffer)
}
}
send "exit\r"
expect "#"
set devices [list]
set current_device ""
set lines [split $result "\n"]
foreach line $lines {
set line [string trim $line]
if {[llength $line] == 1} {
set current_device $line
continue
}
set line "$current_device$line\n"
if {[string match {* R *} $line]} {
lappend devices $line
}
set current_device ""
}
puts $devices
set devices [list]
set current_device ""
foreach line [split $result \n] {
if {[llength [split [string trim $line]]] == 1} {
set current_device $line
continue
}
set line "$current_device$line"
if {[string match {* R *} $line]} {
lappend devices $line
}
set current_device ""
}
# devices list should now contain "joined" routers.