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

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"

Related

How to run command with preceding variable [duplicate]

This question already has answers here:
PowerShell: Setting an environment variable for a single command only
(10 answers)
Closed 4 years ago.
I know that you can pass environment variables to docker-compose.
docker-compose.yml
. . .
mysql:
image: mariadb:10.2
ports:
- "${DB_PORT}:3306"
. . .
$ DB_PORT=3396 docker-compose up
However this only works using bash. I am using PowerShell and am trying to find an equivalent that is only a one line command.
PS> $env:DB_PORT:3306 docker-compose up does not work. Neither does
multiline
$env:DB_PORT=3396 `
>> docker-compose -up
The error I get is
Unexpected token 'docker-compose' in expression or statement.
If I do it one at a time it does work...
PS> $env:DB_PORT=3396
PS> docker-compose -up
Is there not way to do this in PowerShell when the equivalent in bash is ridiculously simple?
POSIX-like shells such as bash offer a way to set environment variables in a command-scoped way, simply by prepending <varName>=<value> pairs directly to a command, as the following example demonstrates:
$ foo=bar bash -c 'echo "[$foo]"'; echo "[$foo]"
[bar]
[]
foo=bar defines environment variable foo for the bash -c '...' child process only; the next command - echo ... - does not see this variable.
PowerShell has NO equivalent construct.
The best you can do is to define the environment variable of interest first, in a separate statement, using ;, PowerShell's statement separator. Any external utility you invoke thereafter - which invariably runs in a child process - will see it, but note that the environment variable will remain in effect in the current PowerShell session, unless you manually remove it:
# Set the env. variable, call the command that should see it,
# remove it afterwards.
PS> $env:foo = 'bar'; bash -c 'echo "[$foo]"'; $env:foo = $null
[bar]
Note how $env:foo = $null i.e., setting the environment variable to $null is the same as removing it; alternatively, you could all Remove-Item env:foo
If you also want to restore a pre-existing value afterwards:
$env:foo = 'original'
# Temporarily change $env:foo to a different value, invoke the
# program that should see it, then restore the previous value.
& { $org, $env:foo = $env:foo, 'bar'; bash -c 'echo "[$foo]"'; $env:foo = $org }
$env:foo
The above yields:
[bar]
original
showing that while the bash process saw the temporary value, bar, the original value of $env:foo was restored afterwards.
Also note another important difference:
In POSIX-like shells, environment variables are implicitly surfaced as shell variables - they share the one and only namespace the shell has for variables.
By contrast, PowerShell surfaces environment variables only via the $env:<varName> namespace (e.g., $env:foo), which is distinct from the (prefix-less) namespace for PowerShell's own variables (e.g., $foo).

Supplying an input file via '#' gives an error: The splatting operator '#' cannot be used to reference variables in an expression

Following this example here https://learn.microsoft.com/en-us/cli/azure/vm/run-command?view=azure-cli-latest
I'm getting an error when running my command
az vm run-command invoke --command-id RunPowerShellScript --name win-vm -g my-resource-group --scripts #script.ps1
Error:
The splatting operator '#' cannot be used to reference variables in an expression. '#script' can be used only as an argument to a command. To reference variables in an expression use '$script'.
Putting it in quotes only passes in the contents in the quotes, not the contents of the script.
if you install package whit # you should install package whit CMD
Note:
This answer shows how to escape / quote the # char. properly in the context of PowerShell's usual parsing rules.
If your command line only contains verbatim arguments - i.e., only literal tokens, not PowerShell variable references (e.g, $file) or expressions (e.g., ($dir + '\script.ps1')) - you can alternatively place --%, the stop-parsing token, before the pass-through arguments, as shown in programmer365's answer; note that cmd.exe-style variable references such as %FOO% are still expanded, however, and that the stop-parsing token has many limitations and pitfalls - see this answer.
# is a metacharacter in PowerShell (a character with syntactic meaning[1]), so in order to pass it verbatim through to az you must either quote the whole argument or `-escape the # individually:
With a literal script filename:
# Either: `-escape the #
az ... --scripts `#script.ps1
#`# Or: quote the whole argument
# Use '...' for a literal argument.
az ... --scripts '#script.ps1'
With the script filename stored in a variable, $file:
# Either: `-escape the #
az ... --scripts `#$file
#`# Or: quote the whole argument
# Use "..." for an argument with variable references, i.e. an expandable string
az ... --scripts "#$file"
Note: You could get away with just #$file in the variable case, but given that that doesn't work with any char. other than $ following the #, it's better to get into the habit of always quoting / escaping a verbatim #.
[1] # has several syntactic uses, and the specific use depends on what character comes next. In your case, the # in #script.ps1 was interpreted as the splatting operator with a variable named script, with the .ps1 part interpreted as an attempt to access a property named ps1 on that variable - hence the error message.
You can use this:
az --% vm run-command invoke --command-id RunPowerShellScript --name win-vm -g my-resource-group --scripts #script.ps1
In PowerShell the special Stop Parsing symbol --% is a signal to PowerShell to stop interpreting any remaining characters on the line. This can be used to call a non-PowerShell utility and pass along some quoted parameters exactly as is.

Running executable with command line parameters in PowerShell

This has to be possible. I am able to open a command prompt in windows and do the following:
<some exe> <some exe command line parameters>
There must be an equivalent way to do this in PowerShell or even a standard windows batch file. For example, from the windows command prompt I can start a docker container with:
docker run –-net=kafka -d –-name=zookeeper -e ZOOKEEPER_CLIENT_PORT=2181 confluentinc/cp-zookeeper:4.1.0
however if I try something like this with PowerShell
& "docker" run –-net=kafka -d –-name=zookeeper -e ZOOKEEPER_CLIENT_PORT=2181 confluentinc/cp-zookeeper:4.1.0
it fails with an generic error:
invalid reference format.
Perhaps PowerShell is not suited for this type of advanced use case. Any suggestions would be appreciated!
Is there a better scripting language for advanced usages like this?
I think Start-Process cmdlet will be useful. ArgumentList can be single or double quoted.
Start-Process docker -ArgumentList "run –-net=kafka -d –-name=zookeeper -e ZOOKEEPER_CLIENT_PORT=2181 confluentinc/cp-zookeeper:4.1.0"
By and large, external programs in PowerShell are called the same way as from cmd.exe - there are differences, due to PowerShell having additional metacharacters such as $ and #, but they do not come into play in your specific case.
(Your & "docker" ... variant would work in principle too, but the use of & is only necessary if you must use a quoted or variable-based command name or path).
The problem is that your original command line contains two instances of – (EN DASH, U+2013) instead of the expected ASCII-range - dash (hyphen), which docker doesn't recognize.
A quick way to discover the problem:
# Print the code points of characters outside the ASCII range.
PS> [int[]] [char[]] '& "docker" run –-net=kafka -d –-name=zookeeper -e ZOOKEEPER_CLIENT_PORT=2181 confluentinc/cp-zookeeper:4.1.0' -gt 127
8211
8211
Decimal 8211 is hex. 0x2013, the code point of en-dash, whereas the code point of the regular - is 45 (0x2d).
All that is needed is to replace these – instances with - (and, since docker needn't be quoted, there is no need for &):
docker run --net=kafka -d --name=zookeeper -e ZOOKEEPER_CLIENT_PORT=2181 confluentinc/cp-zookeeper:4.1.0
Your own answer shows a variable-based implementation of the command that is effectively the same as the command above - if all the arguments are known in advance, there is never a need to use variables.
If you do want to use variables, it is much simpler to use a single array variable for all the arguments and pass that:
$dockerExe = 'docker'
$dockerArgs = 'run',
'--net=kafka',
'-d',
'--name=zookeeper',
'-e',
'ZOOKEEPER_CLIENT_PORT=2181',
'confluentinc/cp-zookeeper:4.1.0'
& $dockerExe $dockerArgs
Note:
The executable name/path must always be specified separately, and if it is quoted or involves variable references (as in this case), &, the call operator must be used for invocation, for syntactic reasons.
Passing the arguments as an array this way works with external programs; for PowerShell commands, you'd create a hashtable variable that you pass with sigil # instead of $, a feature known as splatting.
There is a lot of complexities in powershell escaping. I wrote this module to assist with running of external commands:
https://github.com/choovick/ps-invoke-externalcommand
Demo:
https://terminalizer.com/view/49acb54a979
Install-Module -Name ExternalCommand -Scope CurrentUser
Invoke-ExternalCommand -Command "docker" -Arguments #("run","-d","--name=zookeeper","--net=kafka","-e","ZOOKEEPER_CLIENT_PORT=2181", "confluentinc/cp-zookeeper:4.1.0")
Here's how to do it.
$app = 'docker'
$a1 = 'run'
$a2 = '--net=kafka'
$a3 = '-d'
$a4 = '--name=zookeeper'
$a5 = '-e'
$a6 = 'ZOOKEEPER_CLIENT_PORT=2181'
$a7 = 'confluentinc/cp-zookeeper:4.1.0'
& $app $a1 $a2 $a3 $a4 $a5 $a6 $a7

How to force bash/zsh to evaluate parameter as multiple arguments when applied to a command

I am trying to run a program like this:
$CMD $ARGS
where $ARGS is a set of arguments with spaces. However, zsh appears to be handing off the contents of $ARGS as a single argument to the executable. Here is a specific example:
$export ARGS="localhost -p22"
$ssh $ARGS
ssh: Could not resolve hostname localhost -p22: Name or service not known
Is there a bash or zsh flag that controls this behavior?
Note that when I put this type of command in a $!/bin/sh script, it performs as expected.
Thanks,
SetJmp
In zsh it's easy:
Use cmd ${=ARGS} to split $ARGS into separate arguments by word (split on whitespace).
Use cmd ${(f)ARGS} to split $ARGS into separate arguments by line (split on newlines but not spaces/tabs).
As others have mentioned, setting SH_WORD_SPLIT makes word splitting happen by default, but before you do that in zsh, cf. http://zsh.sourceforge.net/FAQ/zshfaq03.html for an explanation as to why whitespace splitting is not enabled by default.
If you want to have a string variable (there are also arrays) be split into words before passing to a command, use $=VAR. There is also an option (shwordsplit if I am not mistaking) that will make any command $VAR act like command $=VAR, but I suggest not to set it: I find it very inconvenient to type things like command "$VAR" (zsh: command $VAR) and command "${ARRAY[#]}" (zsh: command $ARRAY).
It will work if you use eval $CMD $ARGS.

How to use the # character in 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>