How do we implement %* in powershell? [closed] - windows

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
When I try to execute %* inside batch script by using powershell, it gives me an error. I need something to be replaced for %* so that it works for the following code snippet:
%* ^| ForEach-Object { Write-Host $_ ; $filepath = 'loggerfile.log' ; $output_file =$(Get-Date).ToString() + ' ' + $_ ; Write-Output $output_file ^| Out-File -FilePath $filepath -Append -NoClobber}

First, know why this is not valid code:
%* $| ForEach-Object { Write-Host $_ ; $filepath = 'loggerfile.log' ; $output_file =$(Get-Date).ToString() + ' ' + $_ ; Write-Output $output_file ^| Out-File -FilePath $filepath -Append -NoClobber}
Get-Alias -Definition ForEach-Object |
Format-Table -AutoSize
# Results
<#
CommandType Name Version Source
----------- ---- ------- ------
Alias % -> ForEach-Object
Alias foreach -> ForEach-Object
#>
This is why you get...
%*
%*: The term '%*' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
... when you use it.
And this ...
^
... is a termination character
Second, as well as your code is not a one-liner, as I commented above. What you really have is this...
ForEach-Object '*' ^|
ForEach-Object {
Write-Host $PSItem
$filepath = 'loggerfile.log'
$output_file = $(Get-Date).ToString() + ' ' + $PSItem
Write-Output $output_file ^|
Out-File -FilePath $filepath -Append -NoClobber
}
... and this.
ForEach-Object '*' ^
is not valid at all. I am curious as to where you got that from. As well as this...
^|
... as that is not a thing I've ever seen in any PowerShell docs, help files, repo code. This is also not aliases for anything natively.
Did you mean to do this...
$^
which means - The first token of the last command. Note though, it 'Does NOT' refer to the whole command.
The | pipe is of course pass results from the left of it to the right of it. What are you expecting this...
ForEach-Object '*' ^|
...to do? Even if you did, that is not valid and would error off with.
ForEach-Object: You cannot call a method on a null-valued expression.
So, did you mean to do this...
%* $^
... which is still something I've never seen in 10+ years I've been messing with Monad/Windows PowerShell/PowerShell Core.
To know what your code is doing, you take two steps.
Use PSSCriptANalyzer to validate what you are doing according to the PowerShell ruleset
Invoke-ScriptAnalyzer "$PWD\SomeScriptName.ps1"
Trace the execution, so you can see the stack.
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/trace-command?view=powershell-7.1
Trace-Command -Name metadata,parameterbinding,cmdlet -Expression {
%* ^| ForEach-Object { Write-Host $_ ; $filepath = 'loggerfile.log' ; $output_file =$(Get-Date).ToString() + ' ' + $_ ; Write-Output $output_file ^| Out-File -FilePath $filepath -Append -NoClobber}
} -PSHost

Related

Saving results after a for loop in Windows CMD

I have been trying to create a line of code to ping a range of IP addresses, in the windows command prompt, and after it finishes save the results in a text file. I am using a for loop to do the pinging, but I can't figure out how to save the results in a text file.
This is what I am using:
for /l %i in (1,1,64) do #ping 10.39.63.%i -w 1500 -n 1 | find "Reply"
I tried using the following code to save results in a text file, but it only saves the last command performed by CMD:
for /l %i in (1,1,64) do #ping 10.39.63.%i -w 100 -n 1 | find "Reply" >C:\Users\brymed\Desktop\test.txt
I want to keep it simple, so it'd be awesome to use only a line of code, but I am open to suggestions. Thank you.
This is not difficult using PowerShell. The $Hosts variable is a list of IP addresses to ping. The results are written to a file.
$Hosts = #()
foreach ($i in 1..64) { $Hosts += "10.39.63.$i" }
Test-Connection -Count 1 $hosts |
Select-Object -Property Address,BufferSize,Latency,Status |
Out-File -FilePath "$Env:USERPROFILE/Desktop/test.txt" -Encoding ascii
If you -must- run this in cmd.exe, the code can be formatted to do so.
pwsh.exe -NoLogo -NoProfile -Command ^
"$Hosts = #();" ^
"foreach ($i in 1..64) { $Hosts += \"10.39.63.$i\" };" ^
"Test-Connection -Count 1 $hosts -ErrorAction SilentlyContinue |" ^
"Select-Object -Property Address,BufferSize,Latency,Status |" ^
"Out-File -FilePath "$Env:USERPROFILE/Desktop/test.txt" -Encoding ascii"
Get PowerShell Core from https://github.com/PowerShell/PowerShell

Batch file: Combine echos into one line

I want to dump all the file names in a folder without extension into a text file. They should be in one line separated by commas.
So in my folder I have
File1.bin
File2.bin
....
With
(for %%a in (.\*.bin) do #echo %%~na,) >Dump.txt
I got
File1,
File2,
But what I want in the end is a text file with, so one long combined string.
File1,File2,...
I'm kinda stuck here and probably need something else than echo.
Thanks for trying to help.
Try like this:
#echo off
setlocal enableDelayedExpansion
for %%a in (.\*.txt) do (
<nul set /p=%%~nxa,
)
check also the accepted answer here and the dbenham's one.
You could also leverage powershell from a batch-file for this task:
#"%__APPDIR__%WindowsPowerShell\v1.0\powershell.exe" -NoProfile -Command "( Get-Item -Path '.\*' -Filter '*.bin' | Where-Object { -Not $_.PSIsContainer } | Select-Object -ExpandProperty BaseName ) -Join ',' | Out-File -FilePath '.\dump.txt'"
This could probably be shortened, if necessary, to:
#PowerShell -NoP "(GI .\*.bin|?{!$_.PSIsContainer}|Select -Exp BaseName) -Join ','>.\dump.txt"

Delete Credential Manager Stored Credentials at once

I was looking for a more efficient way to delete all credentials stored in Credential Manager without having to delete credentials one by one. After few hours of browsing, I have finally stumbled upon this command prompt string that does exactly what I need:
for /F "tokens=1,2 delims= " %G in ('cmdkey /list ^| findstr Target') do cmdkey /delete %H
Since my knowledge of this syntax is quite limited, I would like to know what each of its section actually means. I already know what cmdkey /list, findstr and cmdkey /delete do but I am not sure about the rest.
Moreover, I would like to know how to make exceptions. For instance, in this case the line deletes all the strings that have a target as displayed in cmdkey list:
cmdkey.exe /list example
What if I want to make an exception and delete only some credentials but not other? Could I do that using the Type value instead of the Target value, for instance by asking the command prompt to delete only the Generic type credentials and not the Generic Certificate and the Domain Password type credentials?
Thanks in advance for your help.
KR,
Andy
When using the string "for /F "tokens=1,2 delims= " %G in ('cmdkey /list ^| findstr Target') do cmdkey /delete %H", I keep getting "CMDKEY: Element not found."
Therefore I scripted a quite primitive workaround since I do not know batch syntax so well. The script below puts together all the lines that have to be run in cmd to delete the corresponding CM keys. The last command in the script, invokes the file with all the lines. You have to just copy/paste them in a cmd and hit enter and it removes most of them:
$cachedCredentials = cmdkey /list
$CMCredentialsTxt = "C:\temp\CMCredentials.txt"
$cachedCredentials | Out-File $CMCredentialsTxt
(Get-Content $CMCredentialsTxt) | ? {$_.trim() -ne "" } | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt).Trim() | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt).Replace("Currently stored credentials:","") | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt).Replace("Type: Generic", "") | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt).Replace("User: App Info","") | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt).Replace("Local machine persistence","") | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt).Replace("Target: ","") | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt).Replace("User: <Certificate>","") | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt).Replace("Certificate","") | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt).Replace("User: User DT","") | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt).Replace("User: User OS Info","") | Set-Content $CMCredentialsTxt
(Get-Content $CMCredentialsTxt) | ? {$_.trim() -ne "" } | Set-Content $CMCredentialsTxt
$cleanTargets = Get-Content $CMCredentialsTxt
Remove-Item $CMCredentialsTxt
forEach ($cleanTarget in $cleanTargets){"cmdkey /delete:" + $cleanTarget | Out-File $CMCredentialsTxt -Append}
Invoke-Item $CMCredentialsTxt

Getting ACL info using PowerShell and Robocopy due to PathTooLongException

I'm trying to get a listing of all permissions on some network folders using PowerShell. Unfortunately I'm encountering the dreaded PathTooLongException so I'm attempting to use Robocopy as a work around. However I'm a complete novice with PowerShell so was hoping for a little help. The easiest command I've come up with is
Get-Childitem "S:\StartingDir" -recurse | Get-Acl | Select-Object path,accestostring | Export-Csv "C:\export.csv"
That works and does what I want except the exception I'm getting. How would I insert Robocopy into this statement to bypass the exception? Any thoughts?
First, create a batch file, such as getShortFilename.bat, with the following lines:
#ECHO OFF
echo %~s1
This will return the short filename of the long filename passed to it. The following script will use that to get the short filename when Get-Acl fails due to a long path. It will then use the short path with cacls to return the permissions.
$files = robocopy c:\temp NULL /L /S /NJH /NJS /NDL /NS /NC
remove-item "c:\temp\acls.txt" -ErrorAction SilentlyContinue
foreach($file in $files){
$filename = $file.Trim()
# Skip any blank lines
if($filename -eq ""){ continue }
Try{
Get-Acl "$filename" -ErrorAction Stop| Select-Object path, accesstostring | Export-Csv "C:\temp\acls.txt" -Append
}
Catch{
$shortName = &C:\temp\getShortFilename.bat "$filename"
$acls = &cacls $shortName
$acls = $acls -split '[\r\n]'
#create an object to hold the filename and ACLs so that export-csv will work with it
$outObject = new-object PSObject
[string]$aclString
$firstPass = $true
# Loop through the lines of the cacls.exe output
foreach($acl in $acls){
$trimmedAcl = $acl.Trim()
# Skip any blank lines
if($trimmedAcl -eq "" ){continue}
#The first entry has the filename and an ACL, so requires extra processing
if($firstPass -eq $true){
$firstPass = $false
# Add the long filename to the $exportArray
$outObject | add-member -MemberType NoteProperty -name "path" -Value "$filename"
#$acl
# Add the first ACL to $aclString
$firstSpace = $trimmedAcl.IndexOf(" ") + 1
$aclString = $trimmedAcl.Substring($firstSpace, $trimmedAcl.Length - $firstSpace)
} else {
$aclString += " :: $trimmedAcl"
}
}
$outObject | add-member -MemberType NoteProperty -name "accesstostring" -Value "$aclString"
$outObject | Export-Csv "C:\temp\acls.txt" -Append
}
}
Notes:
The string of ACLs that is created by Get-Acl is different from the on created by cacls, so whether that's an issue or not...
If you want the ACL string to be in the same format for all files, you could just use cacls on all files, not just the ones with long filenames. It wouldn't be very difficult to modify this script accordingly.
You may want to add extra error checking. Get-Acl could of course fail for any number of reasons, and you may or may not want to run the catch block if it fails for some reason other than the path too long.

Retrieve parameters from file - Windows PowerShell

I am writing a super-easy script in PowerShell. The target of this script is to read a list of server names from a txt file and a command block from another txt file. The result of the operation shold be a third txt file containing the information.
Here some code:
cls
$usr = Read-Host "Please insert username, you'll be asked for password later"
$path = Read-Host "Insert a valid path for ServerList.txt file"
$serverList = Get-Content -Path $path | Out-String
$path = Read-Host "Insert a valid path fom Command.txt file"
$commandBlock = Get-Content -Path $path | Out-String
echo "Command: " $commandBlock "will be executed on " $serverList
echo "Press CTRL+Z to abort or"
pause
Invoke-Command -ComputerName $serverList -ScriptBlock { $commandBlock } -credential $usr
Serverlist.txt is a plain text containing something like "server1,server2,server3" and command.txt contain only this "Get-WmiObject Win32_BIOS | Select-Object SerialNumber"
Why the error is Invoke-Command : One or more computer names are not valid. If you are trying to pass a URI, use the -ConnectionUri parameter, or pass URI objects
instead of strings. ?
I even tried to substitute $serverlist with $serverlist.toString() but it's not working. I read somewhere that in this case $serverlist is an Array, how do I do to make everything work?
Consider that https://technet.microsoft.com/en-us/library/hh849719.aspx Invoke-Commands work with "server1,server2,server3" format if you put the string via console.
Your $serverList isn't a list, it's a single string of server1,server2 etc. To make it into an array, you can use -split to split the string by commas.
$serverList = Get-Content -Path $path | Out-String
$serverList = $serverList -split ","
For further understanding of why this doesn't work as you expect, please see the parsing and command syntax help files:
Get-Help about_Parsing
Get-Help about_Command_Syntax
$serverlist
When your text file contains the line server1,server2,server3, this command:
Get-Content -Path .\file.txt | Out-String
Just results in the string server1,server2,server3 and a newline - that's not a valid hostname.
Either format your text file like this (Get-Content automatically splits on line breaks):
server1
server2
server3
or split the string(s) from the file yourself:
$Serverlist = Get-Content -Path $Path | ForEach-Object { $_ -split "," }
$commandblock
For the command block part to work, you can't just drop a string into a ScriptBlock and expect it to execute - you need to recreate it as executable code:
$Code = Get-Content -Path $path -Raw
$CommandBlock = [scriptblock]::Create($Code)
# Now you can do this
Invoke-Command -ScriptBlock $CommandBlock

Resources