Strange interaction between print and the ternary conditional operator - function

Ran into a strange interaction between print and the ternary conditional operator that I don't understand. If we do...:
print 'foo, ' . (1 ? 'yes' : 'no') . ' bar';
...then we get the output...:
foo, yes bar
...as we would expect. However, if we do...:
print (1 ? 'yes' : 'no') . ' bar';
...then we just get the output...:
yes
Why isn't " bar" getting appended to the output in the second case?

Let's do it, but for real -- that is, with warnings on
perl -we'print (1 ? "yes" : "no") . " bar"'
It prints
print (...) interpreted as function at -e line 1.
Useless use of concatenation (.) or string in void context at -e line 1.
yes
(but no newline at the end)
So since (1 ? "yes" : "no") is taken as the argument list for the print function then the ternary is evaluated to yes and that is the argument for print and so that, alone, is printed. As this is a known "gotcha," which can easily be done in error, we are kindly given a warning for it.
Then the string " bar" is concatenated (to the return value of print which is 1), what is meaningless in void context, and for what we also get a warning.
One workaround is to prepend a +, forcing the interpretation of () as an expression
perl -we'print +(1 ? "yes" : "no") . " bar", "\n"'
Or, call the print as function properly, with full parenthesis
perl -we'print( (1 ? "yes" : "no") . " bar", "\n" )'
where I've added the newline in both cases.
See this post for a detailed discussion of a related example and precise documentation links.

If the first non-whitespace character after a function name is an opening parenthesis, then Perl will interpret that as the start of the function's parameter list and the matching closing parenthesis will be used as the end of the parameter list. This is one of the things that use warnings will tell you about.
The usual fix is to insert a + before the opening parenthesis.
$ perl -e "print (1 ? 'yes' : 'no') . ' bar'"
yes
$ perl -e "print +(1 ? 'yes' : 'no') . ' bar'"
yes bar

Related

jq if value contains then append

This might not be the ideal way to approach this but I am working on bulk update of some Grafana dashboards. When the expr key contains value that includes something like "sum((rate" I want to append to the value another string. Is this even possible?
if (.dashboard.panels[].targets[].expr | contains("sum((rate")) then . += "TEST" end'
I've tried a few variations of then action and even removed the concatanation to see if I could get simple replace. But getting
jq: error: syntax error, unexpected end (Unix shell quoting issues?) at <top-level>, line 1:
You should be able to update-assign part of your objects:
.dashboard.panels[].targets[].expr |= if contains("sum((rate") then . + "TEST" else . end
It's also possible to do without the if conditional, by first selecting all the interesting paths and then modifying only them:
(.dashboard.panels[].targets[].expr | select(contains("sum((rate"))) += "TEST"
You always need an else branch. For readability you can also pull the target up front (otherwise . will match the previous context).
.dashboard.panels[].targets[].expr |=
if contains("sum((rate") then . + "TEST" else . end
Demo
.dashboard.panels[].targets[].expr |= . +
if contains("sum((rate") then "TEST" else "" end
Demo

What is wrong with print (2 & 2) >> 1?

I am just wondering what happens with that piece of code.
Why the result is incorrect only when printed directly, why is the newline ignored?
user#host_09:22 AM: perl
print 2 >> 1, "\n";
print 2 & 2, "\n";
print (2 & 2) >> 1, "\n";
1
2
2user#host_09:22 AM: perl
$a = (2 & 2) >> 1;
print "$a\n";
1
Perl interprets the parentheses as function arguments marker, as you can verify with
perl -MO=Deparse,-p -e 'print (2 & 2) >> 1'
Output:
(print(2) >> 1);
The canonical way is to precede the left parenthesis with a +:
print +(2 & 2) >> 1
When you print it with warnings it becomes clear(er)
perl -we'print (2 & 2), "\n"'
says
print (...) interpreted as function at -e line 1.
Useless use of a constant ("\n") in void context at -e line 1.
It works out print (2&2) as a function call to print† and duly prints 2 (no newline!), and then it keeps evaluating the comma operator, with "\n" in void context next, which it also warns us about.
With >> 1 also there, the return 1 of print (2&2) (for success) is bit shifted to 0, which disappears into the void, and we get
another "Useless use of ... in void context."
One fix is to add a + since what follows it must be an expression
perl -we'print +(2 & 2) >> 1, "\n"'
Or, make a proper call to print, with parenthesis around the whole thing
perl -we'print((2 & 2) >> 1, "\n")'
Both print a line with 1.
This is mentioned in print, and more fully documented in Terms and List operators and in Symbolic Unary operators, both in perlop. For another, related, example see this post.
† It also warns about it as it is likely an error -- with a space before parens; no space, no warning.

difference between lines in the same column using AWK

I want to compare the lines of the same column in a csv file and keep only the lines that respect the following conditions
1.if the first pattern is the same as the one in the previous line and
2.the difference between the values in the second column equal abs(1)
for example if I have this lines
aaaa;12
aaaa;13
bbbb;11
bbbb;9
cccc;9
cccc;8
I will keep only
aaaa;12
aaaa;13
cccc;9
cccc;8
The logic would work this way:
If the previous pattern is not equal to this pattern, then remember the this pattern and this value as the new "previous", and move to the next line.
Otherwise, if the difference between the previous value and this value equals 1 or -1 (awk does not have an abs() function) then print the previous pattern and value and print this line.
Take a stab at translating that into code, and come back when you have questions.
Given:
$ echo "$test"
aaaa;12
aaaa;13
bbbb;11
bbbb;9
cccc;9
cccc;8
You can do something like:
$ echo "$test" | awk -F ";" 'function abs(v) {return v < 0 ? -v : v} $1==l1 && abs($2-l2)==1 {print l1 FS l2 RS $0} {l1=$1;l2=$2}'
aaaa;12
aaaa;13
cccc;9
cccc;8

Function eats my space characters

I wrote a function
function! BrickWrap(data, icon_align, icon_left, icon_right)
if empty(a:data)
return ''
endif
let l:brick = (a:icon_align ==# 'left') ? a:icon_left . a:data :
\ (a:icon_align ==# 'right') ? a:data . a:icon_right :
\ (a:icon_align ==# 'surround') ? a:icon_left . a:data . a:icon_right :
\ (a:icon_align ==# 'none') ? a:data :
\ ''
if empty(l:brick)
echom 'BrickWrap(): Argument "icon_align" must be "left", "right", "surround" or "none".'
return ''
endif
return l:brick
endfunction
in order to format some data which gets displayed inside my statusline, e.g.:
set statusline+=%#User2#%{BrickWrap('some_data','surround','\ ','\ ')}
The example above should wrap the data with a space character on each side. But what happens actually is that it only appends a space character to the right but not to the left. In order get a space character displayed to the left I have to pass two escaped space characters ('\ \ '). I have to mention that it only happens in the statusline. If I'd :call the function it works as expected.
Why does this happen?
To use backslashes and whitespace with :set, you need additional escaping, see :help option-backslash. So, your backslash in front of the space is already taken by the :set command. (You can check this via :set stl?)
If coming up with the correct escaping is too hard, an alternative is to use the :let command instead:
:let &statusline = '...'
However, then you must only use double quotes in your statusline value, or deal with the quote-within-quote escaping then.

What is wrong with this simple vim function?

Please have a look to the below vim function which I written in my /.gvimrc file.
The function id for deleting the "n" number of last characters in each line from the range of lines specified by "start_line" and "end_line".
function RLNC (n, start_line, end_line)
execute . a:start_line . "," . a:end_line . "s/.\{" . a:n . "}$//"
endfunction
but when I make the same as a function and call it in the vim
:call RLNC(3, 128, 203)
This is the actual operation I am doing here
:start_line,end_lines/.\{n}$//
This is nothing but
:128,203s/.\{3}$//
Please help me to find what is going wrong..?
its is giving errors
The error is:
E15: Invalid expression: . a:start_line . "," . a:end_line . "s/.\{" . a:n . "}$//"
So, the first period is suspect. The :execute command takes (one or multiple) expressions. String concatenation via . is only done between strings, not at the beginning.
Just leave off the first .:
execute a:start_line . "," . a:end_line . "s/.\{" . a:n . "}$//"
The manual concatenation is tedious. Better use printf():
execute printf("%d,%ds/.\{%d}$//", a:start_line, a:end_line, a:n)
The next problem is that inside double quotes, the backslash must be escaped (doubled). Better use single quotes:
execute printf('%d,%ds/.\{%d}$//', a:start_line, a:end_line, a:n)
Finally, Vim has a special syntax to pass a range to a function. See :help function-range-example. You do not need to use this, but it makes the invocation more natural:
:128,203call RLNC(3)
However, I would probably go ahead and define a custom command wrapping the function.
:command! -range -nargs=1 RLNC call RLNC(<args>, <line1>, <line2>)
If your function isn't actually more complex, we can now inline this and get rid of the function altogether:
:command! -range -nargs=1 RLNC execute printf('%d,%ds/.\{%d}$//', <line1>, <line2>, <args>)
(Note that without a function, the last search pattern gets clobbered.)
Thank for your reply, I am new to vim function and all. So I don't know much about the ":command!" and all. So I put it as function in the /.gvimrc file like below :
function RLNC (start_line, end_line, n)
if (a:start_line <= a:end_line)
execute printf(':%d,%ds/.\{%d}$//', a:start_line, a:end_line, a:n)
else
execute printf('Start line %d is more than End line %d ', a:start_line, a:end_line)
endif
endfunction
and its working fine when I use the :call RLNC(128, 203, 3) in my gvim files.
Thanks You