Launch PowerShell from Command Prompt with custom prompt - windows

I'm trying to open PowerShell with a customised prompt (for instance the UNIX shell prompt). I have tried:
powershell -noexit -command "& {function prompt {"$(pwd)$ "}}"
But it just starts powershell without the prompt I want. It does actually work in powershell itself. Could I get this to work or do I have to make a seperate file and do it through "-file"?

Not sure what the UNIX prompt defaults too but this should do what I think you want it to do.
powershell -noexit -command "function prompt {'{0}$ ' -f $pwd}"
If you use single quotes in the prompt function the $ doesn't get interpolated, and you don't have to worry about to many quotes.
SAVING THE PROMPT FUNCTION
Like any function, the Prompt function exists only in the current
session. To save the Prompt function for future sessions, add it to your
Windows PowerShell profiles. For more information about profiles,
see about_Profiles.
Here's how to create a new profile:
if (!(test-path $profile))
{new-item -type file -path $profile -force}
notepad $profile

Quoting on the command-line is tricky. Also, & runs a scriptblock in its own scope, so functions defined there don't "leak" out to the calling scope. The dot operator (also called dot-sourcing) is what you're looking for. This is what I got to work using backslashes to quote the strings.
powershell -noexit -command ". {function prompt {\"$(pwd)$ \"}}"

Add your custom prompt to your profile and it will load/run every time you start PowerShell.

Powershell customisation is always a bit tricky. Try adding a script with a method called prompt() like this:
function prompt() {
$myPrompt = "Ready>";
write-host -NoNewLine -ForegroundColor green $myPrompt
' '
}
Then call this in a profile, such as the one for all users:
%windir%\system32\Windows­PowerShell\v1.0\profile.ps1
Good luck!

Related

How to start another PowerShell session in a separate window while keeping the environment?

While working in PowerShell I tend to quickly switch to admin mode by typing
Start-Process wt -Verb runas
When I do so, a new window appears (sadly, no sudo in Windows). In that new session however, the environment is totally fresh. Is it possible to keep variables, aliases, working dir and all other stuff of similar matter while jumping to a new window? If not, then well, it's a valid answer.
To give some example, I am looking for this behavior:
First window
C:\test> $x = 123
C:\test> Start-Process wt
New window
C:\test> $x
123
By (security-minded) design, elevated sessions (-Verb RunAs) do not inherit the caller's environment variables.
Also, wether or not you use -Verb RunAs, the state of a PowerShell session (aliases, functions, current location, ...) is never inherited when you launch another PowerShell process, such as with Start-Process.
You can work around the issue by explicitly and selectively redefining the state of interest via commands executed in the elevated session, based on values from the caller's state, but that is quite cumbersome and has limitations, as the following example shows:
# Define a few things to copy to the elevated session.
$x1 = 123
$x2 = '3" of snow' # !! See the caveat re regular variables below.
$env:foo = 1
$env:foo2 = 2
Set-Alias bar Get-Date
function baz { "hello, world" }
# Note: The following only copies the definitions above.
# You could try to copy ALL such definitions, by omitting a target name / pattern:
# Get-ChildItem env:
# Get-ChildItem function:
# Get-ChildItem alias:
# CAVEAT: This will NOT generally work with *regular variables*.
Start-Process -Verb RunAs powershell #"
-NoExit -Command Set-Location -LiteralPath \"$((Get-Location -PSProvider FileSystem).ProviderPath)\"
$(Get-Variable x? | ForEach-Object { "`${$($_.Name)} = $(if ($_.Value -is [string]) { "'{0}'" -f ($_.Value -replace "'", "''" -replace '"', '\"') } else { $_.Value }); " })
$(Get-ChildItem env:foo* | ForEach-Object { "Set-Item \`"env:$($_.Name)\`" \`"$($_.Value -replace '"', '\"\"')\`"; " })
$(Get-ChildItem function:bar | ForEach-Object { "`$function:$($_.Name) = \`"$($_.Definition -replace '"', '\"\"')\`"; " })
$(Get-ChildItem alias:baz | ForEach-Object { "`$alias:$($_.Name) = \`"$($_.Definition)\`"; " })
"#
Important:
I've omitted the call to Windows Terminal (wt.exe), as that would create another PowerShell session, which means that only the following definitions would be preserved for that session:
Environment variables.
The current location (working directory), IF its default shell is configured to use the parent process' working directory. Alternatively, and more predictably, pass the working dir. explicitly with the -d option: wt.exe -d \"$((Get-Location -PSProvider FileSystem).ProviderPath)\"
If that is enough, you can remove the commands that preserve aliases, functions, and regular variables, add -WindowStyle Hidden to Start-Process, remove -NoExit before -Command in the argument list, and add a wt.exe call at the bottom.
Preserving the other types of definitions requires working directly in the elevated powershell session, which will invariably use a regular (conhost.exe) console window, however.
In general, it's best to place definitions that should be available in both regular and elevated sessions in your $PROFILE file.
Complementarily, see this answer for convenience function Enter-AdminPSSession, which allows you to pass a script block to execute in the elevated session, to which you can pass values from the caller's state as arguments.
Note:
The above uses the Windows PowerShell CLI, powershell.exe. To use PowerShell (Core) 7+ instead, substitute pwsh.exe.
The above covers preserving the current file-system location, environment variables, aliases, and functions in a generic fashion.
Caveat: By contrast, preserving regular variables is limited to strings and numbers - in essence, instances of those data types whose stringified representation is recognized as such when interpreted as a source-code literal.
With nontrivial additional effort, supporting more data types is possible, by using Base64 encoding with the CLI's -EncodedCommand and -EncodedArguments parameters as shown in this answer, but the range of types that can be represented with type fidelity is fundamentally limited by PowerShell's XML-based serialization infrastructure - see this answer.
You can not keep variables, you will lose them immediately after the new window is created, the best you can do instead is to create a script containing all your activities then save it in the same working directory.
When you open a new window just call your script that will be able to give you the same information as in the other window.

Open a command file with Windows PowerShell running it directly

I want to make a file having Windows Powershell commands. Then I want to open it with windows powershell directly and without pressing any key I want windows powershell start running those commands directly same as command prompy I can make .cmd or .bat file.
For example:
These are two commands or Powershell, I want to save this file. Then I want directly execute this file by powershell. I have tried to save it as ps1 and ps2 extension as well but not working. Many methods online are not working. Any solution?
PowerShell script files, across all versions, use the .ps1 filename extension.
From within PowerShell, you can invoke them directly, e.g., .\script.ps1
Note that, unlike in cmd.exe, you must use .\ (or a full path) in order to execute a file located in the current directory - just script.ps1 won't work - see this answer for background information.
From cmd.exe, you must use PowerShell's CLI (powershell.exe in Windows PowerShell / pwsh in PowerShell [Core] v6+) in order to execute a script file:
powershell.exe -File script.ps1
pwsh -File script.ps1 (-File may be omitted)
Note that with -File the .\-prefix is not required.
However, if you use -Command (-c) instead (which is the default with powershell.exe, whereas pwsh now defaults to -File), you do need the .\, because the -Command argument(s) are interpreted as a piece of PowerShell code, i.e. as if you had submitted it inside a PowerShell session.
You've discovered this in your own answer, where you pass a PowerShell command directly to the (implied) -Command parameter.
Note, however, that it's better to double-quote such commands, so as to prevent cmd.exe from interpreting certain characters itself, which breaks the call.
For instance, the following call would break, if you didn't enclose the -Command (-c) argument in "...":
# From cmd.exe; "..." required.
C:\>powershell.exe -c "Write-Output 'a & b'"
a & b
Another important consideration is that you need to escape embedded " chars. as \" for the CLI (even though PowerShell-internally you would use `" or ""):
# From cmd.exe; note the inner " escaped as \"
C:\>powershell.exe -c "Write-Output \"hi there\""
hi there
I have found the solution. I use command powershell.exe and can directly execute powershell commands within cmd.
powershell.exe $MyVariable=Get-Content .\Path.txt
is working fine for me

How to escape schtasks /tr arguments

I need to schedule a PowerShell task like following:
powershell.exe -file ..\Execute\execute.ps1
And I have tried assigning it to an argument $Argument then pass it to schtasks like following:
$Argument = "powershell.exe -file '..\Execute\execute.ps1'"
schtasks /create /tn "SOE_Checks" /tr $Argument /sc DAILY /st 05:00 /ru "System" /rl HIGHEST /f
but after running above code, nothing happened - while the task is created successfully, it appears not to run.
I have also tried assigning it to $Argument without the quotes, it worked but I got the following warnings:
ERROR: Invalid syntax. Value expected for '/tr'.
Type "SCHTASKS /CREATE /?" for usage.
Can anyone please let me know what I have done wrong here? (I am aware that I can accomplish this using PowerShell's New-ScheduledTaskAction but I want it to work this way)
Just want to add that if I change the file path to a specific location in $Argument like this, $Argument = "powershell.exe -file 'C:\SOE\Execute\execute.ps1'", it works fine without any warnings but this is not ideal.
I have read this but it does not work for me
Scheduled tasks created with schtasks.exe execute with the working directory set to $env:windir\system32[1], so unless your script happens to be located in ..\Execute\execute.ps1 relative to there, your command won't work as intended.
If you don't want to hard-code the script path directly into the command, construct the command dynamically, by resolving the relative path to an absolute one when you assign to $Argument:
$Argument = 'powershell.exe -file \"{0}\"' -f (Convert-Path ..\Execute\execute.ps1)
Note the - unfortunate - need to escape the embedded " as \", which is longstanding bug that hasn't been fixed for the sake of backward compatibility - see this GitHub docs issue for background.
Convert-Path resolves a relative path to an absolute one.
Note that the relative path must refer to an existing file (or directory).
Similarly, relative paths inside your script will be relative to $env:windir\system32 too; to make them relative to the script's directory, change to your script's directory first by executing Set-Location $PSScriptRoot at the start of your script.
Optional reading: How to quote commands that run from a scheduled task:
Note: Virtually the same rules apply as when running a command from the Windows Run dialog (press WinKey+R), which you can use to test-drive a command (the command to pass to schtasks /tr, without outer quoting, not the whole schtasks command line) - though note that the working directory will be the user's home directory, and that you won't be able to use '...'-quoting around the PowerShell CLI's -File argument - see below):
cmd.exe is NOT involved during execution, which means:
You needn't worry about non-double-quoted use of cmd.exe metacharacters such as &, for instance, so you can use these characters even in single-quoted strings passed to the PowerShell CLI powershell.exe as (part of) the -Command argument(s).
Conversely, output redirections (e.g., > c:\path\to\log.txt) are not directly supported.
In the context of invoking the PowerShell CLI, this means:
With -File, you cannot use them on the command line and must instead perform them from within your script.
With -Command, however, you can use them, because it is then PowerShell that applies them (but note that Windows PowerShell's > operator creates UTF-16LE files).
(Even though cmd.exe isn't involved) references to environment variables using the same syntax form as in cmd.exe are expanded (e.g., %USERNAME%)
Caveat: You cannot escape such references:
%% doesn't work - the additional % is simply treated as a literal, and expansion still occurs; e.g., %%OS%% results in %Windows_NT%.
^% (accidentally) prevents expansion, but retains the ^ - the ^ doesn't escape; rather, it "disrupts" the variable name, in which case the token is left as-is; e.g., ^%OS^% results in ^%OS^%, i.e., is retained as-is.
The above applies to the commands as they must end up defined inside a scheduled task, as you would see or define them interactively in Task Scheduler (taskschd.msc).
Additionally, for creating a scheduled task from the command line / a PowerShell script / a batch file:
you must quote the command as a whole and
comply with the syntax rules of the calling shell regarding escaping and up-front string interpolation.
(You can only get away without quoting if the command consists of only a single word that needs no escaping, such the path to an executable that contains no spaces or special characters and to which no arguments are passed.)
When calling schtasks.exe[2], quote the /tr argument as a whole as follows:
from PowerShell, use "...", if you need to expand (string-interpolate) the command string up front; otherwise, use '...'.
Important: The need to escape nested " as \" applies in both cases, which in the case of outer "..." quoting means that nested " must be escaped as \`" (sic).
Surprisingly, schtasks.exe recognizes embedded '...' quoting and automatically translates it to "..." quoting - that is why your original command, "powershell.exe -file '..\Execute\execute.ps1'", worked, even though in direct invocation the PowerShell CLI does not support the use of '...' in combination with -File.
from cmd.exe (whether directly or from a batch file), you must use "...".
PowerShell examples:
The following PowerShell commands create and execute two run-once
scheduled tasks, named test1 and test2, that run when the next calendar minute starts, in the context of the calling user, visibly. (You'll have to remove these tasks manually afterwards.)
You may have to wait for up to 1 minute to see the invocation kick in, at which point a new console window pops up for each task.
# Create sample script test.ps1 in the current dir. that
# echoes its arguments and then waits for a keypress.
'"Hi, $Args."; Read-Host "Press ENTER to exit"' > test.ps1
# Find the start of the next calendar minute.
$nextFullMinute = ([datetime]::Now.AddMinutes(1).TimeOfDay.ToString('hh\:mm'))
# -File example:
# Invoke test.ps1 and pass it 'foo' as an argument.
# Note the escaped embedded "..." quoting around the script path
# and that with -File you can only pass literal arguments at
# invocation time).
schtasks.exe /create /f /tn test1 /sc once /st $nextFullMinute `
/tr "powershell -File \`"$PWD/test.ps1\`" foo" #`# (dummy comment to fix broken syntax highlighting)
# -Command example:
# Invoke test.ps1 and pass it $env:USERNAME as an argument.
# Note the '...' around the script path and the need to invoke it with
# &, as well as the ` before $env:USERNAME to prevent its premature expansion.
schtasks.exe /create /f /tn test2 /sc once /st $nextFullMinute `
/tr "powershell -Command & '$PWD/test.ps1' `$env:USERNAME"
"Tasks will execute at ${nextFullMinute}:00"
[1] Note that the Task Scheduler GUI allows you to configure a working directory, but this feature isn't available via the schtasks.exe utility.
[2] The same applies to values passed to the -Argument parameter of the New-ScheduledTaskAction PowerShell cmdlet, though note that the executable name/path is specified separately there, via the -Execute parameter.
By contrast, the Register-ScheduledJob cmdlet for creating scheduled PowerShell jobs accepts a script block as the command to run, which eliminates the quoting headaches.

How to have powershell script execute batch command in same terminal

I have a powershell 2 script that I'm trying to develop. The purpose of this script is to wrap around a batch script and intelligently choose what version of said batch script to run. Somewhere along the lines in my code I have some logic that goes like this:
& $myCommand $args
$myCommand is the fully qualified filename of the batch file I want to run. $args is the args passed into this script. This works except it opens up a command window when running $myCommand. How do I prevent this so that the output is within the same powershell shell?
What's odd is that if I execute the command directly, it shows up the way I want it. So something like:
C:\myCommand.bat $args
Given that I need to choose which command I want to run at runtime, how do I make it so the output is in the same shell when I use the '&' to execute the command in the variable? Thanks!
Use Start-Process with the -NoNewWindow parameter instead of &:
Start-Process -filepath C:\myCommand.bat -argumentList #("arg1","arg2") -NoNewWindow

How to propagate windows environment variable for powershell run from cygwin

I have installed cygwin and I am about to execute a script written in powershell. I do it like this:
powershell "& ""C:\my\script.ps1"""
This works as expected, I have to do this that way because in that script I am executing another external command and so on ...
I would like to add some environment variable to that script, so I would like to write something like
powershell "$FOO="bar"; & ""C:\my\script.ps1"""
so I can then access $FOO variable in the script and do something with it. The idea is that if that variable is not defined, I use some default value. I know that this could be also achieved with some environment variables or I could put these variables to the profile (profile.ps1) but I want to get rid of that file so I need none and I can override the default value with the variables as I showed.
but is says that:
=bar : The term '=bar' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path
was included, verify that the path is correct and try again.
At line:1 char:1
So I was thinking that something like this could work:
powershell { $web = "google.com" ; & ping.exe $web }
But it works only in powershell console and not in cygwin, it cygwin it says that
-bash: syntax error near unexpected token `&'
So it seems like that & is treaten as bash character. I tried to escape it in thousand ways, e.g.
bash -c "'powershell { \$web = \"google.com\" ; & ping.exe \$web }'"
But this is the output
bash: powershell { $web = "google.com" ; & ping.exe $web }: command not found
Thank you for a hint.
UPDATE
I am able to do this:
powershell "Invoke-Command -ScriptBlock {\$env:FOO = \"google.com\" ; & ""C:\my\script.ps1"" }"
But when I am trying to access that FOO variable throught $env:FOO it is empty, it seems like I am unable to do so because that script is running in another scope or what ...
this command will pass an environment variable from cygwin ($FOO="bar") to powershell, then run another powershell command (dir env:) which lists the environment variables (proving that it was set):
powershell "\$env:FOO=\"bar\";dir env:"
replace the dir env: part with your script call and this should work for you.
edit: here's the command (quoted slightly differently) including the call to an external script:
powershell '$env:FOO="bar";& "C:\script name.ps1"'

Resources