How can I concatenate a variable and string without spaces in powershell - powershell

I'm importing some values from a csv file and using them to create a adb command for an Android intent with the following code.
Write-Host adb shell am start -a android.intent.action.VIEW '-d' '"https://api.whatsapp.com/send?phone=' $($c.number)'"'
This gives me an out put of:
adb shell am start -a android.intent.action.VIEW -d "https://api.whatsapp.com/send?phone= 12345678 "
How can I remove the spaces where the variable is concatenated to the string to give the output of:
adb shell am start -a android.intent.action.VIEW -d "https://api.whatsapp.com/send?phone=12345678"

Use string interpolation by switching to double quotes:
Write-Host adb shell am start -a android.intent.action.VIEW '-d' "`"https://api.whatsapp.com/send?phone=$($c.number)`""
Within double quotes, you have to backtick-escape double quotes to output them literally.

zett42's helpful answer is unquestionably the best solution to your problem.
As for what you tried:
Write-Host ... '"https://api.whatsapp.com/send?phone=' $($c.number)'"'
The fact that there is a space before $($c.number) implies that you're passing at least two arguments.
However, due to PowerShell's argument-mode parsing quirks, you're passing three, because the '"' string that directly follows $($c.number) too becomes its own argument.
See this answer for more information.
Therefore, compound string arguments (composed of a mix of quoted and unquoted / differently quoted tokens) are best avoided in PowerShell.
Therefore:
Either: Use a single, expandable (double-quoted) string ("..."), as in zett42's answer.
Or: Use an expression enclosed in (...) and use string concatenation with the + operator, as shown below.
Write-Host ... ('"https://api.whatsapp.com/send?phone=' + $c.number + '"')

Related

Using backticks to escape characters in sed with PowerShell running on Windows

I'm aware that, in PowerShell, backticks(`) are used to escape characters instead of the backslash(\). Hence, when using sed, this:
echo '"where am I"' | sed "s/\"[^\"]*\"/\033[96m&\033[0m/g;"
will not work and I would have to write it like this:
echo '"where am I"' | sed "s/`"[^\`"]*`"/`e[96m&`e[0m/g;"
Hence, I would like to do the same with this code:
echo '[where am I]' | sed "s/\[\([^09]\)/\033[94m[\033[0m\1/g;"
The expected output should be:
However, when I tried to replace all the backslashes(\) with backticks(`), I can't seem to get the expected output as you can see below:
Any help would be appreciated. Thanks in advance.
As far as I'm aware, GNU sed doesn't support octal escape sequences - such as \033 to represent an ESC char. - only hexadecimal ones - such as \x1b.
The following command, which uses hex. escape sequences, should therefore work in both POSIX-compatible shells and in PowerShell:
# Note: Requires *GNU* sed
(echo '[where am I]' | sed 's/\[\([^09]\)/\x1b[94m[\x1b[0m\1/g')
In other words:
Because the entire, verbatim string ('...') is interpreted by sed itself, there is no need for PowerShell's string interpolation (via "...").
Note:
The enclosing (...), which on Windows is required for Windows PowerShell to properly render the ANSI escape codes / VT (Virtual Terminal) sequences in the terminal (console) - see this answer. It isn't needed if you capture the output in a variable or redirect it.
In PowerShell, use of echo - a built in alias of the Write-Output cmdlet - isn't necessary and can be omitted, due to PowerShell's implicit output behavior.
If your sed implementation doesn't support escape sequences such as \x1b - such as on macOS - you must indeed resort to using PowerShell's escape sequences inside an expandable (double-quoted) string ("..."); that is, you must replace \x1b with escape sequence `e in order to embed a verbatim ESC character into the string, up front (in Windows PowerShell, which doesn't support `e, use subexpression $([char] 0x1b) instead):
# Note: `e only works in PowerShell (Core) 7+
# In Windows PowerShell, use $([char] 0x1b)
(echo '[where am I]' | sed "s/\[\([^09]\)/`e[94m[`e[0m\1/g")
Important: The above commands contain no embedded " characters, which avoids a long-standing PowerShell bug that is still present as of PowerShell (Core) 7.2.2:
The need to unconditionally, manually \-escape embedded " chars. in arguments passed to external programs - see this answer for details.
A few quick examples that print verbatim 3" of snow:
# Up to at least PowerShell 7.2.2:
# Without escaping the embedded " with \ (too),
# they would in effect be removed from the arguments.
# Note that pipeline input is *not* affected.
'3" of snow' | findstr '\"' # Windows
/bin/echo '3\" of snow' # Unix
# In expandable (double-quoted) strings, you therefore must escape *twice*:
# once for PowerShell, then for the external-program call.
'3" of snow' | findstr "\`"" # Windows
/bin/echo "3\`" of snow" # Unix
You can pass non-interpolated expression strings to sed by using single quotes, '; e.g., 'a non-interpolated string'. instead of double quotes around the text. sed would take what it normally does for input. When you use double quotes, that's when powershell escaping comes into play.
Anyway, an alternative to sed would be leveraging the regular expression substitution built into powershell. For instance,
Write-Output ('[where am i]' -replace '\[(.*?)\]', "<<`e[5;36m`$1`e[0m>>")
Will capture the text between square brackets and place it between "chevrons" and make it blink.
Here's an article on inserting ANSI in text using powershell.

Powershell Newman run pass --env-var as string

Trying to run via powershell postman collection as following:
newman run $collPath -e $envPath $envVarsStr
where
$envVarsStr is a string containg N number dynamically generated --env-var
e.g.
--env-var "Base.Url=someurl" --env-var "Password=somepass"
But newman seems to ignore all my --env-vars
Using extra file is really not an option for me.
Any ideas on how to pass env-var string correctly?
You cannot pass what an external program should see as multiple arguments via a single string, because PowerShell passes a string as a single argument, irrespective of the string's content.
Instead, use an array of strings, each element of which is passed as a separate argument.
Thus, if you control the creation of the arguments, use the following:
# Construct an array of arguments.
# Note: No *embedded* " quoting necessary - PowerShell will double-quote on demand.
$envVarsArgs =
'--env-var',
'Base.Url=someurl', # use variables or expressions as needed
'--env-var',
'Password=somepass'
and then call newman as follows:
# Note: #envVarsArgs would work too.
newman run $collPath -e $envPath $envVarsArgs
If you're given a single string encoding multiple arguments, you can split it into an array of individual arguments yourself, which in the simplest case means:
newman run $collPath -e $envPath (-split $envVarsStr -replace '"')
However, this is not enough if any of the double-quoted substrings contain spaces (e.g. --env-var "Foo=bar none"), because the unary form of -split splits by whitespace only.
If you need to deal with a given string whose embedded quoted arguments have spaces, you can use Invoke-Expression with an expandable (double-quoted) string ("...") in a pinch.
Note: Invoke-Expression (iex) should generally be the last resort, due to its security risks: only ever use it on input you either provided yourself or fully trust - see this answer.
# !! See warning re Invoke-Expression above.
Invoke-Expression #"
newman run "$collPath" -e "$envPath" $envVarsStr
"#
Note the use of an expandable here-string (#"<newline>...<newline>"#), which simplifies embedded quoting (no need to escape the embedded " chars.)
Another option is to use --%, the stop-parsing token, which, however, requires you to define an auxiliary environment variable that duplicates the value of your $envVarsStr variable:
Note: --% has many limitations - see this answer.
# Duplicate the string stored in $envVarsStr
# in an aux. environment variable, so it can be used with --%
$env:__aux = $envVarsStr
# Note the use of --% and the cmd.exe-style environment-variable
# reference:
newman run $collPath -e $envPath --% %__aux%

Is there a way to escape quotes in ripgrep for MS Windows (Powershell or CMD)?

I want to find a string "Hello (Hello starts with double quote) in text files using ripgrep.
Normally, in Bash or ZSH, this would work by escaping with backslash or surrounding with single quote:
rg \"Hello
rg '"Hello'
However, in MS Windows (Powershell and CMD), I've tried these but none of these worked:
rg \"Hello
rg '"Hello'
rg `"Hello
rg '`"Hello'
Is there any way to escape single or double quotes using ripgrep in MS Windows?
Verbatim string "Hello must ultimately be passed as \"Hello to rg ("\"Hello" would work too). That is, the verbatim " char. must be \-escaped:
From cmd.exe:
rg \^"Hello
^, cmd.exe's escape character, ensures that the " is treated verbatim and is removed by cmd.exe before calling rg.
Note that ^ isn't strictly necessary here, but it prevents the " from being considered the start of a double-quoted argument, which could make a difference if there were additional arguments.
From PowerShell:
rg \`"Hello
`, PowerShell's escape character, ensures that the " is treated verbatim and is removed by PowerShell before calling rg.
Arguably, the explicit \-escaping shouldn't be necessary, because it is the duty of a shell to properly pass arguments to target executables after the user has satisfied the shell's own escaping requirements (escaping the verbatim " with ^ in cmd.exe, and with ` in PowerShell).
In the context of PowerShell, this problematic behavior is summarized in this answer.
Note that in PowerShell this extra escaping is only needed if you call external programs; it isn't needed PowerShell-internally - such as when you call Select-String, as shown in js2010's answer.
You can use rg -F \"Hello
-F, --fixed-strings
Treat the pattern as a literal string instead of a regular expression. When this flag is used, special regular expression meta characters such as .(){}*+ do not
need to be escaped.
This flag can be disabled with --no-fixed-strings.
If you're in powershell you might as well do:
get-childitem file | select-string '"hello'
file:1:"hello

How to escape double quotes and colon in ACK in PowerShell

I am using Ack version 2.04, in PowerShell. I want to search texts like "jsonClass":"Page" (quotes included) inside text files.
I can't seem to get the quoting and escaping correct.
ack -c --match '"jsonClass":"Page"'
It doesn't work in PowerShell. I guess ack's picking up the single quotes as well.
Escaping the double quotes gives an invalid regex error:
ack -c --match "\"jsonClass\":\"Page\""
Invalid regex '\':
Trailing \ in regex m/\/ at C:\CHOCOL~1\lib\ACK2~1.04\content\ack.pl line 315
I tried the literal option as well, but I think ack's interpreting the colon as file parameters.
ack -c -Q --match "jsonClass":"Page"
ack.pl: :Page: No such file or directory
What am I missing?
I am using PowerShell v2.
To complement JPBlanc's effective answer with a PowerShell v3+ solution:
When invoking external programs such as ack
, use of the so-called stop-parsing symbol, --%, makes PowerShell pass the remaining arguments through as-is, with the exception of expanding cmd.exe-style environment-variable references such as %PATH%:
ack --% -c --match "\"jsonClass\":\"Page\""
This allows you to focus on the escaping rules of the target program only, without worrying about the complex interplay with PowerShell's own parsing and escaping.
Thus, in PowerShell v3 or higher, the OP's own 2nd solution attempt would have worked by passing --% as the first parameter.
See Get-Help about_Parsing.
Note that the exact equivalent of the above command without the use of --% (that also works in PSv2 and is generally helpful if you want to include PowerShell-expanded variables / expressions in other arguments) would look like this:
ack -c --match '"\"jsonClass\":\"Page\""'
That is, the entire argument to be passed as-is is enclosed in single quotes, which ensures that PowerShell doesn't interpret it.
Note the inner enclosing " that aren't present in JPBlanc's answer (as of this writing). They guarantee that the argument is ultimately seen as a single argument by ack, even if it contains whitespace.
If ack is a function it could happen that -c is interpreted by PowerShell. Can you test the following?
ack '-c' '--match' '"jsonClass":"Page"'
-c --match should be interpreted as --match is the value of parameter c in function ask.
If it's an EXE file, try:
ack -c --match '\"jsonClass\":\"Page\"'
In Powershell you can always use ' quote to handle inner " without problems.
But if you need to use " for a string (to interpolate a variable, for instance), you can escape them with `
$myvar = 'test'
$formatted = "{`"value`":`"$myvar`"}"
Write-Host $formatted
# output: {"value":"test"}

tcl exec to open a program with agruments

I want to open a text file in notepad++ in a particular line number. If I do this in cmdline the command should be:
start notepad++ "F:\Path\test.txt" -n100
And it is working fine from command line. Now I have to do this from tcl. But I can't make this command work with exec. When I try to execute this:
exec "start notepad++ \"F:\Path\test.txt\" -n100"
I am getting this error:
couldn't execute "start notepad++ "F:\Path\test.txt" -n100": no such file or directory.
What am I missing. Please guide.
Similar to this question:
exec {*}[auto_execok start] notepad++ F:/Path/test.txt -n10
First, you need to supply each argument of the command as separate values, instead of a single string/list. Next, to mimic the start command, you would need to use {*}[auto_execok start].
I also used forward slashes instead of backslashes, since you would get a first level substitution and get F:Path est.txt.
EDIT: It escaped me that you could keep the backslashes if you used braces to prevent substitution:
exec {*}[auto_execok start] notepad++ {F:\Path\test.txt} -n10
You can simply surround the entire exec statement in curly braces. Like this:
catch {exec start notepad++.exe f:\Path\test.txt -n10}
I haven't found a perfect solution to this yet. All my execs seem to be different from each other. On windows there are various issues.
Preserving double quotes around filename (or other) arguments.
e.g. in tasklist /fi "pid eq 2060" /nh the quotes are required.
Preserving spaces in filename arguments.
Preserving backslash characters in filename arguments.
[Internally, Windows doesn't care whether pathnames have / or \, but some programs will parse the filename arguments and expect the backslash character].
The following will handle the backslashes and preserve spaces, but will not handle double-quoted arguments. This method is easy to use. You can build up the command line using list and lappend.
set cmd [list notepad]
set fn "C:\\test 1.txt"
lappend cmd $fn
exec {*}$cmd
Using a string variable rather than a list allows preservation of quoted arguments:
set cmd [auto_execok start]
append cmd " notepad"
append cmd " \"C:\\test 1.txt\""
exec {*}$cmd
Note that if you need to supply the full path to the command to be executed, it often needs to be quoted also due to spaces in the pathname:
set cmd "\"C:\\Program Files\\mystuff\\my stuff.exe\" "