TCL: How to give different output in set out [command] and command? - tcl

I want to give different output in different invocation of "mycommand". Detailed output (name and age) if "mycommand" is invoked directly or smaller output (only first col) if used in set command. Is this possible in TCL ?
> set output [mycommand]
> puts "$output"
name1
name2
name3
> mycommand
name1 age29
name2 age30
name2 age31
>

There is nothing built-in to allow that, and doing so would probably cause more problems that it would solve. If you need it to produce different results in different contexts, the best thing is to implement an option to control the output (eg: [mycommand -verbose]`)

You could check to see if the global tcl_interactive variable is set and true, and use info level to see (OK, to guess) if you're being called directly, but it would be a total hack.
if {$::tcl_interactive && [info level]==1} {
# do a verbose thing
} else {
# do a not-so-verbose thing
}
But I advise you not do this. Let the caller ask for one or the other mode explicitly, and pick a sensible default. (Which one? When are you laziest?)

Related

Print from jq using a wild card (or coalesce to first non null)

I have the following command:
kubectl get pod -A -o=json | jq -r '.items[]|select(any( .status.containerStatuses[]; .state.waiting or .state.terminated))|[.metadata.namespace, .metadata.name]|#csv'
This command works great. It outputs both the namespace and name of my failing pods.
But now I want to add one more column to the results. The column I want is located in one (and only one) of two places:
.status.containerStatuses[].state.waiting.reason
.status.containerStatuses[].state.terminated.reason
I first tried adding .status.containerStatuses[].state.*.reason to the results fields array. But that gave me an unexpected '*' compile error.
I then got to thinking about how I would do this with SQL or another programming language. They frequently have a function that will return the first non-null value of its parameters. (This is usually called coalesce). However I could not find any such command for jq.
How can I return the reason as a result of my query?
jq has a counterpart to "coalesce" in the form of //.
For example, null // 0 evaluates to 0, and chances are that it will suffice in your case, perhaps:
.status.containerStatuses[].state | (.waiting // .terminated) | .reason
or
.status.containerStatuses[].state | (.waiting.reason // .terminated.reason )
or similar.
However, // should only be used with some understanding of what it does, as explained in detail on the jq FAQ at https://github.com/stedolan/jq/wiki/FAQ#or-versus-
If // is inapplicable for some reason, then the obvious alternative would be an if ... then ... else ... end statement, which is quite like C's _ ? _ : _ in that it can be used to produce a value, e.g. something along the lines of:
.status.containerStatuses[].state
| if has("waiting") then .waiting.reason
else .terminated.reason
end
However, if containerStatuses is an array, then some care may be required.
In case you want to go with coalesce:
# Stream-oriented version
def coalesce(s):
first(s | select(. != null)) // null;
or if you prefer to work with arrays:
# Input: an array
# Output: the first non-null element if any, else null
def coalesce: coalesce(.[]);
Using the stream-oriented version, you could write something along the lines you had in mind with the wildcard, e.g.
coalesce(.status.containerStatuses[].state[].reason?)

in Tcl, when should use use set vs unset to prepare to use a variable

In my scripts, when using a variable, I generally empty the contents of a variable to ensure that the list appends are clean. Something like the following
set var1 [list]
foreach var2 {a b c} {
lappend var1 $var2
}
But it seems like unsetting the variable first would accomplish the same thing. Something like this
unset -nocomplain var1
foreach var2 {a b c} {
lappend var1 $var2
}
Is there any advantage for using one vs the other?
It doesn't make any difference in this case. If I was to write such a loop in my own code, I would be more likely to use set var {} since that is the empty list literal (as well as being the empty string, the empty dictionary, the empty script, etc.) but there isn't any execution time difference to speak of. It just reflects how I think about scripts.
Of course, if you are doing something where it does matter, use the right one for that case.
As Donal wrote, set var {}. Internally, the same value will be assigned regardless of whether you assign {}, [list] etc. Yes, it will shimmer, and no, it won't be a problem.
Regarding set vs unset: while you can use them as you see fit, they mostly serve different patterns. In the assign-empty-value pattern, you want a variable ready for writing or reading, with a predefined, empty value. In the remove-from-scope pattern you want the name to be unused (it won't be unusable: you can still assign to / create it). Unless you're after something like the second pattern, you probably won't have much serious use for unset.

TCL Checking Environment Variables

So I have been trying to find an answer for this for a bit and could not find the answer on the internet. I need to check to see if an environment variable exists. I thought I had the right code but it keeps returning false.
if { [info exists ::env(USER)] } {
RAT::LogMsg INFO "Found USER"
} else {
RAT::LogMsg INFO "Nope!"
}
Any ideas?
You might want to check what environment variables are actually set; I don't think that USER is one of the guaranteed ones.
RAT::LogMsg INFO "Got these env-vars: [lsort [array names ::env]]"
If puts stdout works in your environment, you can try doing:
parray ::env
(The parray command is a procedure that pretty-prints an array.)
To get the current username reliably, check out the tcl_platform array's user element. That array is generated internally by Tcl (well, with probes to relevant basic OS APIs) instead of by looking at environment variables, and that particular element is always present back at least as far as Tcl 8.4.
RAT::LogMsg INFO "Username is $::tcl_platform(user)"
I've just noticed that the documentation is wrong: it says that the user element comes from USER and/or LOGNAME environment variables. It doesn't, and doesn't in at least 8.5 and 8.6. (And it's definitely my mistake. I forgot to update the code when I fixed this. Ooops!)
You have the right code, test in tclsh:
% if {[info exists ::env(USER)]} {puts "found $::env(USER)"}
found strobel
%
The problem must be in your environment.

Trying to redirect output of a command to a variable

>> set signal_name [get_fanout abc_signal]
{xyz_blah_blah}
>> echo $signal_name
#142
>> set signal_name [get_fanout abc_signal]
{xyz_blah_blah}
>> echo $signal_name
#144
>>
I tried other stuff like catch etc, and every where, it returns #number. My goal is to be able to print the actual value instead of the number - xyz_blah_blah.
I am new to tcl. Want to understand, if this is an array or a pointer to an array or something like that. When I try the exact same thing with a different command, which returns just a value, then it works. This is a new command which returns value in parenthesis.
Please help. Thanks.
Every Tcl command produces a result value, which you capture and use by putting the call of the command in [square brackets] and putting the whole lot as part of an argument to another command. Thus, in:
set signal_name [get_fanout abc_signal]
the result of the call to get_fanout is used as the second argument to set. I suggest that you might also like to try doing this:
puts "-->[get_fanout abc_signal]<--"
It's just the same, except this time we're concatenating it with some other small string bits and printing the whole lot out. (In case you're wondering, the result of puts itself is always the empty string if there isn't an error, and set returns the contents of the variable.)
If that is still printing the wrong value (as well as the right one beforehand, without arrow marks around it) the real issue may well be that get_fanout is not doing what you expect. While it is possible to capture the standard output of a command, doing so is a considerably more advanced technique; it is probably better to consider whether there is an alternate mechanism to achieve what you want. (The get_fanout command is not a standard part of the Tcl language library or any very common add-on library like Tk or the Tcllib collection, so we can only guess at its behavior.)

Perl - getlogin, getpwuid, and $<

Wanted to understand the example line of code given # perldoc.perl.org for getlogin
$login = getlogin || getpwuid($<) || "Kilroy";
It seems like it tries to get the user name from getlogin or getpwuid, but if either fails, use Kilroy instead. I might be wrong, so please correct me. Also, I've been using getlogin() in previous scripts - is there any difference between getlogin() and getlogin?
What is this code safeguarding against? Also, what purpose does $< serve? I'm not exactly sure what to search for when looking up what $< is and what it does.
EDIT
found this in the special variables section - still don't know why it is needed or what is does in the example above
$<
The real uid of this process.
(Mnemonic: it's the uid you came from,
if you're running setuid.) You can
change both the real uid and the
effective uid at the same time by
using POSIX::setuid(). Since changes
to $< require a system call, check $!
after a change attempt to detect any
possible errors.
EDIT x2
Is this line comparable to the above example? (it is currently what I use to avoid any potential problems with "cron" executing a script - i've never run into this problem, but i am trying to avoid any theoretical problem)
my $username = getlogin(); if(!($username)){$username = 'jsmith';}
You're exactly right. If getlogin returns false it will test getpwuid($<) if that returns false it will set $login to "Kilroy"
$< is the real uid of the process. Even if you're running in a setuid environment it will return the original uid the process was started from.
Edit to match your edit :)
getpwuid returns the user's name by the UID (in scalar context, which would be the case here). You would want $< as an argumnent in case the program switched UID at some point ($< is the original one it was started with)
The only thing it's guarding against is the fact that on some systems, in some circumstances, getlogin can fail to return anything useful. In particular, getlogin only does anything useful when the process it's in has a "controlling terminal", which non-interactive processes may not. See, e.g., http://www.perlmonks.org/?node_id=663562.
I think the fallback of "Kilroy" is just for fun, though in principle getpwuid can fail to return anything useful too. (You can have a user ID that doesn't have an entry in the password database.)