I want to get the Output of my Ip Address only inside the Anaconda Powershell in Windows.
curl https://ipinfo.io/ip
Gives me the following:
StatusCode : 200
StatusDescription : OK
Content : 22.031.123.456
RawContent : HTTP/...
...
...
...
...
Content-Type: text/html; charset=utf-8
Date: ...
Via:...
Forms : {}
Headers : {[..., *], [...], [...],
[..., ...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : ...
RawContentLength : ...
As you can see, "Content" shows my IP Adress. But I can't get the IP-Address without any other informations.
Note: "curl api.ipify.org" and "curl ipinfo.io/ip" and others are doing the exact same thing.
In Windows PowerShell, curl does not refer to the external curl.exe program; instead, it is a built-in alias for PowerShell's Invoke-WebRequest cmdlet.
In order to invoke curl.exe, include the filename extension, .exe:
curl.exe ipinfo.io/ip # Note: The https:// part isn't strictly needed.
Note that this is no longer necessary in PowerShell (Core) v6+, where the curl alias has been removed, along with others, so as not to shadow external programs that come with the operating system.
Also note that you could also have used the Invoke-RestMethod cmdlet, which - unlike Invoke-WebRequest, which returns a wrapper object around the actual data - returns the response data directly (and, as applicable, parses XML data into [xml] instances and JSON data into [pscustomobject] instances); its built-in alias is irm:
irm ipinfo.io/ip # irm == Invoke-RestMethod
As a general tip: To determine which command (a PowerShell-native one or an external program) a given name such as curl refers to, use the Get-Command cmdlet.
By default, the effective command by that name will be listed.
If you add the -All switch, you'll potentially see shadowed command forms.
On Windows, if an external program is shadowed, you can still invoke it by excluding the .exe extension, as shown.
Generally, if a shadowed command is of a different type than the effective command, you can use the Get-Command's -Type parameter to target the shadowed command and invoke it via &, the call operator; e.g., the (less desirable) alternative to appending .exe on Windows would be:
& (Get-Command -Type Application curl) ipinfo.io/ip
Related
I'm getting the list of installed Microsoft Store apps with this command:
Get-AppxPackage -AllUsers
And then I try to open an app:
powershell -Command "Start-Process 'C:\Program Files\WindowsApps\Microsoft.Windows.Photos_2021.21070.22007.0_x64__8wekyb3d8bbwe\Microsoft.Photos.exe' -Verb runAs"
I get an access error:
This command cannot be run due to the error: Access is denied.
# Use the URI scheme of the Microsoft.Photos application.
# Note: Unfortunately, -Wait does *not* work in this case.
Start-Process ms-photos:
# Wait for the process to exit (from what I can tell there's only ever 1
# Microsoft.Photos process).
# The relevant process name was obtained with: Get-Process *Photos*
(Get-Process Microsoft.Photos).WaitForExit()
Note: That Start-Process -Wait (and -PassThru) cannot be used with at least some Microsoft Store applications is unfortunate; the problem has been reported in GitHub issue #10996.
Using a URI protocol scheme such as ms-photos: is the simplest approach, although discovering a given Microsoft Store's application's protocol(s) is non-trivial - see this answer, which provides a helper function, Get-AppXUriProtocol, which builds on the standard
Get-AppXPackage cmdlet; e.g.:
# Note:
# * Requires custom function Get-AppXUriProtocol from the linked answer.
# * Must be run in *Windows PowerShell*, because the AppX module
# isn't supported in PowerShell (Core), as of v7.1.
PS> Get-AppXUriProtocol *Photos* | Format-List
PackageFullName : Microsoft.Windows.Photos_2021.21070.22007.0_x64__8wekyb3d8bbwe
Protocols : {ms-wcrv, ms-wpdrmv, ms-photos, microsoft.windows.photos.crop...}
As you can see, the Microsoft Photos application has several protocol schemes associated with it, but the obvious candidate for simply launching the application is ms-photos:, which indeed works.
Launching Microsoft Store applications that do not have a URI protocol scheme defined:
If a given application doesn't define a URI protocol scheme, you must - somewhat obscurely - launch it via its AppId (application ID), and the general shell: URI protocol scheme and the virtual AppsFolder shell folder; e.g., to launch Calculator:
Start-Process shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App
Finding an application's AppID:
In Windows 10 and above, you can now use the Get-StartApps cmdlet to list all installed AppX applications or search by (part of) their display name, which reports their AppIDs. Thus, if you know an application's full display name, e.g., Photos, you can launch it as follows:
Start-Process "shell:AppsFolder\$((Get-StartApps Photos | Select-Object -First 1).AppId)"
Note: The reason for Select-Object -First 1 is that even specifying the exact display name can result in multiple results, such as for Microsoft Edge.
If you're unsure of the full display name, you can use a substring to find matching applications; e.g.. Get-StartApps edge
In older versions, you must determine the AppID manually, which is quite cumbersome, unfortunately: read on.
The AppID is composed of the family package name (e.g. Microsoft.MicrosoftEdge_8wekyb3d8bbwe) followed by ! and a package-internal identifier, which is typically - but not always !App; two notable exceptions:
Spotify requires !Spotify (as you've discovered yourself).
Microsoft Edge uses !MicrosoftEdge (note, however, that Edge does have a URI protocol for lauching, microsoft-edge: and that there's also a simpler AppID, MSEdge, though it doesn't support passing arguments (see below)).
As you have discovered yourself, the suffix is the package-internal application ID, which is defined in an application's manifest file, appxmanifest.xml, located in the app-specific subfolder underneath $env:Programfiles\WindowsApps; note that a manifest can contain multiple application IDs, as is indeed the case for Microsoft Photos:
# Run in *Windows PowerShell*.
# For Microsoft Photos, the following application IDs are reported:
# 'App', 'SecondaryEntry'
$appManifestPath = (Get-AppxPackage *Photos*)[-1].InstallLocation + '\appxmanifest.xml'
(
Select-Xml '//ns:Application' $appManifestPath `
-Namespace #{ ns='http://schemas.microsoft.com/appx/manifest/foundation/windows10' }
).Node.Id
It's fair to assume for launching the application interactively that the first entry must be used, though I'm not clear on whether there are official rules.
To demonstrate the technique using Microsoft Photos:
# Get the package family name (the assumption here is that only *1* package matches).
# Note: While you must run the Get-AppXPackage from *Windows PowerShell*
# you can use the resulting package name to launch the application
# from PowerShell (Core) too.
$packageFamilyName = (Get-AppXPackage *Photos*).PackageFamilyName
Start-Process "shell:AppsFolder\$packageFamilyName!App"
Note that since the executable is then launched indirectly, the actual target process (Microsoft.Photos in this case) isn't guaranteed to exist yet when Start-Process returns, so more work is needed to first wait for it to come into existence, and then wait for it to exit.
In the simplest - but not fully robust - case, insert a Start-Sleep command, to sleep as long as you would expect creation of the target process to take at most (the actual timing varies with system load):
Start-Process "shell:AppsFolder\$packageFamilyName!App"
Start-Sleep -Seconds 5 # Wait for the Microsoft.Photos process to be created.
(Get-Process Microsoft.Photos).WaitForExit()
A fully robust approach would require more work.
Passing arguments to Microsoft Store applications:
With the general "shell:AppsFolder\$appId" approach, you can seemingly pass argument as usual, via Start-Process' -ArgumentList (-Args) parameter; e.g., with Microsoft Edge (run from Windows PowerShell - if you have an older Edge version, replace !App with `!MicrosoftEdge:
# Starts Microsoft Edge and opens the specified URLs.
# Note: Curiously, this does NOT work with the simpler "MSEdge" AppID.
Start-Process `
shell:AppsFolder\Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge `
-Args 'http://example.org https://wikipedia.org'
With the app-specific URI-scheme approach, argument(s) must be passed as part of the URI (-ArgumentList is ignored):
Caveat: It is unclear to me how you can pass multiple arguments and, generally, whether there is a standardized method across applications to embed arguments in the URI.
For instance, Microsoft Edge seems to accept only one argument: the URL of a site to open. Anything after that one URL is seemingly interpreted as a part of that one URL:
# Starts Microsoft Edge an opens the specified URL.
Start-Process 'microsoft-edge:https://en.wikipedia.org?search=wikipedia'
I want to get the Output of my Ip Address only inside the Anaconda Powershell in Windows.
curl https://ipinfo.io/ip
Gives me the following:
StatusCode : 200
StatusDescription : OK
Content : 22.031.123.456
RawContent : HTTP/...
...
...
...
...
Content-Type: text/html; charset=utf-8
Date: ...
Via:...
Forms : {}
Headers : {[..., *], [...], [...],
[..., ...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : ...
RawContentLength : ...
As you can see, "Content" shows my IP Adress. But I can't get the IP-Address without any other informations.
Note: "curl api.ipify.org" and "curl ipinfo.io/ip" and others are doing the exact same thing.
In Windows PowerShell, curl does not refer to the external curl.exe program; instead, it is a built-in alias for PowerShell's Invoke-WebRequest cmdlet.
In order to invoke curl.exe, include the filename extension, .exe:
curl.exe ipinfo.io/ip # Note: The https:// part isn't strictly needed.
Note that this is no longer necessary in PowerShell (Core) v6+, where the curl alias has been removed, along with others, so as not to shadow external programs that come with the operating system.
Also note that you could also have used the Invoke-RestMethod cmdlet, which - unlike Invoke-WebRequest, which returns a wrapper object around the actual data - returns the response data directly (and, as applicable, parses XML data into [xml] instances and JSON data into [pscustomobject] instances); its built-in alias is irm:
irm ipinfo.io/ip # irm == Invoke-RestMethod
As a general tip: To determine which command (a PowerShell-native one or an external program) a given name such as curl refers to, use the Get-Command cmdlet.
By default, the effective command by that name will be listed.
If you add the -All switch, you'll potentially see shadowed command forms.
On Windows, if an external program is shadowed, you can still invoke it by excluding the .exe extension, as shown.
Generally, if a shadowed command is of a different type than the effective command, you can use the Get-Command's -Type parameter to target the shadowed command and invoke it via &, the call operator; e.g., the (less desirable) alternative to appending .exe on Windows would be:
& (Get-Command -Type Application curl) ipinfo.io/ip
How can I create a protocol handler for a powershell script and make the target powershell script receive command line arguments?
And what are the security concerns in doing so?
I thought I write up a decent guide on doing so since the information I found online was lacking some details.
https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa767914(v=vs.85)
First off the security concerns
Any program, website, script etc. that is running on your computer can set off the protocol. There are no authorization checks.
You should NOT create a universal protocol handler. That would be a massive security issue concern. I mean that would enable a program, website, script etc. to run any powershell script or command on your computer.
Creating the protocol handler in Windows registry
The protocol must be registered in Windows Registry. It's a simple task.
I'm calling my powershell protocol handler for pwsh
Step 1: Open the Registry Editor and navigate to
Computer\HKEY_CLASSES_ROOT
For inspiration you can look at Computer\HKEY_CLASSES_ROOT\http to look at how that protocol handler is made.
Step 2: Create the following hierarchy:
Create the key pwsh: [Computer\HKEY_CLASSES_ROOT\pwsh]
Edit the default value of (Default) to URL:pwsh. Remember I call my protocol handler for pwsh, write whatever your is called.
Add a string value with the name URL Protocol and empty data.
It should look like this now:
Create a new key under pwsh, DefaultIcon: Computer\HKEY_CLASSES_ROOT\pwsh\DefaultIcon.
Set the (Default) data field to a filepath that leads to an icon or image. I used the powershell icon for Powershell 7 C:\Program Files (x86)\PowerShell\7-preview\assets\ps_black_32x32.ico.
Then create the keys shell -> open -> command like shown on the image above.
In the key command change the (Default) data value to where powershell is installed and then the powershell script to be run.
When testing I do this: "C:\Program Files\PowerShell\6\pwsh.exe" -noexit -executionpolicy bypass -Command {Write-Host %1}
Note I am using powershell core 6 and your path to powershell is probably different.
You can test to check if it works by opening the run program in Windows(Windows+R).
Expected behavior is the powershell window to open with the text pwsh:Hello Stackoverflow printed.
Step 3: Create a powershell script to handle incoming actions on the protocol.
The production ready data value for the command key: "C:\Program Files\PowerShell\6\pwsh.exe" -noexit -File C:\handleActions.ps1 %1
Param($Argument="") # If the protocol is ran you always at least get the protocol name as an argument. (if using the %1)
[String]
$Argument
function Handle-Actions { # The cmdlet 'Handle-Actions' uses an unapproved verb.
[cmdletBinding()]
param(
[Parameter(Mandatory=$false, Position=0)]
[String]
$Argument
)
$Argumuments = $Argument.Split([Char]0x003F) # Splits by `?`
#Argumnets is now in an array, do whatever you need to next.
$Argumuments | %{
Write-Host $_ # Writes each argument that was seperated by ? to a line
}
}
Handle-Actions -Argument $Argument
Given the run command pwsh:?firstArgument?SecondArgument the script will output:
pwsh:
firstArgument
SecondArgument
To complement your helpful guide with sample code that automates creation of a custom protocol handler:
The following:
Creates a custom URI protocol custom: (rather than pwsh:, given that PowerShell is simply used to implement the protocol) to which an open-ended number of arguments can be passed.
Does so for the current user only (HKEY_CURRENT_USER\Software\Classes) by default; however, it's easy to tweak the code to implement the custom protocol for all users instead (HKEY_LOCAL_MACHINE\Software\Classes), though you'll need to run the code with elevation (as administrator) then.
A handler *.ps1 script is automatically created:
At $env:USERPROFILE\customUriHandler.ps1 in the current-user scenario.
At $env:ALLUSERPROFILE\customUriHandler.ps1 in the all-users scenario.
The handler script simply echoes the arguments passed to it, and it is invoked in a PowerShell script window that is kept open after script execution (-NoExit); tweak the PowerShell command as needed.
The protocol expects its arguments as if it were a shell command, i.e., as a space-separated list of arguments, with argument-individual "..." quoting, if necessary.
The sample command at the end uses Start-Process to invoke the following URI, which you could also submit from the Run dialog (WinKey-R), which passes arguments one, two & three, four:
URI: custom:one "two & three" four
Invocation via Start-Process: Start-Process 'custom:one "two & three" four'
Caveat: If you submit this URI via a web browser's address bar (note: doesn't seem to work with Microsoft Edge), it is URI-escaped, and a single one%20%22two%20&%20three%22%20four argument is passed instead, which would require custom parsing; similarly, submitting from File Explorer's address bar passes one%20two%20&%20three%20four, though note that the " chars. are - curiously - lost in the process.
# Determine the scope:
# Set to $false to install machine-wide (for all users)
# Note: Doing so then requires running with ELEVATION.
$currentUserOnly = $true
if (-not $currentUserOnly) {
net session *>$null
if ($LASTEXITCODE) { Throw "You must run this script as administrator (elevated)." }
}
$ErrorActionPreference = 'Stop'
# The name of the new protocol scheme
$schemeName = 'custom'
$pwshPathEscaped = (Get-Process -Id $PID).Path -replace '\\', '\\'
$handlerScript = ($env:ALLUSERSPROFILE, $env:USERPROFILE)[$currentUserOnly] + "\${schemeName}UriHandler.ps1"
$handlerScriptEscaped = $handlerScript -replace '\\', '\\'
# Create the protocol handler script.
#'
# Remove the protocol scheme name from the 1st argument.
$argArray = $args.Clone()
$argArray[0] = $argArray[0] -replace '^[^:]+:'
# If the 1st argument is now empty, remove it.
if ('' -eq $argArray[0]) { $argArray = $argArray[1..($argArray.Count-1)] }
"Received $($argArray.Count) argument(s)."
$i = 0
foreach ($arg in $argArray) {
"#$((++$i)): [$arg]"
}
'# > $handlerScript
# Construct a temp. *.reg file.
# Target the scope-appropriate root registrykey.
$rootKey = ('HKEY_LOCAL_MACHINE\Software\Classes', 'HKEY_CURRENT_USER\Software\Classes')[$currentUserOnly]
# Determine a temp. file path.
$tempFile = [IO.Path]::GetTempPath() + [IO.Path]::GetRandomFileName() + '.reg'
#"
Windows Registry Editor Version 5.00
[$rootKey\$schemeName]
#="URL:$schemeName"
"URL Protocol"=""
[$rootKey\$schemeName\DefaultIcon]
#="$pwshPathEscaped"
[$rootKey\$schemeName\shell]
#="open"
[$rootKey\$schemeName\shell\open\command]
; === Tweak the PowerShell command line here: ===
#="\"$pwshPathEscaped\" -ExecutionPolicy Bypass -NoProfile -NoExit -File \"$handlerScriptEscaped\" %1"
"# > $tempFile
# Import the *.reg file into the registry.
& {
$ErrorActionPreference = 'Continue'
reg.exe import $tempFile 2>$null
if ($LASTEXITCODE) { Throw "Importing with reg.exe failed: $tempFile"}
}
# Remove the temp. *.reg file.
Remove-Item -ErrorAction Ignore -LiteralPath $tempFile
# ---
# Sample invocation of the new protocol with 3 arguments:
$uri = "$schemeName`:one `"two & three`" four"
Write-Verbose -Verbose "Invoking the following URI: $uri"
Start-Process $uri
What I am trying to do is download 2 images from URL's and open them after download. Here's what I have:
#echo off
set files='https://cdn.suwalls.com/wallpapers/cars/mclaren-f1-gtr-42852-400x250.jpg','http://www.dubmagazine.com/home/media/k2/galleries/9012/GTR_0006_EM-2014-12-21_04_GTR_007.jpg'
powershell "(%files%)|foreach{$fileName='%TEMP%'+(Split-Path -Path $_ -Leaf);(new-object System.Net.WebClient).DownloadFile($_,$fileName);Invoke-Item $fileName;}"
Im getting 'Cannot find drive' A drive with the name 'https' cannot be found.
It's the Split-path command that is having problems but cant seem to find a solution.
You could get away with basic string manipulation but, if the option is available, I would opt for using anything else that is data aware. In your case you could use the [uri] type accelerator to help with these. I would also just opt for pure PowerShell instead of splitting between batch and PS.
$urls = 'https://cdn.suwalls.com/wallpapers/cars/mclaren-f1-gtr-42852-400x250.jpg',
'http://www.dubmagazine.com/home/media/k2/galleries/9012/GTR_0006_EM-2014-12-21_04_GTR_007.jpg'
$urls | ForEach-Object{
$uri = [uri]$_
Invoke-WebRequest $_ -OutFile ([io.path]::combine($env:TEMP,$uri.Segments[-1]))
}
Segments will get you the last portion of the url which is a proper file name in your case. Combine() will build the target destination path for you. Feel free to add you invoke item logic of course.
This also lacks error handling if the url cannot be accessed or what not. So be aware of that possibility. The code above was meant to be brief to give direction.
I am sending a short SSL command to Twilio using Xojo.
On the Mac, cURL is built-in and so it is simple to execute a shell command.
Since cURL is not installed on Windows, is there one version of cURL that will run on all Windows versions (or does my app installer, Inno, need to determine with Windows OS is running and install that specific version)?
Any advice as to what files need to be included would be greatly appreciated.
Once installed, how would the cURL command on the Windows machine differ from the Mac?
Thanks.
After some time searching, I did finally come up with a solution.
Install cURL - downloaded from here with SSL.
If you download the installer file (with the .msi extension), it creates a C:\Program Files\curl directory and extracts the files to that directory. You can place the files in whatever directory you choose, just so long as you reference it properly.
In Xojo, you create a shell command similar to the code here:
Dim sh As New Shell
Dim command As String
command = """C:\Program Files\curl\bin\curl"" -X POST https://myurl.com"
sh.Execute(command)
Note that C:\Program Files\curl\bin\curl is within double-quotes. This is because there is a space within "Program Files". If you do not place the address within double-quotes, everything after the space is considered a parameter and will generate an error.
IMPORTANT NOTE: Windows does not recognize the single-quote, so all single-quotes need to be replaced with double-quotes.
In addition, on the Mac OS, the "-" character needs to be escaped with a backslash "-". These backslashes need to be removed when translating the cURL command from Mac to Windows.
You shouldn't need cURL to send a POST. Take a look to the POST method of the HTTPsocket object.
From the official Xojo documentation:
This example does a synchronous POST to a service that returns what you POSTed to it as JSON:
Dim d As New Dictionary
d.Value("Test") = "TestValue"
d.Value("Value2") = "Testing"
Socket.SetFormData(d)
// This service simply returns the post data as the result
Dim result As String
result = Socket.Post("http://httpbin.org/post", 30) // Synchronous
result = DefineEncoding(result, Encodings.UTF8)
MsgBox(result)
To do this asynchronous, call Post without a timeout:
Dim d As New Dictionary
d.Value("Test") = "TestValue"
d.Value("Value2") = "Testing"
Socket.SetFormData(d)
// This service simply returns the post data as the result
Socket.Post("http://httpbin.org/post")
The results will be available in the PageReceived event handler in the content parameter.
Sub PageReceived(url As String, httpStatus As Integer, headers As InternetHeaders, content As String)
Dim data As String
data = DefineEncoding(content, Encodings.UTF8)
ResultArea.Text = data
End Sub