In conky, how do I nest a variable within a template? - templates

In conky, how do I nest a variable within a template?
EXAMPLES:
${template2 enp0s25} <- WORKS (fixed string)
${template2 ${gw_iface}} < FAILS (nested variable)
${template2 ${execpi 10 ls -d /sys/class/net/enp* 2> /dev/null | sed -e 's,/sys/class/net/,,'}} <- FAILS (nested variable command)
I've also tried (and failed):
${combine ${template2 ${gw_iface}}}
${combine ${template2} ${gw_iface}}
Here is "template2":
template2 = [[
${if_existing /proc/net/route \1}Wired Ethernet ("\1"):
- MAC: ${execi 5 cat /sys/class/net/\1/address} IP: ${addr \1}
- Max: ${execi 5 /sbin/ethtool '\1' 2>/dev/null | sed -n -e 's/^.*Speed: //p'}${goto 190}${if_match ${downspeedf \1} > 0}${font :bold:size=14}${endif}Down: ${downspeedf \1}kB/s${font}${goto 370}${if_match ${upspeedf \1} > 0}${font :bold:size=14}${endif}Up: ${upspeedf \1}kB/s${font}
${endif}]]
Thanks for the help.

Templates are a little limited as you cannot evaluate the parameters before they are passed through. One workaround is to use a bit of lua code to do the eval explicitly and then parse the template. For example,
conky.config = {
lua_load = '/tmp/myfunction.lua',
...
};
conky.text = [[
${lua myeval template2 ${gw_iface}}
]]
Create the lua file, /tmp/myfunction.lua holding
function conky_myeval(tpl, var1)
v = conky_parse(var1)
cmd = "${"..tpl.." "..v.."}"
return conky_parse(cmd)
end
The lua function takes the name of the template, tpl, and the parameter to evaluate, var1. It evaluates the latter using conky_parse() to, say, string "xxx", then constructs a new string "${template2 xxx}", which is parsed and returned as the value of the ${lua} call.
The same can be done for the longer example ${execpi ...} too.

Related

gdb how to print variable name along with variable value like "$number = variable-name = variable-value"

By default, using p variable-name will display $num = variable-value, $num is the value history, but it there a way to print the variable name along with the variable value like $num = variable-name = variable-value?
I want this since I use
define p
set $i = 0
while $i < $argc
eval "print $arg%d", $i
set $i = $i + 1
end
end
in my ~/.gdbinit, to redefine p command so I can use p var1 var2 var3... to print multiple variables at once, but the print command only output $num = variable-value, and I don't know what the exact variable is in the output, the other situation is when I print the value history using just p $num, it is not that readable, I don't know the exact variable name.
NOTE: the variable may be int/char/pointer/array/vector/...
A solution could be to first add to the display list the wanted variables and then to display all of them together. Note that, it is needed to free the display list before with undisplay, otherwise it also prints the variables of the previous executions.
define p
set confirm off
eval "undisplay"
set confirm on
set $i = 0
while $i < $argc
eval "display $arg%d", $i
set $i = $i + 1
end
display
end
The undisplay evaluation is enclosed between set confirm off/on to suppress the following message:
[answered Y; input not from terminal]
If you have already set the confirm off option in your ~/gdbinit file, you will need to remove these two lines.
Edit: Honestly, I came to know about the display command finding a solution for this question. Although this answer might be useful to print multiple variables with their respective names, after several days using display in my workflow, I discourage to use this answer since I have come to the conclusion that display itself fits better at least my needs (printing multiple variables at every stop). Here the official doc:
If you find that you want to print the value of an expression frequently (to see how it changes), you might want to add it to the automatic display list so that GDB prints its value each time your program stops. Each expression added to the list is given a number to identify it; to remove an expression from the list, you specify that number. The automatic display looks like this:
2: foo = 38
3: bar[5] = (struct hack *) 0x3804
Basically, I have started using the command like this: I add a variable with display $var to the list of variables, and every time a breakpoint is reached the listed variables are automatically printed. It makes sense to have a feature like this in gdb. Thanks #CodyChan for the motivation.
In a nutshell, we want to output
$num = variable-name = variable-value
instead of
$num = variable-value
As far as I can tell, gdb adds to the value history in only three places: the print command, the call command, and the history-append! Scheme function. Since my Scheme is rusty, we'll need to use the CLI or Python to run print and modify its output.
Using the CLI
define pp
set $i = 0
while $i < $argc
eval "pipe print $arg%d | awk -v name='$arg%d' '{ if (NR == 1 && $2 == \"=\") { $2 = \"= \" name \" =\" }; print }'", $i, $i
set $i++
end
end
Pipe is new in gdb 10.
That awk command is, after unescaping,
awk -v name='$arg%d' '{ if (NR == 1 && $2 == "=") { $2 = "= " name " =" }; print }'
which changes the = (second field) in $num = variable-value to = variable-name = . If gdb's print command outputs more than one line, the NR == 1 in the awk command makes sure the substitution is only done on the first line.
Security note: gdb's pipe command appears to parse the shell_command into tokens and uses execve to run it, rather than passing it to an actual shell. This prevents some code injection attacks (if, for instance, the $arg%d in name='$arg%d' contains single quotes), but you should be careful of running any shell command comprised of text you haven't vetted.
Using Python
class PP(gdb.Command):
"""print value history index, name, and value of each arg"""
def __init__(self):
super(PP, self).__init__("pp", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION)
def invoke(self, argstr, from_tty):
for arg in gdb.string_to_argv(argstr):
line = gdb.execute("print " + arg, from_tty=False, to_string=True)
line = line.replace("=", "= " + arg + " =", 1)
gdb.write(line)
PP()
Here, we're using a more sed-like approach, using string.replace.
Sample session:
(gdb) set args a b c
(gdb) start
Starting program: /home/mp/argprint a b c
Temporary breakpoint 2, main (argc=4, argv=0x7ffffffee278) at argprint.c:4
4 for(int i=0; i < argc; i++) {
(gdb) pp i argc argv argv[0]#argc
$1 = i = 0
$2 = argc = 4
$3 = argv = (char **) 0x7ffffffee278
$4 = argv[0]#argc = {0x7ffffffee49f "/home/mp/argprint", 0x7ffffffee4b1 "a", 0x7ffffffee4b3 "b", 0x7ffffffee4b5 "c"}

Bash - Extract a column from a tsv file whose header matches a given pattern

I've got a tab-delimited file called dataTypeA.txt. It looks something like this:
Probe_ID GSM24652 GSM24653 GSM24654 GSM24655 GSM24656 GSM24657
1007_s_at 1149.82818866431 1156.14191288693 743.515922643437 1219.55564561635 1291.68030259557 1110.83793199643
1053_at 253.507372571459 150.907554200493 181.107054946649 99.0610660103702 147.953428467212 178.841519788697
117_at 157.176825094869 147.807257232552 162.11169957066 248.732378039521 176.808414979907 112.885784025819
121_at 1629.87514240262 1458.34809770171 1397.36209234134 1601.83045996129 1777.53949459116 1256.89054921471
1255_g_at 91.9622298972477 29.644137111864 61.3949774595639 41.2554576367652 78.4403716513328 66.5624213750532
1294_at 313.633291641829 305.907304474766 218.567756319376 335.301256439494 337.349552407502 316.760658896597
1316_at 195.799277107983 163.176402437481 111.887056644528 194.008323756222 211.992656497053 135.013920706472
1320_at 34.5168433158599 19.7928225262233 21.7147425051394 25.3213322300348 22.4410631949167 29.6960283168278
1405_i_at 74.938724593443 24.1084307838881 24.8088845994911 113.28326338746 74.6406975005947 70.016519414531
1431_at 88.5010900723741 21.0652011409692 84.8954961447585 110.017339630928 84.1264201735067 49.8556999547353
1438_at 26.0276274326623 45.5977459152141 31.8633816890024 38.568939176828 43.7048363737468 28.5759163094148
1487_at 1936.80799770498 2049.19167519573 1902.85054762899 2079.84030768241 2088.91036902825 1879.84684705068
1494_f_at 358.11266607978 271.309665853292 340.738488775022 477.953251687206 388.441738062896 329.43505750512
1598_g_at 2908.90515715761 4319.04621682741 2405.62061966298 3450.85255814957 2573.97860992156 2791.38660060659
160020_at 416.089910909237 327.353902186303 385.030831004533 385.199279534446 256.512900212781 217.754025190117
1729_at 43.1079499314469 114.654670657195 133.191500889286 86.4106614983387 122.099426341898 218.536976034472
177_at 75.9653827137444 27.4348937420347 16.5837374743166 50.6758325717831 58.7568500760629 18.8061888366161
1773_at 31.1717741953018 158.225161489953 161.976679771553 139.173486349393 218.572194156366 103.916119454
179_at 1613.72113870554 1563.35465407698 1725.1817757679 1694.82209331327 1535.8108561345 1650.09670894426
Let's say I have a variable col="GSM24655". I want to extract the column from dataTypeA.txt that corresponds to this column name.
Additionally, I'd like to put this in a function, where I can just give it a file (i.e. dataTypeA.txt), and a column (i.e. GSM24655), and it'll return that column.
I'm not very proficient in Bash, so I've been having some trouble with this. I'd appreciate the help.
Below script using awk can be used to achieve the objective.
col="GSM24655";
awk -v column_val="$col" '{ if (NR==1) {val=-1; for(i=1;i<=NF;i++) { if ($i == column_val) {val=i;}}} if(val != -1) print $val} ' dataTypeA.txt
Working: Initially, value of col is passed to awk script using -v column_val="$col" . Then the column number is find out. (when NR==1, i.e the first row, it iterates through all the fields (for(i=1;i<=NF;i++), awk variable NF contains the number of columns) and then compare the value of column_val (if ($i == column_val)), when a match is found the corresponding column number is found and stored ( val=i )). After that, from next row onwards, the values in that column is printed (print $val).
If you copy the below code into a file called say find_column.sh, you can call sh find_column.sh GSM24655 dataTypeA.txt to display the column having value of first parameter (GSM24655) in the file named second parameter (dataTypeA.txt). $1 and $2 are positional parameters. The lines column=$1 and file=$2 will assign the input values to the variables.
column=$1;
file=$2;
awk -v column_val="$column" '{ if (NR==1) {val=-1; for(i=1;i<=NF;i++) { if ($i == column_val) {val=i;}}} if(val != -1) print $val} ' $file
I would use the following, it is quick and easy.
In your script, you get the name of the file, let's say $1, and word, $2.
Then, in my for each I am using the whole header, but you can just add a head -1 $1, and in the IF, the $2, this is going to output column name.
c=0;
for each in `echo "Probe_ID GSM24652 GSM24653 GSM24654 GSM24655 GSM24656 GSM24657"`;do if [[ $each == "Probe_ID" ]];then
echo $c;
col=$c;
else c=$(( c + 1 ));
fi;
done
Right after this, you just do a cat $1| cut -d$'\t' -f$col

Recursively replacement on a variable

Given this associative array:
declare -A variables=(
[prefix]='/usr'
[exec_prefix]='#prefix#'
[libdir]='#exec_prefix#/lib'
)
I would like to replace any occurrence of the pattern #([^#/]+)# (e.g. #prefix#, with prefix being the capture) with the value that's associated to the capture (e.g. /usr for prefix) in all its values, such that the substitution be performed recursively until there are no more occurrences. The steps for each key in the array would be:
Retrieve the value associated to it and perform (2) on it.
Check if there is a match of the pattern in the given string.
If there isn't any, return the given string.
If there's a match:
Perform (1) on the capture and keep the result.
Replace the match by the result.
Perform (2) on the resulting string.
Drop the previous value associated to the key and associate to it the last string returned.
Whatever the approach, the desired result is:
prefix=/usr
exec_prefix=/usr
libdir=/usr/lib
Additional requirements:
Self references (e.g. prefix=#prefix#) will not occur.
If possible, use only Bash builtins.
Example in Lua:
local variables={
prefix="/usr",
exec_prefix="#prefix#",
includedir="#prefix#/include",
libdir="#exec_prefix#/lib",
random_one_to_show_off_fancy_recursion="#prefix##libdir##includedir#"
}
function replacer( variable )
return compute_value(variables[variable])
end
function compute_value( s )
return s:gsub('#([^#/]+)#',replacer)
end
local variable, value = next(variables)
while variable do
variables[variable] = compute_value(value)
print( string.format('%-39s\t%s', variable, variables[variable]) )
variable, value = next(variables,variable)
end
The (pure Bash) code below assumes that '##' is left unchanged and '#xyz#' is left unchanged when 'xyz' is not a variable. It also attempts to detect recursive variable definitions, including indirect ones (e.g. [a]=#b# [b]=#c# [c]=#a#).
# Regular expression for a string with an embedded expansion
# For a string of the form 'u#v#w', where 'u' and 'v' do not contain '#':
# u -> BASH_REMATCH[1]
# v -> BASH_REMATCH[2]
# w -> BASH_REMATCH[3]
readonly EXPANSION_RX='^([^#]*)#([^#]*)#(.*)$'
# First pass tries to expand all variables
vars_to_expand=( "${!variables[#]}" )
while (( ${#vars_to_expand[*]} > 0 )) ; do
old_vars_to_expand=( "${vars_to_expand[#]}" )
vars_to_expand=()
for var in "${old_vars_to_expand[#]}" ; do
val=${variables[$var]}
unexpanded=$val
newval=
while [[ $unexpanded =~ $EXPANSION_RX ]] ; do
newval+=${BASH_REMATCH[1]}
v=${BASH_REMATCH[2]}
unexpanded=${BASH_REMATCH[3]}
if [[ $v == "$var" ]] ; then
echo "ERROR - Expanding '#$var#' in '$var'" >&2
exit 1
elif [[ -z $v ]] ; then
# The empty string can not be a hash key (Duh!)
newval+=#$v#
else
newval+=${variables[$v]-#$v#}
fi
done
newval+=$unexpanded
if [[ $newval != "$val" ]] ; then
# An expansion has occurred.
# Update the variable value
variables[$var]=$newval
# Further expansions may be possible, so add the variable to the
# list of variables to be expanded again
vars_to_expand+=( "$var" )
fi
done
done

Trouble parsing custom structure with shell script utilities like sed / awk / grep

I am trying to use a shell script to parse a complex list of structures out of a text file, and search those structures for a very specific set of values. If there is a match then I need to print the values of one variable. I am limited to lightweight utilities like sed, awk, and grep (but not Perl).
Here is an example of the structure, followed by an explanation of what I am looking for:
{
{ 1, 2,
{ 15, 25 },
{ 15, 25 }
},
{ 3, 4,
{ 35, 45 },
{ 35, 45 }
},
{ 5, 6,
{ 55, 65 },
{ 55, 65 }
}
};
In this example I would be parsing the three structures and searching for a structure which has a "3" as the first value, has any single digit (0-9) as the second value, and at least one set of "35" and "45" in the inner list of structures. Once I have located a match I would then print the value of the second value. In this case the second structure would match, and I would need to print out the value "4".
I don't want to assume anything about how the whitespace is organized, only that the format above is followed. I.e. it could all be on a single line or have different combinations of line breaks in random places.
Can someone please help me think about how to approach this problem?
this may be what you want, using GNU awk for various extensions:
$ cat tst.awk
BEGIN { RS="[{}]"; FS="\\s*,\\s*" }
depth == 2 { split($0,outer) }
(depth == 3) && (outer[1]==3) && (outer[2]~/^[0-9]$/) &&
((($1==35) && ($2==45)) || (($1==45) && ($2==35))) { print outer[2] }
{ depth = depth + (RT=="{" ? 1 : -1) }
$ awk -f tst.awk file
4
A non-robust awk attempt
$ awk -F"[{,}]" '/{/ && !/}/{c=($2==3)?+$3:""}
c~/^[0-9]$/ && $2==35 && $3==45{print c;exit}' file
4
using the layout
Thank you all for the help on this one. I was able to eventually solve it using only sed and tr, although it wasn't pretty. I used tr to join all of the lines together, then sed to remove the outer { };, sed again to split the lines along structure boundaries by using backrefrences, sed again to cleanup commas and whitespace between structures, and then "sed -n -r "s//\1/p" to validate the expected values in pattern, and to print only the matching variable.
I will take a look at your examples and see if I can learn from them.

Is there a good way to replace home directory with tilde in bash?

Im trying to work with a path and replace the home directory with a tilde in bash, Im hoping to get it done with as little external programs as necessary. Is there a way to do it with just bash. I got
${PWD/#$HOME/\~}
But thats not quite right. It needs to convert:
/home/alice to ~
/home/alice/ to ~/
/home/alice/herp to ~/herp
/home/alicederp to /home/alicederp
As a note of interest, heres how the bash source does it when converting the \w value in the prompt:
/* Return a pretty pathname. If the first part of the pathname is
the same as $HOME, then replace that with `~'. */
char *
polite_directory_format (name)
char *name;
{
char *home;
int l;
home = get_string_value ("HOME");
l = home ? strlen (home) : 0;
if (l > 1 && strncmp (home, name, l) == 0 && (!name[l] || name[l] == '/'))
{
strncpy (tdir + 1, name + l, sizeof(tdir) - 2);
tdir[0] = '~';
tdir[sizeof(tdir) - 1] = '\0';
return (tdir);
}
else
return (name);
}
I don't know of a way to do it directly as part of a variable substitution, but you can do it as a command:
[[ "$name" =~ ^"$HOME"(/|$) ]] && name="~${name#$HOME}"
Note that this doesn't do exactly what you asked for: it replaces "/home/alice/" with "~/" rather than "~". This is intentional, since there are places where the trailing slash is significant (e.g. cp -R ~ /backups does something different from cp -R ~/ /backups).
See this unix.stackexchange answer:
If you're using bash, then the dirs builtin has the desired
behavior:
dirs +0
~/some/random/folder
That probably uses Bash's own C code that you pasted there. :)
And here's how you could use it:
dir=... # <- Use your own here.
# Switch to the given directory; Run "dirs" and save to variable.
# "cd" in a subshell does not affect the parent shell.
dir_with_tilde=$(cd "$dir" && dirs +0)
Note that this will only work with directory names that already exist.