I am farily new to tcl. I am trying to write a tcl script that will perform a few thins on certain files in a tree structure, but on all files.
I have in my tree a number of files ending with .xci.
Now I want to filter our all .gbn files except the ones in a part of my tree (i.e. /src/ps/<a number of directories>/<a number of files>.xci) that contains the path part "/ps/"
I have done this:
foreach xci_file [get_files *.xci] {
#if (ps_is_found_in_path_of_${xci_file}) {
generate_target simulation [get_files $xci_file]
}
}
The foreach search through all files in my project and returns the filename (including the full path). How do I write the if statement to avoid target generation of the files whose paths include "/ps/"?
Is there a nice soul out there who could share some light on this?
You want:
if {"ps" in [file split $xci_file]} {
The quotes are required here: expr (which handles if's first argument)
needs literal strings quoted.
Related
I'm writing a reusable function, so I need the argument to be as flexible as possible.
Consider a simple example:
function testf(){
print ./*.$1
}
This works. For example, with testf mp3 it lists all the files ending with .mp3 in an array, making possible the use of for loops. But this way it only allows me to work with the extension name.
Therefore, I tried:
function testf(){
print ./$1
}
However, it doesn't work. Using testf *.mp3, unlike using print *.mp3 in the terminal, it will only pass the first matching string instead of the whole array.
Any suggestion?
ists all the files ending with .mp3 in an array ... there is no array involved in your question.
But to your problem: First, you want to pass to your function a wildcard pattern, but this is not what you are actually doing. testf *.mp3 expands the pattern before the function is invoked (this process is called filename generation), and your testf gets just a list of files as parameters. You can pass a pattern, but you have to ask the shell not to expand it:
testf '*.mp3'
In this case, your $1 indeed will contain the string *.mp3. However, your print ./$1 will still not work. The reason is that filename generation occurs before parameter expansion (which is the process where $1 is replaced by the string it contains). Again, you have to ask the shell to do it the other way round:
print ./${~1}
The shell performs several types of expansions before launching the command. When you enter
testf *.mp3
the shell will expand the glob first, and pass each filename as a separate argument to the function
Your function could look like this:
function testf(){
printf './%s\n' "$#"
}
So I am trying to move a bunch of files with similar extensions from /home/ to /root/
Code I tried is
file copy /home/*.abc.xyz /root/
Also tried
set infile [glob -nocomplain /home/*.abc.xyz ]
if { [llength $infile] > 0 } {
file copy $infile /root/
}
No success.
Your two attempts fail for different reasons:
There is no wildcard expansion in arguments to file copy, or any Tcl command, for that matter: file copy /home/*.abc.xyz /root/. This will look for a single source with a literal * in its filename.
glob -nocomplain /home/*.abc.xyz is ok to collect the sources, but glob returns a list of sources. file copy requires each source to passed as a separate argument, not a single one. To expand a single collection value of source files into a multiple separate arguments, use the Tcl expansion operator {*}
Therefore:
set infiles [glob -nocomplain *.tcl]
if {[llength $infiles]} {
file copy {*}$infiles /tmp/tgt/
}
For a 1-line answer:
file copy {*}[glob /home/*.abc.xyz] /root/.
The file copy (and file rename) commands have two forms (hence the reference to the manual page in the comment). The first form copies a single file to a new target. The second form copies all the file name arguments to a new directory and this form of the command insists that the directory name be the last argument and you may have an arbitrary number of source file names preceding. Also, file copy does not do glob expansion on its arguments, so as you rightly surmised, you also need to use the glob command to obtain a list of the files to copy. The problem is that the glob command returns a list of file names and you passed that list as a single argument, i.e.
file copy $infile /root/
passes the list as a single argument and so the file copy command thinks it is dealing with the first form and attempts to find a file whose name matches that of the entire list. This file probably doesn't exist. Placing the error message in your question would have helped us to know for sure.
So what you want to do is take the list of files contained in the infile variable and expand it into separate argument words. Since this is a common situation, Tcl has some syntax to help (assuming you are not using some ancient version of Tcl). Try using the command:
file copy {*}$infile /root/
in place of your first attempt and see if that helps the situation.
Is there any way to pass each and every file (with an extention .tcl) of a selected directory one by one in a particular variable in tcl ?
One way to do what I think you want to do is
foreach file [glob -directory $dir *.tcl] {
# do something with the filename $file
}
The glob command returns a list of file names that match the pattern in the last argument (in this case *.tcl, which means "any name that ends in .tcl"). The -directory option specifies which directory to look for files in.
The command will raise an error if there are no files that match the pattern. To avoid this and simply get an empty list, use the option -nocomplain.
The foreach command takes a variable specification, a list (in this case the list of file names), and a script. The script is evaluated once for every item (or batch of items in some cases) with the variable set to the item.
Documentation:
foreach,
glob,
File name conventions supported by Tcl commands
Syntax of Tcl string matching:
* matches a sequence of zero or more characters
? matches a single character
[chars] matches a single character in the set given by chars (^ does not negate; a range can be given as a-z)
\x matches the character x, even if that character is special (one of *?[]\)
From Tcl online manual I see that Tcl's file copy command can take multiple source files as argument:
file copy ?-force? ?--? source ?source ...? targetDir
However, I have the following code:
set flist [list a.txt b.txt]
file copy $flist [file join D:\\ test dest]
And get this error message:
error copying "a.txt b.txt": no such file or directory
How do I properly pass a file list as source argument to the file copy command?
The right way to do this is to use expansion:
file copy {*}$flist {D:\test\dest}
The {*} substitutes the words of the list given by what follows it as separate words; it's precisely right here.
I've also written the destination directory as a brace-quoted literal.
Still on Tcl 8.4 or before? Upgrade! Or use this:
eval file copy $flist [list {D:\test\dest}]
It's quite a lot harder to use eval right than {*}, so really upgrade.
Or even do:
foreach f $flist {
file copy $f {D:\test\dest}
}
Given that IO operations will dominate the performance, you shouldn't notice any speed difference for doing it this way.
The problem is the list is passed as a whole to the command instead of individual elements. Use {*} operator to break the list down to its individual elements.
The short answer is don't use a list the way you have done.
This works in your example:
set flist "a.txt b.txt"
file copy $flist [file join D:\\ test dest]
More correct would be to use the list expansion {*} syntax.
I am trying to convert some tcl script into a C++ program. I don't have much experience with tcl and am hoping someone could explain what some of the following things are actually doing in the tcl script:
1) set rtn [true_test_sfm $run_dir]
2) cd [glob $run_dir]
3) set pwd [pwd]
Is the first one just checking if true_test_sfm directory exists in run_dir?
Also, I am programming on a windows machine. Would the system function be the equivalent to exec statements in tcl? And if so how would I print the result of the system function call to stdout?
In Tcl, square brackets indicate "evaluate the code between the square brackets". The result of that evaluation is substituted for the entire square-bracketed expression. So, the first line invokes the function true_test_sfm with a single argument $run_dir; the result of that function call is then assigned to the variable rtn. Unfortunately, true_test_sfm is not a built-in Tcl function, which means it's user-defined, which means there's no way we can tell you what the effect of that function call will be based on the information you've provided here.
glob is a built-in Tcl function which takes a file pattern as an argument and then lists files that match that pattern. For example, if a directory contains files "foo", "bar" and "baz", glob b* would return a list of two files, "bar" and "baz". Therefore the second line is looking for any files that match the pattern given by $run_dir, then using the cd command (another Tcl built-in) to change to the directory found by glob. Probably $run_dir is not actually a file pattern, but an explicit file name (ie, no globbing characters like * or ? in the string), otherwise this code may break unexpectedly. On Windows, some combination of FindFirstFile/FindNextFile in C++ could be used as a substitute for glob in Tcl, and SetCurrentDirectory could substitute for cd.
pwd is another built-in Tcl function which returns the process current working directory as an absolute path. So the last line is querying the current working directory and saving the result in a variable named pwd. Here you could use GetCurrentDirectory as a substitute for pwd.