How to use the # character in PowerShell - powershell

I am trying to do the following - for each *.sql file in the current directory run
sqlplus username/password#connect_identifier_specified_in_argument #file_name
Here is what I have so far:
$scripts = dir *.sql
foreach($script in $scripts) {
Write-Host sqlplus username/password"#"$args "#"$script.Name
}
(I know Write-Host outputs it to the screen; I'm just trying to debug for now.)
However, there is something funky with how PowerShell treats the # character and when I run this I always get something like:
PS C:\code\scripts> C:\utils\run_sql_scripts_on.ps1 identifier
sqlplus username/password#identifier # ALERTS.sql
See that space after the "#"? What gives?

Escape the # with a backtick (`).
Write-Host sqlplus username/password`#$args `#$script.Name

PowerShell Community Extensions has a handy little utility (echoargs) for debugging this sort of problem:
5>echoargs username/password"#"$args "#"$script.Name
Arg 0 is <username/password#>
Arg 1 is <#>
Arg 2 is <test.txt>
Try escaping with a backtick:
6>echoargs "username/password`#$args" "`#$($script.Name)"
Arg 0 is <username/password#>
Arg 1 is <#test.txt>

Related

How do I get WSL Vim to accept pipeline input from Powershell?

I'm trying to create a Powershell function that acts as a wrapper for Vim in WSL. I would like this wrapper to be able to accept input from the pipeline.
In WSL, it's possible to do something like:
$ cat manylines.txt | vim -
The - indicates that Vim should read the input from stdin, which is then redirected through the pipeline. This will open vim with the command output already in the buffer, without any file associated.
(You could also simply open the file with vim manylines.txt, but that wouldn't work in general for the outputs of other commands or executables).
The same is possible in Powershell, with Windows native Vim:
Get-Content manylines.txt | vim -
It's possible to go one step further and execute Vim within another instance of Powershell or cmd:
Get-Content manylines.txt | powershell -Command vim -
Get-Content manylines.txt | cmd /c vim -
However, the same does not work with WSL Vim.
# doesn't work, Vim freezes
Get-Content manylines.txt | wsl vim -
Get-Content manylines.txt | wsl bash -c "vim -"
# doesn't work, Vim quits reporting "Error reading Input"
Get-Content manylines.txt | wsl bash -c vim -
In the first two cases, Vim opens correctly with the contents of manylines.txt in the buffer... and then refuses to accept any keyboard input. It is not possible to navigate, edit, or even quit with :q!.
Using a temporary powershell variable seems to fix things, but only when the input consists of a single line
# "works as long as the file only has a single line of text"
# "$tmpvar has type System.String (not an array)"
$tmpvar = Get-Content singleline.txt
wsl bash -c "echo $tmp | vim -"
But this breaks when $tmpvar becomes a String[] array or a String with any newlines
# sort of works, but newlines are converted to a single space
$tmpvar = [string[]] Get-Content manylines.txt
wsl bash -c "echo $tmp | vim -"
# combines the strings in the array, inserts a newline between each pair of elements
$tmpvar = ([string[]] Get-Content manylines.txt) -join "`n"
# doesn't work, bash interprets the newline as the start of the next command
wsl bash -c "echo $tmp | vim -"
How do I get WSL Vim to accept pipeline input from Powershell?
Other Notes:
I could use a temporary file and pass the file to WSL Vim, but that's not an elegant solution and leaves a file that has to be cleaned up. Unless the temporary file is in the current directory, it also involves extra shenanigans with wslpath
Indeed it seems that piping input to vim via wsl seems to break vim's keyboard interface with respect to cursor movements (I was still able to type :q! to quit, for instance).
The workaround is to refine the approach you've discovered, namely to provide the input as part of a shell command passed to wsl instead of using the pipeline:
Doing so requires careful escaping and transformations, as shown in the following wrapper function:
# PowerShell wrapper function for invoking vim via WSL
function vim {
if ($MyInvocation.ExpectingInput) { # pipeline input present
$allInput = ($input | Out-String) -replace '\r?\n', "`n" -replace "'", "'\''" -replace '"', '\"'
wsl -e sh -c "printf %s '$allInput' | vim $args -"
}
elseif ($args) { # no pipeline input, arguments that may include file paths given
wsl -e vim ($args.ToLower() -replace '^([a-z]):', '/mnt/$1' -replace '\\', '/')
} else { # no pipeline input, no pass-through arguments
wsl -e vim
}
}
Caveats:
When passing command output, you may run into the max. command-line length.
When passing file paths, relative file paths work fine, but absolute ones work only for local drives (mapped drives / UNC paths require mounting a volume on the WSL side first).
With this function defined, you can call, say (+, as a sample option, tells vim to place the cursor at the end of the input):
vim + somefile.txt
or
Get-ChildItem | vim +

how to pass parameter to a powershell script when running from a shell script through cygwin?

i have sh script let say a.sh in which i am doing a ssh to windows server (configured with cygwin) and running a B.ps1 script which takes the parameter defined in a.sh.
Content of a.sh:
var1="abc"
var2="xyz"
#sshing to windows box
/usr/bin/scp -r -q /home/$user/Jenkins/workspace/job/jobname $user2#$x:/cygdrive/C/
/usr/bin/ssh $user2#$x 'powershell C:\\B.ps1 $var1 $var2'
this is running the script but without any parameters, when i write host variable name in B.ps1, i get blank output, which means the var1&var2 values are not getting passed to my ps1 script.
Content of B.ps1:
$var1=$args[0]
$var2=$args[1]
Write-Host "var1 is:" $var1
Write-Host "var2 is:" $var2
i have tried to use double quotation in my sh script , didn't work, it seems like there must be some way i can pass parameter but may be missing out anything on syntax.
please help.
/usr/bin/ssh $user2#$x "powershell C:/B.ps1 $var1 $var2"
You need double quoting ("...") rather than single quoting ('...') in order for the shell-variable references $var1 and $var2 to be expanded.
By using / as the path separator - which PowerShell accepts interchangeably with \ - you avoid the need to escape \ characters, which you would have had to double on transitioning from '...' to "..."; that is, the following would have worked too:
/usr/bin/ssh $user2#$x "powershell C:\\\\B.ps1 $var1 $var2"

PowerShell: String expansion in command mode

What are the rules of string expansion in command mode? If on cmd.exe, I would write this:
c:\asdoc.exe -doc-sources+=src
I need to convert this to a string where the actual source path ("src") is computed so somewhere above this line, $sourcePath = "src" is executed. Now I need to transform that cmd.exe command to PowerShell command. I have tried the following but it doesn't work:
& c:\asdoc.exe -doc-sources+=$sourcePath # does NOT work
& c:\asdoc.exe -doc-sources+="$sourcePath" # does NOT work
& c:\asdoc.exe -doc-sources+=($sourcePath) # does NOT work
I used the EchoArgs utility and it gives me these results:
Arg 0 is <-doc-sources+=$sourcePath> # case 1
Arg 0 is <-doc-sources+=$sourcePath> # case 2
Arg 0 is <-doc-sources+=> # case 3
Arg 1 is <src path>
How do I make the string expand "correctly" in this example?
If i understand you properly you want everything expanded and as a single argument. Try to "collect" the argument with quotes.
PS > $sourcepath = "src"
PS > & EchoArgs.exe "-doc-sources+=$($sourcePath)"
Arg 0 is <-doc-sources+=src>
So try this:
& c:\asdoc.exe "-doc-sources+=$($sourcePath)"
The example below will also works AS LONG as you want to expand a variable an not a property inside a variable (ex $myobject.sourcepath)
& c:\asdoc.exe "-doc-sources+=$sourcePath"

How to install a Windows Service in Powershell when the commandline has quotes in it?

I have a service I need to install that has a commandline that looks something like this:
d:\My Service\service.exe /AsService /Config="d:\Config Files\TheConfig.config"
This is pretty much the worst-case scenario for a commandline. The issue I have is with the double quotes in the args.
This is what I've tried that is close but not quite:
$cmd = "create ""$ServiceName"" binPath= $FullServicePath start= $StartMethod"
Invoke-Expression "cmd.exe /c sc.exe $cmd" | Write-Host
Where $FullServicePath is:
d:\My Service\service.exe /AsService /Config="d:\Config Files\TheConfig.config"
Suggestions? I'm open to anything. WMI. Whatever.
Help!
If you're 100% sure that you need quotes, then you have to escape them with \:
&'d:\My Service\service.exe' '/AsService' '/Config=\"d:\Config Files\TheConfig.config\"'
Echoargs output:
Arg 0 is </AsService>
Arg 1 is </Config="d:\Config Files\TheConfig.config">
But probably this will work too and it's much easier:
&'d:\My Service\service.exe' #('/AsService', '/Config=d:\Config Files\TheConfig.config\')
Echoargs output:
Arg 0 is </AsService>
Arg 1 is </Config=d:\Config Files\TheConfig.config>
Try declaring $FullServicePath like this:
$FullServicePath = "d:\My Service\service.exe /AsService /Config=`"d:\Config Files\TheConfig.config`""
This uses the escape character ` to tell it to process the quotes as text. (The escape character is the back tic next to the 1 key on most keyboards)
I believe you can also use this syntax:
$FullServicePath = $('d:\My Service\service.exe /AsService /Config="d:\Config Files\TheConfig.config"')
Both created the variable without a problem on my system.
When you call the $FullServicePath do it like this: $($FullServicePath)
This is a method i used .
Installing a service in powershell

How do I reference variables when executing a shell command in PowerShell?

I'm a newbie to PowerShell. What's wrong with my script below? It's not wanting to emit the value of $config. However, when I wrap that command in double quotes, everything looks okay.
param($config, $logfolder)
# Must run log analysis in chronological order.
ls $logfolder | Sort-Object LastWriteTime | % {
perl D:\Websites\_awstats\wwwroot\cgi-bin\awstats.pl -LogFile="$($_.FullName)" -config=$config update
}
# Execute with - .\regen-logs.ps1 webgenesis "C:\inetpub\logs\LogFiles\W3SVC5"
# Returns for each file - Error: Couldn't open config file "awstats.config.conf" nor "awstats.conf" after searching in path "D:\Websites\_awstats\wwwroot\cgi-bin,/etc/awstats,/usr/local/etc/awstats,/etc,/etc/opt/awstats": No such file or directory
As-is, what gets emitted and executed seems to have "-config=$config" passed as an argument. At least, that's my best guess. I don't know if $_ is working correctly either.
If I put quotes around the perl command like so, I get the command I do want to execute.
ls $logfolder | Sort-Object LastWriteTime | % {
"perl D:\Websites\_awstats\wwwroot\cgi-bin\awstats.pl -LogFile=`"$($_.FullName)`" -config=$config update"
}
# Outputs for each log file something like - perl D:\Websites\_awstats\wwwroot\cgi-bin\awstats.pl -LogFile="C:\inetpub\logs\LogFiles\W3SVC5\u_ex110602.log" -config=webgenesis update
If putting quotes around it produces the correct commandline, one way to execute the contents of a string is with Invoke-Expression (alias iex):
$v = "myexe -myarg1 -myarg2=$someVar"
iex $v
Put double quotes around "-config=$config". Without this, PowerShell will interpret -config=$config as one string argument that just happens to contain a $ sign in it.
I think you need to start your perl command out with & so that PowerShell interprets things as a command and not a string.
& perl D:\Websites\_awstats\wwwroot\cgi-bin\awstats.pl -LogFile=`"$($_.FullName)`" -config=$config update
Also, see: Run a program in a foreach