Still new to PowerShell and have been creating automation scripts, checks and balances and anything I can think of to make our jobs easier day to day. The contract we are working on are extremely tight when it comes to applications and forbids opensource\freeware applications. So I have been working on using powershell to do the job.
Here is what I have so far:
1. Created HKCR\Directory\Shell\powershell (default) Reg_sz Search Folder Text & Log files
2. Created HKCR\Directory\Shell\powershell\command (default) Reg_sz
C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -file c:\temp\RightClickSearch.ps1 -NoExit -Command Set-Location -LiteralPath '%L'
I was able to get the right click to launch and the script opens up and prompts me for my keyword to search. Problem is, I can't seem to figure out how to pass the location to the script properly.
RightClickSearch.ps1 (I know the $path isn't set, before it was hardcoded and I know I have to pass a variable to it from the menu)
$promt = (Read-Host -Prompt "Enter Search Keyword")
Get-ChildItem -Path $Path -Include *.txt, *.log -Recurse | Select-String -Pattern $promt | Format-table -AutoSize -Property LineNumber,Filename,Path
Pause
There are two problems with your call to powershell.exe:
You can't specify both the -File and -Command parameters. You can only specify one of them.
Whichever one you do specify, it has to be the last parameter in the command. In your example, the -NoExit and -Command parameters would be ignored. (Type powershell.exe -? for an explanation.)
The good news is that PowerShell scripts themselves can accept arguments by using the param keyword. Simply declare the parameter at the top of your script:
param ($Path)
$promt = (Read-Host -Prompt "Enter Search Keyword")
Get-ChildItem -Path $Path -Include *.txt, *.log -Recurse | Select-String -Pattern $promt | Format-table -AutoSize -Property LineNumber,Filename,Path
Pause
You call it from the command line like this:
C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -File c:\temp\RightClickSearch.ps1 -Path '%L'
Since $Path is the one and only parameter, you don't even have to specify its name:
C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -File c:\temp\RightClickSearch.ps1 '%L'
Ironically, you could use the -Command parameter in exactly the same way. The only difference is that your script file will not be dot-sourced, but that won't matter in the example you gave.
Related
I am struggling with a PowerShell script to return .json file types in all users %APPDATA% folders. When I run it in ISE, it returns "Get-ChildItem : Access is denied" and when I run ISE as admin, no output is returned.
Here is the script I am working with:
Get-ChildItem C:\Users\*\AppData\Roaming -Filter '*.json' -File -Recurse -Force -ErrorAction SilentlyContinue |
ForEach-Object {
Write-Host $('.json found in {1}' -f $_.Name, ([System.IO.Path]::GetDirectoryName($_.FullName)))
}
Any help would be greatly appreciated!
tl;dr
It is the use of the -File switch that prevents your command from working - see next section.
Given that you're filtering by *.json and that directories are unlikely to match that pattern, you can simply omit it.
If you do need to guard against that, insert a Where-Object { -not $_.PSIsContainer } pipeline segment after the Get-ChildItem call; in PowerShell (Core) 7+, you can simplify to Where-Object -Not PSIsContainer
# Do NOT use -File
# Note: Run from an ELEVATED session (as admin), which is necessary to
# access other users' directories.
Get-ChildItem C:\Users\*\AppData\Roaming -Filter '*.json' -Recurse -Force -ErrorAction SilentlyContinue |
ForEach-Object { '{0} found in {1}' -f $_.Name, $_.DirectoryName }
The reason that Get-ChildItem's -File doesn't work in your case is that you're combining it with a -Path value that is an actual wildcard expression rather than a literal path:
The wildcard expression is resolved first and - perhaps surprisingly - if directories are among the matching paths, they are returned as themselves, rather than listing their children (what is inside them); the latter only happens with literal input paths.
In your case, only directories match and the -File switch is therefore applied to them, not to their children. Since directories don't match the -File switch (by definition aren't files), there is no output.
Whether or not the -Recurse switch is also present then makes no difference, given that there's nothing to recurse into.
As an aside:
If you wanted to make your command work without -Recurse, -File can be made to work, but only if you append \* to your input wildcard (the positionally implied -Path argument), so as to force enumeration of the children to match the -File and the -Filter against:
# Without recursion and a trailing /*, -File works
Get-ChildItem C:\Users\*\AppData\Roaming\* -File -Filter '*.json'
However, since PowerShell then needs to enumerate all children first, it is then simpler and more efficient to omit -Filter and append its pattern directly to the input wildcard:
# No -Filter, pattern appended to input path
Get-ChildItem C:\Users\*\AppData\Roaming\*.json -File
I'm playing with malware in a VM and every script I try gets stuck. Basically I need to run every .exe in a folder. Tried batch files using start, powershell, etc. The issue happens when AV moves some file to quarentine, or some process keep running then the script doesn't jump to the next one.
CMD start works but shows popups when doesn't find some file, then you have to keep clicking to jump to the next file.
These works but get stuck after a while:
Get-ChildItem 'C:\Users\LAB\Desktop\test' | ForEach-Object {
>> & $_.FullName
>> }
Same here:
for %%v in ("C:\Users\LAB\Desktop\test\*.exe") do start "" "%%~v"
and here:
for %%i in (C:\Users\LAB\Desktop\test\*.exe) do %%i
You need to provide some form of code to allow us to help you troubleshoot it; this is not a request a script page.
Anyways, you would be looking at something like this:
#Assuming the .exe's are located in C Root.
Get-ChildItem -Path C:\ | Where-Object {$_.Extension -like ".exe"}| Foreach {Start-Process $_.FullName}
#In Ps, we like to filter as far left as possible for faster results.
Get-ChildItem -Path C:\ -File "*.exe" | Foreach {Start-Process $_.FullName}
#Running the commands as jobs so it doesnt wait on any to finish before running the next.
Start-Job { Get-ChildItem -Path C:\ -File "*.exe" | Foreach {Start-Process $_.FullName} }
Start-Sleep 2
Get-Job | Remove-Job
Please refer to the following link: How to ask a question
I have a few scripts that I need to run against a dozen folders all with a relative path. I'm trying to solve this with a master script to run for each folder in that path, one folder at a time. The folders are all children of the "here" folder in the below path. I can't seem to get the syntax right, but I think I'm close :)
Is there a more efficient way to run a script against the contents of every folder in a directory, one folder at a time?
$pdfFolder = 'C:\path\to\folders\here'
$Completed = Get-ChildItem $pdfFolder -Recurse
ForEach-Object ($Completed){
Invoke-Expression -Command "C:\path\where\scriptis\script.ps1"
}`
$pdfFolder = 'C:\path\to\folders\here'
# Get all subfolders - note the -Directory switch (PSv3+)
$Completed = Get-ChildItem $pdfFolder -Recurse -Directory
# Pipe the subfolders to ForEach-Object, invoke the
# script with & (avoid Invoke-Expression), and pass the subfolder
# at hand as an argument.
$Completed | ForEach-Object {
& "C:\path\where\scriptis\script.ps1" $_
}
As for what you tried:
Get-ChildItem $pdfFolder -Recurse
This command returns not just folders (directories), but also files. To limit the output to folders, pass switch -Directory (PSv3+).
ForEach-Object ($Completed) { ... }
You're confusing the syntax of the foreach loop with the syntax of the pipeline-based ForEach-Object cmdlet.
The cmdlet expects input from the pipeline, so you must use $Completed | ForEach-Object { ... } instead.
Also note that unless you truly need to collect all subfolders in an array first, you can simply pipe your Get-ChildItem call directly to ForEach-Object.
Invoke-Expression -Command "C:\path\where\scriptis\script.ps1"
Invoke-Expression should be avoided, because it is rarely the right tool and can be a security risk.
All you need to invoke a script by its quoted and/or stored-in-a-variable file path is to use &, the call operator, as shown above.
I want to move the file "file_to_move.txt"in each folder to their respective "done"-folder.
so the file_to_move.txt in C:\Temp\test\folder1 is moved to C:\Temp\test\folder1\done
and file_to_move.txt in C:\Temp\test\folder2 is moved to C:\Temp\test\folder2\done
...and so on, preferably with a %date%_%time% added to the file-name.
if a folder (like folder4 in the example below) does not have a file_to_move.txt, the script should just ignore it and move on.
folder structure example:
C:\Temp\test\DONE
C:\Temp\test\folder1
C:\Temp\test\folder1\done
C:\Temp\test\folder1\some_other_folder
C:\Temp\test\folder1\some_other_file.txt
C:\Temp\test\folder1\file_to_move.txt
C:\Temp\test\folder2
C:\Temp\test\folder2\done
C:\Temp\test\folder2\some_other_folder
C:\Temp\test\folder2\some_other_file.txt
C:\Temp\test\folder2\file_to_move.txt
C:\Temp\test\folder3
C:\Temp\test\folder3\done
C:\Temp\test\folder3\some_other_folder
C:\Temp\test\folder3\some_other_file.txt
C:\Temp\test\folder3\file_to_move.txt
C:\Temp\test\folder4
C:\Temp\test\folder4\done
C:\Temp\test\folder4\some_other_folder
C:\Temp\test\folder4\some_other_file.txt
I have experimented with a Powershell script even if I'm not very good at it and I dont know it can be done in a standard batch-script.
I have tried this so far:
In a batch-script:
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%bin\movescript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'"
in the movescript.ps1:
Move-Item C:\Temp\test\*\file_to_move.txt C:\Temp\test\*\done\file_to_move_$(get-date -f yyyyMMdd_HHmmss).txt
But this is not working.
I guess it's not precise enough to work.
As a bonus, can the whole thing be done within the basic script or must we use the external .PS1-file?
You can use the Get-ChildItem cmdlet with a filter to retrieve all file_to_move.txt files recursively from a path. Use the Foreach-Object (alias foreach) to iterate over them and combine the new path using the Join-Path cmdlet. To Copy the Item, you can use the Copy-Item cmdlet:
$itemsToCopy = Get-ChildItem -Path c:\Temp\Test -Filter file_to_move.txt -Recurse
$itemsToCopy | foreach {
$newPath = Join-Path $_.DirectoryName 'done'
New-Item -Path $newPath -ItemType directory -Force | out-null
$_ | Copy-Item -Destination $newPath
}
If you want to add a Timestamp, you could use the Get-Date cmdlet and invoke the ToString method with your desired format on it, example:
(Get-Date).ToString("yyyy-dd-M_HH-mm-ss")
Output:
2016-05-4_15-06-02
You can now concat the filenames using a format string and the $_.Basename and $_.Extension property within your foreach loop. I will leave this as an exercise to you.
I have to read each file in a folder and determine the length of the first line of each, then do something depending on whether or not that length is what is should be in a table. I can loop through each file in batch and have %%f as the file, but how do I get that length and assign it to a variable?
If there is a way to do this in Powershell using a batch file, that would help, but I would need to know how to call the Powershell from the batch file also.
The simple PowerShell code would look something like this:
param($path)
Get-ChildItem $path -File |
Select FullName,#{Label="1stLineLength";Expression={(Get-Content $_.FullName -First 1).Length}}
So the first argument will be taken as the path of the script. Then to call it from batch I borrow the answer to this SO question.
Powershell.exe -executionpolicy remotesigned -File m:\Scripts\firstlinelength.ps1 "C:\temp"
That will get output like this on console.
FullName 1stLineLength
-------- -------------
C:\Users\mcameron\CleansedBigFile.txt 4
This code assumes that you have at least PowerShell v3.0 for the -First and -File parameter and switch. I would like to think that most batch code can be converted easily to a PowerShell equivalent so if your environment allows you consider converting to the powerful PowerShell.
Some of your question is pretty vague (what table?). But in general, you don't need a batch file at all. PowerShell example:
Get-ChildItem -File | ForEach-Object {
$firstLineLength = (Get-Content $_ | Select-Object -First 1).Length
if ( $firstLineLength -gt $whateverFromTable ) {
...
}
}
Note that the -File parameter of Get-ChildItem doesn't exist before PowerShell v3. For PowerShell v2 and below, you would replace Get-ChildItem -File with Get-ChildItem | Where-Object { -not $_.PSIsContainer }.