Powershell: Have working Aliases for longer commands with parameters - powershell

I am trying to write some PowerShell aliases in my profile.ps1 file. The problem is that I can not get commands with a variable number of parameters to work properly.
For example, I want to have an alias that links "kg" to "kubectl get", which is followed by a resource and sometimes an option. So "kg pods -A" and "kg pod" should both work afterwords.
According to this post, I need to define a function that can be seen below, my problem is that it ignores all arguments after the first. So "kg pods -A" returns the result of "kg pods".
This post explains how to pass multiple arguments to a function, up it does not work for some reason.
(I am using Powershell in Cmder if this is relevant).
Could it be that the "-" causes an issue?
The relevant part of the "profile.ps1":
Function get ($restOfLine, $restOfLine2) { kubectl get $restOfLine $restOfLine2}
Set-Alias kg get

As soon as you explicitly declare parameters in a function, PowerShell will attempt to bind input arguments to those parameters, by parsing the command invocation expression and treating any sequence starting with - as a parameter name.
Remove the parameters and pass $args directly to avoid this interference:
function get { kubectl get #args }
Set-Alias kg get
kg post -A will now pass post and -A to kubectl as-is

Related

Trying to create an alias to include options in powershell

My goal is to have an alias to run a command with -o at the end.
PS C:\Users\XXXX> vlab -o <various different arguments>
I have tried to setup an alias, but Set-Alias recognizes -o as a parameter Option, and
placing it in single or double quotes fails
> set-alias vlab 'vlab -o'
> vlab
vlab : The term 'vlab -o' is not recognized as the name of a cmdlet, function, script file, or operable program.
also setup a profile.ps1 with a function, but this just hangs when I run it.
Function VlabSpeed {
vlab -o
}
Set-Alias vlab VlabSpeed
Is it possible to set an alias like this?
if so, how?
Thanks
To recap:
PowerShell aliases can only map a name to another command name or executable file path - unlike in, say, Bash, defining arguments as part of the mapping is not supported.
To incorporate arguments, you indeed need a function rather than an alias:
Inside that function, you can use #args to pass any arguments through to another command, as Santiago Squarzon points out.
There is no need for both an alias and a function - just name your function directly as the short name you desire.
If your short (function) name is the same as the file name of the external program that it wraps, you need to disambiguate the two to avoid infinite recursion:
On Windows:
Use the external program's filename extension, typically .exe:
function vlab { vlab.exe -o #args }
On Unix-like platforms:
Given that executables there (typically) have no filename extension, use the full path to disambiguate:
function vlab { /path/to/vlab -o #args }

Adding Arguments to PowerShell Alias Functions

I just installed grep on PowerShell through chocolatey and using it, I could search for a string I want from any text as shown below:
However, I don't want to always have to type grep --color, hence I tried to make an alias on my $profile like this:
Function grep($a1){
grep --color $a1
}
but then I would get an error:
So I tried:
Set-Alias grep "grep --color"
but then I get yet another error:
I can't think of anything else to make this work, hence I'd really appreciate any help.
Aliases in PowerShell are mere alternative names for other commands, so you cannot use them to include arguments to pass to these other commands.
You therefore indeed need a function, but since you're naming it the same as the external program you're wrapping, you need to disambiguate so as to avoid an infinite recursion:
function grep {
$externalGrep = Get-Command -Type Application grep
if ($MyInvocation.ExpectingInput) { # pipeline (stdin) input present
# $args passes all arguments through.
$input | & $externalGrep --color $args
} else {
& $externalGrep --color $args
}
}
Note:
Use of the automatic $input variable to relay pipeline (stdin) input means that this input is collected (buffered) in full first. More effort is needed to implement a true streaming solution - see this answer for an example.
Alternatively - at least on Unix-like platforms - you can set an environment variable to control grep's coloring behavior, which may obviate the need for a function wrapper; the equivalent of the --color parameter is to set $env:GREP_COLOR='always' (the other supported values are 'never' and 'auto').

Multi parameters in Powershell Bash/Zsh command

Unable to run the following Bash/Zsh command in Powershell:
$KeyPath = Join-Path -Path $this.Plate -ChildPath "install/tekton.key"
kubectl create secret docker-registry regcred `
--docker-server="https://gcr.io" `
--docker-username=_json_key `
--docker-email="name#org.iam.gserviceaccount.com" `
--docker-password="$(cat $KeyPath)"
I get error:
error: exactly one NAME is required, got 5
See 'kubectl create secret docker-registry -h' for help and examples
If I run this command directly in bash it works:
kubectl create secret docker-registry regcred --docker-server="https://gcr.io" --docker-username=_json_key --docker-email="name#org.iam.gserviceaccount.com" --docker-password="$(cat ./tekton.key)"
I don't know if it's the cause of your problem, but there are two potential problems:
An expanded value that contains spaces causes PowerShell to double-quote the argument as a whole when it rebuilds the command line behind the scenes (on Windows):
For instance, if $(cat $KeyPath) ($(Get-Content $KeyPath)) expands to one two, PowerShell passes "--docker-password=one two" behind the scenes, not --docker-password="one two".
Whether this changes the meaning of the argument depends on how the target program parses its command line - I don't know what kubectl does.
If you do need to address this, escape the enclosing " (double quotes) with `` ``` (the backtick, PowerShell's escape character to make PowerShell pass your argument in the original syntax form:
--docker-password=`"$(cat ./tekton.key)`"
Note that - unlike in POSIX-like shells such as Bash and Zsh - you normally do not enclose a variable reference or subexpression in "..." in order to pass it through safely; e.g., --foo=$someVar or --foo=$(Get-Date) work fine, even if $someVar or the output from Get-Date contains spaces or wildcard characters.
If file $KeyPath contains multiple lines, the lines are concatenated with spaces in the argument:
For instance, if the file contains "a`nb`n" ("`n" being a newline), PowerShell will pass
"--docker-password=a b".
By contrast, POSIX-like shells such as Bash or Zsh will preserve the interior newlines, while trimming (any number of) trailing ones.
On a side note: PowerShell's handling of embedded double-quoting in arguments passed to external programs is broken - see this answer.

Assignment of environment variables in Dockerfile as a result of a command

I'm trying to asign the result of a powershell command to MY_VAR environment variable. I have tried several ways but I cannot get the variable to take the value of the operation. If I assign the variable as follows what I get as a value is the command literally.
ARG MY_ARG="VALUE"
ENV MY_VAR=[Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes($MY_ARG))
ENV MY_VAR2=$([Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes($MY_ARG)))
When I check the values into the container I get this:
Get-Childitem -Path Env:MY_VAR*
Name Value
---- -----
MY_VAR [Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes(VALUE))
MY_VAR2 $([Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes(VALUE)))
The base of my containers are Windows Server Core and my shell is powershell.
See this SO post , it contains several workarounds to achieve that you want.
ENV treat value as a simple text string.
UPDATE:
According to answers and comments here:
1) RUN $profile that would show you location of environment profile. Get more familiar with this file.
2) Try RUN $env.MY_VAR = [Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes($MY_ARG)) >> $profile or another way to append the command to file. I am not familiar enough with powershell, thus be aware that you maybe would to fix slightly the command. (Comment with the right command and I will fix it for next viewers.)
3) Try to read MY_VAR in the container. If all is right, then Hoooray!, else check in the $profile that you actually get the right string of setting the variable.

Powershell Variable replacement not working from command line

I have the following command I want to run from PowerShell:
docker run -v C:\src\docker_certs:/root/.dotnet/https -it MyContainer:MyTag /bin/bash
When I run that it works perfectly. (It mounts a volume using the source folder at the destination folder.)
But when I run this:
docker run -v $env:DOCKER_CERTS_PATH:/root/.dotnet/https -it MyContainer:MyTag /bin/bash
The volume does not get mounted.
I run this to check the value:
echo $env:DOCKER_CERTS_PATH
And it returns:
C:\src\docker_certs
As I understood things, it should have replaced the value of $env:DOCKER_CERTS_PATH with C:\src\docker_certs in the second command.
How can I get the PowerShell reference to an environment variable to replace when I run a command?
Enclose the environment-variable reference in {...}:
docker run -v ${env:DOCKER_CERTS_PATH}:/root/.dotnet/https ...
Alternatively, `-escape the : char. following the env.-var. reference:
docker run -v $env:DOCKER_CERTS_PATH`:/root/.dotnet/https ...
As for what you tried:
docker run -v $env:DOCKER_CERTS_PATH:/root/.dotnet/https ...
If you don't use {...} to explicitly delineate a variable name, PowerShell may have a different idea of where the variable name ends than you do.
As an alternative to using {...}, you can `-escape the first character you don't want to be considered part of the variable name.
Note that your command argument is in this case implicitly treated as if it were enclosed in "...", so the above applies to expandable strings ("...") too.
For an comprehensive discussion of how unquoted tokens are parsed as command arguments, see this answer.
In the case at hand, the : that follows $env:DOCKER_CERTS_PATH is not considered the end of the variable reference; instead, it is considered part of the variable name, so that PowerShell looks for an environment variable (env:) literally named DOCKER_CERTS_PATH: (sic).
Since no such environment variable (presumably) exists, $env:DOCKER_CERTS_PATH: expands to the empty string and all that is passed to docker is /root/.dotnet/https.
You can verify that DOCKER_CERTS_PATH: is a valid environment variable name as follows:
PS> $env:DOCKER_CERTS_PATH: = 'hi'; $env:DOCKER_CERTS_PATH:
hi
By contrast, a regular (shell) variable is not permitted to contain :, because that : - in the absence of a namespace prefix such as env: - is itself considered a namespace prefix, which fails, because then the variable-name part is missing:
PS> $DOCKER_CERTS_PATH: = 'hi' # BREAKS, even with {...}
Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to delimit the name.
The first : in a variable identifier is invariably considered the end of the namespace identifier, which must refer to an existing PowerShell drive name, as reported by Get-PSDrive.
This notation is called namespace variable notation, as explained in this answer.