This question shows a Powershell script to generate a list of open File Explorer windows and their path.
My goal is to capture the currently open set of explorer windows, and write out a CMD file with commands like: C:\WINDOWS\explorer.exe /e, "C:\open\this\folder"
So I would like to have the full path and folder name in normal path notation. This is what is showing in titlebar of the Explorer Windows: "C:\open\this\Favorite folder"
The proposed code is:
function Get-WindowTitle($handle) {
Get-Process |
Where-Object { $_.MainWindowHandle -eq $handle } |
Select-Object -Expand MainWindowTitle
}
$app = New-Object -COM 'Shell.Application'
$app.Windows() |
Select-Object LocationURL, #{n='Title';e={Get-WindowTitle $_.HWND}}
As shown above, LocationURL provides a full path in an escaped-URL style:
file:///C:/open/this/Favorite%20%folder"
The #{n='Title';e={Get-WindowTitle $_.HWND}} component produces a column "Title" which is truncated to 5 characters:
C:\...
The full output for one explorer window looks like:
LocationURL Title
----------- -----
file:///C:/open/this/Favorite%20%folder C:...
I found I could avoid the truncation by padding the string 'Title' with many spaces. That string's width seems to determine the maximum width of the output.
Still, I observe that only about 60% of the open explorer windows list a path. The rest are just a blank line.
I tried "$app.Windows() | Select-Object LocationName", but the output only contains the Explorer folder name only, not the full path and folder that is displayed in the Explorer title.
Another mystery is why the script runs so slowly. If I have 10 explorer windows open, the script runs for 30 seconds, taking about 3 seconds per path.
For this script:
function Get-WindowTitle($handle) {
Get-Process |
Where-Object { $_.MainWindowHandle -eq $handle } |
Select-Object -Expand MainWindowTitle
}
$app = New-Object -COM 'Shell.Application'
$app.Windows() |
Select-Object LocationName,#{n=' ------------Title---------------- ';e={Get-WindowTitle $_.HWND}}
This is the output (with some redacting with *** for privacy)
PS C:\E***> .\OpenExplorer.ps1
LocationName ------------Title----------------
------------ ----------------------------------------------------------------------------------
2019-07
Ame****
2019 Priv...
2019-10-3... C:\E\Event Presentations\2019-10-31 Priv**********bcast
E C:\E
5G Brief ... C:\E\Tech************ing\5G Brief (2018)
36 Series...
2019 DE* ... C:\E\16*****N\2019 DE*******************
Newsletters C:\E\Newsletters
Reports C:\E\Tech************ing\Reports
2019-10-2... C:\E\16**********s\2019-10-29 *********************
2019-11 C:\Data\Docs\Stand*********24\2019-11
UB****
Financial... C:\E\Financ************
Expenses C:\E\Internal\Expenses
E C:\E
E***
I assume what you're really interested is the local filesystem paths of the open Explorer windows, not necessarily the window titles (which aren't guaranteed to reflect the full paths).
Somewhat obscurely, the window objects returned by the .Windows() method contain the local path representation in their .Document.Folder.Self.Path property.
(New-Object -ComObject 'Shell.Application').Windows() | ForEach-Object {
$localPath = $_.Document.Folder.Self.Path
"C:\WINDOWS\explorer.exe /e, `"$localPath`""
}
The above produces output such as:
C:\WINDOWS\explorer.exe /e, "C:\Users\jdoe"
C:\WINDOWS\explorer.exe /e, "C:\Program Files"
You can output this to a batch file file as needed, e.g. by appending | Set-Content file.cmd to the above command.
Note: The windows are listed in the order in which they were created, so you cannot infer which among them was most recently activated. See this answer for a solution that finds the topmost File Explorer window and determines the path shown in it.
I found I could avoid the truncation
The truncation is just a display artifact - the data is still there.
You can make the data visible in one of two ways:
pipe to Format-Table -AutoSize to make sure that column values aren't truncated, space permitting
pipe to Format-List, which will show each property on its own line (line-wrapping overly long values).
I have a long (several million lines) data sheet in plain txt. Looks like this:
cellnumber x-coordinate y-coordinate z-coordinate temperature
1 -6.383637190E-01 2.408539131E-02 -5.244855285E-01 3.081549136E+02
2 -6.390314698E-01 2.286404185E-02 -5.245100260E-01 3.081547595E+02
3 -6.381718516E-01 2.373264730E-02 -5.236577392E-01 3.081547591E+02
4 -6.360489130E-01 2.259869128E-02 -5.245736241E-01 3.081547591E+02
5 -6.369081736E-01 2.253472991E-02 -5.236831307E-01 3.081547591E+02
6 -6.382256746E-01 2.215057984E-02 -5.237988830E-01 3.081547591E+02
7 -6.381900311E-01 2.126700431E-02 -5.245448947E-01 3.081547591E+02
8 -6.373924613E-01 2.117809094E-02 -5.238834023E-01 3.081547591E+02
I currently only have win command line and need to get rid off the whitespaces ath the beginning (their length is not constant as the cellnumber increases) so that I get
cellnumber x-coordinate y-coordinate z-coordinate temperature
1 -6.383637190E-01 2.408539131E-02 -5.244855285E-01 3.081549136E+02
2 -6.390314698E-01 2.286404185E-02 -5.245100260E-01 3.081547595E+02
3 -6.381718516E-01 2.373264730E-02 -5.236577392E-01 3.081547591E+02
4 -6.360489130E-01 2.259869128E-02 -5.245736241E-01 3.081547591E+02
5 -6.369081736E-01 2.253472991E-02 -5.236831307E-01 3.081547591E+02
6 -6.382256746E-01 2.215057984E-02 -5.237988830E-01 3.081547591E+02
7 -6.381900311E-01 2.126700431E-02 -5.245448947E-01 3.081547591E+02
8 -6.373924613E-01 2.117809094E-02 -5.238834023E-01 3.081547591E+02
May I ask for a solution? I dont have a clue, am not really experienced with this. Thx!
I guess TrimStart may be my friend.
EDIT: I have put together this:
#ECHO OFF
set "victim=testJana.txt"
SETLOCAL
FOR /F "tokens=*" %%A IN (%victim%) DO (
IF NOT "%%A"=="_" ECHO %%A>>%victim%_edited.txt
)
ENDLOCAL
pause
it works fine for smaller files but Im getting the message
not enough storage to process this command
Any idea how to deal with this?
I would suggest using powershell:
First, Second and Third edit: To be executed in the directory where data.txt file is placed and in the powershell.exe shell:
(good point to add -ReadCount by #lit in other post)
Get-Content -ReadCount 500 -Path .\path_to_your_source\data.txt | % {$_ -replace " +", " "} | Set-Content -Path .\path_to_our_output\data_no_additional_spaces.txt
Why -ReadCount makes sense? Here it takes 500 lines per run via pipes.
Here is the info from Microsoft pages)
-ReadCount
Specifies how many lines of content are sent through the pipeline at a
time. The default value is 1. A value of 0 (zero) sends all of the
content at one time.
This parameter does not change the content displayed, but it does
affect the time it takes to display the content. As the value of
ReadCount increases, the time it takes to return the first line
increases, but the total time for the operation decreases. This can
make a perceptible difference in very large items.
Reads data, replaces all the spaces and then saves data into data_new.txt
This answer was meant for the powershell.exe shell not the cmd.exe where you normally run your *.bat files. In powershell you have scripts called *.ps1.
If you store this the above command in a trim_space.ps1 and then launch it as (you need to have the script in the same directory as the data being transformed):
powershell.exe -ExecutionPolicy Bypass &'C:\path_to_script\trim_space.ps1'. You will see it executed.
Forth edit
To address your:
it works fine for smaller files but Im getting the message not enough
storage to process this command
Any idea how to deal with this?
You have to process it by chunks which you are not doing in your batch file right now. You get just to the point where you exhaust all the thread memory and it naturally fails. You need to have approach which allows you to limit the chunk of lines which are processed at once like -Readcount. In batch file I imagine it would be possible to call one batch file from other which would process only limited part of the file.
Using PowerShell, you can limit how much data is processed at a time in the pipeline.
Get-Content -Path .\testJana.txt -ReadCount 1000 |
ForEach-Object { $_ -replace '^ +', '' } |
Out-File -FilePath .\testJana_edited.txt -Encoding ASCII
If you want to run this from a cmd.exe shell, put the PowerShell code above into a file named sourcelimit.ps1 and use the following in a .bat script file.
powershell -NoProfile -File .\sourcelimit.ps1
I'm very new to powershell and is it possible to obtain the actual size of disk of a file? I was able to use the du, but is there another way of doing this without using that application?
This will give you the actual size of a file in bytes:
(gci <insert file path> | Measure-Object -Property length -Sum).sum
You can then use other logic to convert to KB, MB, GB, whatever you want. You can use the same command for size of directories, with the -Recurse option to get the size of all subdirectories and files in the root.
I am currently using the following command to find out how many documents with pdf format is there with there complete path but it shows the list of like 11,000 documents,
dir *.pdf /s /b**
I'd like to list only those images that has the file size greater than 1024KB , the file size shouldn't be displayed yet the file should be greater than 1024KB in size.
is that possible using command prompt ?
Since you're using windows, you will most likely have powershell:
ls *.pdf | where-object {$_.length -gt 1048576} | format-table -property Name
ls will list the files with .pdf extensions. where-object will filter the result set to files with length greater than 1MB (1048576 = 1MB). format-table will format the final output to display only the name of the file
I have to look at the last few lines of a large file (typical size is 500MB-2GB). I am looking for a equivalent of Unix command tail for Windows Powershell. A few alternatives available on are,
http://tailforwin32.sourceforge.net/
and
Get-Content [filename] | Select-Object -Last 10
For me, it is not allowed to use the first alternative, and the second alternative is slow. Does anyone know of an efficient implementation of tail for PowerShell.
Use the -wait parameter with Get-Content, which displays lines as they are added to the file. This feature was present in PowerShell v1, but for some reason not documented well in v2.
Here is an example
Get-Content -Path "C:\scripts\test.txt" -Wait
Once you run this, update and save the file and you will see the changes on the console.
For completeness I'll mention that Powershell 3.0 now has a -Tail flag on Get-Content
Get-Content ./log.log -Tail 10
gets the last 10 lines of the file
Get-Content ./log.log -Wait -Tail 10
gets the last 10 lines of the file and waits for more
Also, for those *nix users, note that most systems alias cat to Get-Content, so this usually works
cat ./log.log -Tail 10
As of PowerShell version 3.0, the Get-Content cmdlet has a -Tail parameter that should help. See the technet library online help for Get-Content.
I used some of the answers given here but just a heads up that
Get-Content -Path Yourfile.log -Tail 30 -Wait
will chew up memory after awhile. A colleague left such a "tail" up over the last day and it went up to 800 MB. I don't know if Unix tail behaves the same way (but I doubt it). So it's fine to use for short term applications, but be careful with it.
PowerShell Community Extensions (PSCX) provides the Get-FileTail cmdlet. It looks like a suitable solution for the task. Note: I did not try it with extremely large files but the description says it efficiently tails the contents and it is designed for large log files.
NAME
Get-FileTail
SYNOPSIS
PSCX Cmdlet: Tails the contents of a file - optionally waiting on new content.
SYNTAX
Get-FileTail [-Path] <String[]> [-Count <Int32>] [-Encoding <EncodingParameter>] [-LineTerminator <String>] [-Wait] [<CommonParameters>]
Get-FileTail [-LiteralPath] <String[]> [-Count <Int32>] [-Encoding <EncodingParameter>] [-LineTerminator <String>] [-Wait] [<CommonParameters>]
DESCRIPTION
This implentation efficiently tails the cotents of a file by reading lines from the end rather then processing the entire file. This behavior is crucial for ef
ficiently tailing large log files and large log files over a network. You can also specify the Wait parameter to have the cmdlet wait and display new content
as it is written to the file. Use Ctrl+C to break out of the wait loop. Note that if an encoding is not specified, the cmdlet will attempt to auto-detect the
encoding by reading the first character from the file. If no character haven't been written to the file yet, the cmdlet will default to using Unicode encoding
. You can override this behavior by explicitly specifying the encoding via the Encoding parameter.
Probably too late for an answere but, try this one
Get-Content <filename> -tail <number of items wanted> -wait
Just some additions to previous answers. There are aliases defined for Get-Content, for example if you are used to UNIX you might like cat, and there are also type and gc. So instead of
Get-Content -Path <Path> -Wait -Tail 10
you can write
# Print whole file and wait for appended lines and print them
cat <Path> -Wait
# Print last 10 lines and wait for appended lines and print them
cat <Path> -Tail 10 -Wait
I have a useful tip on this subject concerning multiple files.
Following a single log file (like 'tail -f' in Linux) with PowerShell 5.2 (Win7 and Win10) is easy (just use "Get-Content MyFile -Tail 1 -Wait"). However, watching MULTIPLE log files at once seems complicated. With PowerShell 7.x+ however, I've found an easy way by using "Foreach-Object -Parrallel". This performs multiple 'Get-Content' commands concurrently. For example:
Get-ChildItem C:\logs\*.log | Foreach-Object -Parallel { Get-Content $_ -Tail 1 -Wait }
Using Powershell V2 and below, get-content reads the entire file, so it was of no use to me. The following code works for what I needed, though there are likely some issues with character encodings. This is effectively tail -f, but it could be easily modified to get the last x bytes, or last x lines if you want to search backwards for line breaks.
$filename = "\wherever\your\file\is.txt"
$reader = new-object System.IO.StreamReader(New-Object IO.FileStream($filename, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [IO.FileShare]::ReadWrite))
#start at the end of the file
$lastMaxOffset = $reader.BaseStream.Length
while ($true)
{
Start-Sleep -m 100
#if the file size has not changed, idle
if ($reader.BaseStream.Length -eq $lastMaxOffset) {
continue;
}
#seek to the last max offset
$reader.BaseStream.Seek($lastMaxOffset, [System.IO.SeekOrigin]::Begin) | out-null
#read out of the file until the EOF
$line = ""
while (($line = $reader.ReadLine()) -ne $null) {
write-output $line
}
#update the last max offset
$lastMaxOffset = $reader.BaseStream.Position
}
I found most of the code to do this here.
I took #hajamie's solution and wrapped it up into a slightly more convenient script wrapper.
I added an option to start from an offset before the end of the file, so you can use the tail-like functionality of reading a certain amount from the end of the file. Note the offset is in bytes, not lines.
There's also an option to continue waiting for more content.
Examples (assuming you save this as TailFile.ps1):
.\TailFile.ps1 -File .\path\to\myfile.log -InitialOffset 1000000
.\TailFile.ps1 -File .\path\to\myfile.log -InitialOffset 1000000 -Follow:$true
.\TailFile.ps1 -File .\path\to\myfile.log -Follow:$true
And here is the script itself...
param (
[Parameter(Mandatory=$true,HelpMessage="Enter the path to a file to tail")][string]$File = "",
[Parameter(Mandatory=$true,HelpMessage="Enter the number of bytes from the end of the file")][int]$InitialOffset = 10248,
[Parameter(Mandatory=$false,HelpMessage="Continuing monitoring the file for new additions?")][boolean]$Follow = $false
)
$ci = get-childitem $File
$fullName = $ci.FullName
$reader = new-object System.IO.StreamReader(New-Object IO.FileStream($fullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [IO.FileShare]::ReadWrite))
#start at the end of the file
$lastMaxOffset = $reader.BaseStream.Length - $InitialOffset
while ($true)
{
#if the file size has not changed, idle
if ($reader.BaseStream.Length -ge $lastMaxOffset) {
#seek to the last max offset
$reader.BaseStream.Seek($lastMaxOffset, [System.IO.SeekOrigin]::Begin) | out-null
#read out of the file until the EOF
$line = ""
while (($line = $reader.ReadLine()) -ne $null) {
write-output $line
}
#update the last max offset
$lastMaxOffset = $reader.BaseStream.Position
}
if($Follow){
Start-Sleep -m 100
} else {
break;
}
}
try Windows Server 2003 Resource Kit Tools
it contains a tail.exe which can be run on Windows system.
https://www.microsoft.com/en-us/download/details.aspx?id=17657
There have been many valid answers, however, none of them has the same syntax as tail in linux. The following function can be stored in your $Home\Documents\PowerShell\Microsoft.PowerShell_profile.ps1 for persistency (see powershell profiles documentation for more details).
This allows you to call...
tail server.log
tail -n 5 server.log
tail -f server.log
tail -Follow -Lines 5 -Path server.log
which comes quite close to the linux syntax.
function tail {
<#
.SYNOPSIS
Get the last n lines of a text file.
.PARAMETER Follow
output appended data as the file grows
.PARAMETER Lines
output the last N lines (default: 10)
.PARAMETER Path
path to the text file
.INPUTS
System.Int
IO.FileInfo
.OUTPUTS
System.String
.EXAMPLE
PS> tail c:\server.log
.EXAMPLE
PS> tail -f -n 20 c:\server.log
#>
[CmdletBinding()]
[OutputType('System.String')]
Param(
[Alias("f")]
[parameter(Mandatory=$false)]
[switch]$Follow,
[Alias("n")]
[parameter(Mandatory=$false)]
[Int]$Lines = 10,
[parameter(Mandatory=$true, Position=5)]
[ValidateNotNullOrEmpty()]
[IO.FileInfo]$Path
)
if ($Follow)
{
Get-Content -Path $Path -Tail $Lines -Wait
}
else
{
Get-Content -Path $Path -Tail $Lines
}
}
Very basic, but does what you need without any addon modules or PS version requirements:
while ($true) {Clear-Host; gc E:\test.txt | select -last 3; sleep 2 }
It is possible to download all of the UNIX commands compiled for Windows from this GitHub repository: https://github.com/George-Ogden/UNIX
For those admins who live by the axiom that less typing is best, here is the shortest version I can find:
gc filename -wai -ta 10