Function eats my space characters - function

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.

Related

I need to extract Data from a single line of json-data which is inbetween two variables (Powershell)

I need to extract Data from a single line of json-data which is inbetween two variables (Powershell)
my Variables:
in front of Data:
DeviceAddresses":[{"Id":
after Data:
,"
I tried this, but there needs to be some error because of all the special characters I'm using:
$devicepattern = {DeviceAddresses":[{"Id":{.*?},"}
#$deviceid = [regex]::match($changeduserdata, $devicepattern).Groups[1].Value
#$deviceid
As you've found, some character literals can't be used as-is in a regex pattern because they carry special meaning - we call these meta-characters.
In order to match the corresponding character literal in an input string, we need to escape it with \ -
to match a literal (, we use the escape sequence \(,
for a literal }, we use \}, and so on...
Fortunately, you don't need to know or remember which ones are meta-characters or escapable sequences - we can use Regex.Escape() to escape all the special character literals in a given pattern string:
$prefix = [regex]::Escape('DeviceAddresses":[{"Id":')
$capture = '(.*?)'
$suffix = [regex]::Escape(',"')
$devicePattern = "${prefix}${capture}${suffix}"
You also don't need to call [regex]::Match directly, PowerShell will populate the automatic $Matches variable with match groups whenever a scalar -match succeeds:
if($changeduserdata -match $devicePattern){
$deviceid = $Matches[1]
} else {
Write-Error 'DeviceID not found'
}
For reference, the following ASCII literals needs to be escaped in .NET's regex grammar:
$ ( ) * + . ? [ \ ^ { |
Additionally, # and (regular space character) needs to be escaped and a number of other whitespace characters have to be translated to their respective escape sequences to make patterns safe for use with the IgnorePatternWhitespace option (this is not applicable to your current scenario):
\u0009 => '\t' # Tab
\u000A => '\n' # Line Feed
\u000C => '\f' # Form Feed
\u000D => '\r' # Carriage Return
... all of which Regex.Escape() takes into account for you :)
To complement Mathias R. Jessen's helpful answer:
Generally, note that JSON data is much easier to work with - and processed more robustly - if you parse it into objects whose properties you can access - see the bottom section.
As for your regex attempt:
Note: The following also applies to all PowerShell-native regex features, such as the -match, -replace, and -split operators, the switch statement, and the Select-String cmdlet.
Mathias' answer uses [regex]::Escape() to escape the parts of the regex pattern to be used verbatim by the regex engine.
This is unequivocally the best approach if those verbatim parts aren't known in advance - e.g., when provided via a variable or expression, or passed as an argument.
However, in a regex pattern that is specified as a string literal it is often easier to individually \-escape the regex metacharacters, i.e. those characters that would otherwise have special meaning to the regex engine.
The list of characters that need escaping is (it can be inferred from the .NET Regular-Expression Quick Reference):
\ ( ) | . * + ? ^ $ [ {
If you enable the IgnorePatternWhiteSpace option (which you can do inline with
(?x), at the start of a pattern), you'll additionally have to \-escape:
#
significant whitespace characters (those you actually want matched) specified verbatim (e.g., ' ', or via string interpolation,"`t"); this does not apply to those specified via escape sequences (e.g., \t or \n).
Therefore, the solution could be simplified to:
# Sample JSON
$changeduserdata = '{"DeviceAddresses":[{"Id": 42,"More": "stuff"}]}'
# Note how [ and { are \-escaped
$deviceId = if ($changeduserdata -match 'DeviceAddresses":\[\{"Id":(.*?),"') {
$Matches[1]
}
Using ConvertFrom-Json to properly parse JSON into objects is both more robust and more convenient, as it allows property access (dot notation) to extract the value of interest:
# Sample JSON
$changeduserdata = '{"DeviceAddresses":[{"Id": 42,"More": "stuff"}]}'
# Convert to an object ([pscustomobject]) and drill down to the property
# of interest; note that the value of .DeviceAddresses is an *array* ([...]).
$deviceId = (ConvertFrom-Json $changeduserdata).DeviceAddresses[0].Id # -> 42

Strange interaction between print and the ternary conditional operator

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

Does Ruby's gsub not treat \n as a whitespace character?

I'm trying to perform a simple operation - turn some input text into JSON, process it, and use it further.
require 'json'
aws_region = "us-east-1"
tag = `sudo aws ec2 describe-tags --region="#{aws_region}" --
filters "Name=resource-type,Values=instance" "Name=key,Values=Group"
"Name=resource-id,Values=$(ec2metadata --instance-id)"`
puts tag
tag_json = tag.to_json.gsub(/\s+/, "")
#tag_json = tag.gsub("\n", "")
puts tag_json
obj = JSON.parse(tag_json)
desired_value = obj["Tags"][0]["Value"]
puts desired_value
I expected the above to strip out all whitespace including newlines, but to my surprise, the output still has newlines in it. The JSON.parse fails with the below error because the newlines are still present. With the additional tag_json assignment above uncommented, it removes the newlines and succeeds.
JSON::ParserError
-----------------
746: unexpected token at '"{\n\"Tags\": [\n{\n\"ResourceType\":
\"instance\", \n\"ResourceId\": \"i-XXXXXX\", \n\"Value\":
\"groupA\", \n\"Key\": \"Group\"\n}\n]\n}\n"'
I end up having to have a separate case for newlines. Why does gsub treat newline characters as non-whitespace? Is there any other expression that will combine all of whitespace, tabs and newlines so I can strip them out?
Maybe it's an encoding issue. Try tag_json = tag.to_json.gsub(/[\s\p{]/, "")
You don't need the + in your expression because gsub removes all occurrences of a single character anyway.
Consider "aaaaaa".gsub(/a/, '') # => ""

Using backslash-newline sequence in Tcl

In Tcl, we are using the backslash for escaping special characters as well as for spreading long commands across multiple lines.
For example, a typical if loop can be written as
set some_Variable_here 1
if { $some_Variable_here == 1 } {
puts "it is equal to 1"
} else {
puts "it is not equal to 1"
}
With the help of backslash, it can be written as follows too
set some_Variable_here 1
if { $some_Variable_here == 1 } \
{
puts "it is equal to 1"
} \
else {
puts "it is not equal to 1"
}
So, with backslash we can make the statements to be treated as if like they are in the same line.
Lets consider the set statement
I can write something like as below
set x Albert\ Einstein;# This works
puts $x
#This one is not working
set y Albert\
Einstein
If I try with double quotes or braces, then the above one will work. So, is it possible to escape the newline with backslashes without double quotes or braces?
A backslash-newline-whitespace* sequence (i.e., following whitespace is skipped over) is always replaced with a single space. To get a backslash followed by a newline in the resulting string, use \\ followed by \n instead.
set y Albert\\\nEinstein

Why is my perl replace not working?

I have the following perl search and replace :
With Escapes :
perl -pi -w -e 's/ onclick=\"img=document.getElementById(\'img_1\')\; img.style.display = (img.style.display == \'none\' ? \'block\' : \'none\');return false"//' test.html
Without Escapes :
perl -pi -w -e 's/ onclick="img=document.getElementById('img_1'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false"//' test.html
My objective is to replace onclick="img=document.getElementById('img_1'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false" with nothing in the file test.html. What am I messing up?
I keep on getting the error : -sh: syntax error near unexpected token)'` which I cannot help but feel is because of some stupid escaping on my part. Please help me out.
[ You didn't specify for which shell you are building the command. I'm going to assume sh or bash. ]
Problem #1: Converting text into a regex pattern
Many characters have a special meaning in regex patterns. Among those you used, (, ), . and ? are special. These need to be escaped.
If you want to match
onclick="img=document.getElementById('img_1'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false"
You need to use the pattern
onclick="img=document\.getElementById\('img_1'\); img\.style\.display = \(img\.style\.display == 'none' \? 'block' : 'none'\);return false"
So your Perl code is
s/onclick="img=document\.getElementById\('img_1'\); img\.style\.display = \(img\.style\.display == 'none' \? 'block' : 'none'\);return false"//
Problem #2: Converting text (a Perl program) into a shell literal
This is the problem you asked about.
Sections quoted by single quotes ("'") end at the next single quote (unconditionally), so the following is wrong:
echo 'foo\'bar' # Third "'" isn't matched.
If you wanted to output "foo'bar", you could use
echo 'foo'\''bar' # Concatenation of "foo", "'" and "bar".
foo and bar are quoted by single quotes, and the single quote is escaped using \. \ works here because it's outside of single quotes.
So, the lesson is basically use '\'' instead of ' inside single quotes.
you want to pass the following program to Perl:
s/onclick="img=document\.getElementById\('img_1'\); img\.style\.display = \(img\.style\.display == 'none' \? 'block' : 'none'\);return false"//
To do so, you'll need to create a shell literal that produces the correct argument, meaning we want to surround the whole with single quotes and escape the existing single quotes. We get the following:
's/onclick="img=document\.getElementById\('\''img_1'\''\); img\.style\.display = \(img\.style\.display == '\''none'\'' \? '\''block'\'' : '\''none'\''\);return false"//'
As such, the entire command would be
perl -i -wpe's/onclick="img=document\.getElementById\('\''img_1'\''\); img\.style\.display = \(img\.style\.display == '\''none'\'' \? '\''block'\'' : '\''none'\''\);return false"//' test.html
Using the next:
perl -lnE 'say quotemeta $_'
and feed it with your plain input:
onclick="img=document.getElementById('img_1'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false"
you will get:
onclick\=\"img\=document\.getElementById\(\'img_1\'\)\;\ img\.style\.display\ \=\ \(img\.style\.display\ \=\=\ \'none\'\ \?\ \'block\'\ \:\ \'none\'\)\;return\ false\"
So using it:
perl -i -pe "s/onclick\=\"img\=document\.getElementById\(\'img_1\'\)\;\ img\.style\.display\ \=\ \(img\.style\.display\ \=\=\ \'none\'\ \?\ \'block\'\ \:\ \'none\'\)\;return\ false\"//"
should work.
Some things are easier when you don't use a one-liner. Less things to escape, and not as fragile.
use strict;
use warnings;
my $literal_string = q{ onclick="img=document.getElementById('img_1'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false"};
while (<>) {
s/\Q$literal_string//;
print;
}
Then execute the script:
perl -i thescript.pl test.html