How do I display multiple characters in a MS-Dos command
So for example, if I want to display everything in a current directory that begins with A, I might type the following command into CMD.
dir/b A*
But what if I want to display everything A-M? How would I go about telling the computer to display multiple characters?
(this is for PowerShell) ls command for listing the current directory and after you add A*.
so the command is ls A*(it will list name starting with a as well).
If you want to filter using regular expression
Get-ChildItem | Where-Object { $_.Name -Match 'RegularExpression' }
A regular expression for starts with a letter is ^[A-M]+.
Change -Match to CMatch for capital sensitive.
Using cmd leaves you two possibilities: list files for each desired letter:
dir /b a* b* c* d* ... m*
which is awkward and inflexible because of hardcoding the list. Even using a for loop isn't better:
for %a in (a b c d e ... m) do #dir /b %a*
This might work fine if you want just two or three letters.
The second (much more flexible and probably faster too) method: get the whole list and just filter it:
dir /b | findstr /rib "[a-m]"
For not-consecutive letters (a*, b*, m*, n*, x*, y*):
dir /b | findstr /rib "[abmnxy]"
Related
I run subsequent reg queries and this takes quite a time:
reg query HKLM\SOFTWARE\Classes /s /f "foo"
reg query HKLM\SOFTWARE\Classes /s /f "bar"
Is there any way to search by multiple values at once with reg query?
No, unfortunately reg query /s /f accepts only a single filter expression.
The filter expression:Tip of the hat to aschipfl for his help.
is matched against all registry entities by default: key names, value names and data.
(OR-ed combinations of) options /k (keys), /v (values) and /d (data) can be used to narrow the scope.
/v can also be used without /f, in which case it requires a value-name search term (e.g., /v foo) that is matched in full (see below); /ve returns only default values (the values whose name is the empty string) if they contain data.
When combining /f with /v <valueNameSearchTerm> or /ve, only combining key search (/k) via /f is supported to narrow down the matches; that is, the only combinations that make sense are:
/f <keySearchTerm> /k /v <valueNameSearchTerm>
/f <keySearchTerm> /k /ve
That way, the /v <valueNameSearchTerm> / /ve matching is limited to those keys that match /f <keySearchTerm>, amounting to AND logic.
Any other combination - omitting /k, adding /d, using just /d - effectively causes the /f search term to be ignored.
/t REG_* can be used to narrow matching to specified value types, such as REG_SZ
performs case-insensitive substring matching by default.
supports wildcard characters * (any number of chars., including none) and ? (exactly 1 char.) - though note that something like foo* still only performs substring matching; there is seemingly no way to anchor substrings.
As stated, when you use /v directly with a value-name search term (e.g., /v foo, it must match in full; e.g., to find a value name that contains substring foo, you then must use *foo*.
/e performs whole-string matching, without wildcard support.
/c uses case-sensitive matching.
numeric data such as REG_DWORD is matched in its decimal string representation
binary data (REG_BINARY) is matched as a "byte string": a list of 2-hex-digit byte values without separators.
Run reg query /? to see all options or consult the documentation.
You can use the following PowerShell command to provide multiple filters:
Note:
The command is limited to the following search logic - though it could be adapted to support all reg query options, at which point creating a function wrapper would definitely be called for:
A regular expression (with -match) rather than wildcard matching (with -like) is used, which both simplifies the command and makes it more flexible (it wouldn't be hard to adapt the solution to use wildcard matching instead).
Only registry data is searched, not also key names and value names.
For instance, to search key names only, the command would be as simple as:
Get-ChildItem HKLM:\SOFTWARE\Classes -Recurse |
Where-Object { $_.PSChildName -match 'foo|bar' }
Unlike with reg.exe, binary data is matched byte by byte, based on their decimal string representation.
Only the sub-keys of the target key are examined, not the target key itself.
With a single filter, the command is slower than reg.exe, but with multiple filters it is eventually likely faster than multiple reg.exe calls; for the OP it took 4-5 - YMMV.
Replacing the Get-ChildItem call with direct use of the .NET framework for recursive key enumeration will likely bring speed improvements, though I have no sense of how much. A purpose-built native binary such as reg.exe will always be faster than custom PowerShell code.
Generally, the main advantages of a PowerShell solution are:
Objects are being returned, which greatly facilitates subsequent processing (no need for parsing text output).
The use of regular expressions allows for more sophisticated matching.
# The two filters to use, combined into a single regex.
$regex = 'foo|bar'
Get-ChildItem HKLM:\SOFTWARE\Classes -Recurse | ForEach-Object {
foreach ($value in $_.GetValueNames()) {
if (($data = $_.GetValue($value)) -match $regex) {
[PSCustomObject]#{
Key = $_.Name
Value = if ($value) { $value } else { '(default)' }
Data = $data
}
}
}
}
The output is something like the following, with the Data column containing the matches (scroll to the right; alternatively, pipe the above to Format-List for a one-property-per-line view):
Key Value Data
--- ----- ----
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AllSyncRootObjects StatusBar prop:~System.StatusBarViewItemCount;~System.StatusBarSelectedItemCount...
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0002E132-0000-0000-C000-000000000046}\InprocServer32 Class Microsoft.Vbe.Interop.CommandBarEventsClass
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0002E132-0000-0000-C000-000000000046}\InprocServe... Class Microsoft.Vbe.Interop.CommandBarEventsClass
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0006F054-0000-0000-C000-000000000046} (default) Microsoft Outlook InfoBar Control
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0006F054-0000-0000-C000-000000000046}\InprocServer32 Class Microsoft.Office.Interop.Outlook.OlkInfoBarClass
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0006F054-0000-0000-C000-000000000046}\InprocServe... Class Microsoft.Office.Interop.Outlook.OlkInfoBarClass
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0006F054-0000-0000-C000-000000000046}\ProgID (default) Outlook.OlkInfoBar.1
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0006F054-0000-0000-C000-000000000046}\VersionInde... (default) Outlook.OlkInfoBar
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{056440FD-8568-48e7-A632-72157243B55B} (default) Explorer Navigation Bar
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{05d7b0f4-2121-4eff-bf6b-ed3f69b894d7} (default) Taskbar Control Panel
...
Currently upon using the dir command dir /b *.*dot my files are listed in the following random order.
C.dot
D.dot
B.dot
A.dot
What should be done so that the same command dir /b *.*dot returns an ordered list, i.e.
A.dot
B.dot
C.dot
D.dot
I was initially thinking about a touch like command and I have tried copy /b A.dot+ trying to update timestamp but it did not work.
Please suggest which command can be used in windows / powershell to achieve this.
If you're trying to sort by alphabetical order of the file name, use Sort-Object. So something like Get-ChildItem -Path . -Filter "*.*dot" | Sort-Object -Property Name. Or, if you insist on not using Get-ChildItem, you could do dir /b *.*dot | Sort-Object.
you mentioned touch, if you need the timestamp updated you can set the LastWriteTime
(Get-ChildItem a.dot).LastWriteTime = get-date
there might be a better way to do what you are trying to do lastwritetime is limited to minutes so you can only update one file per minute
i am just unsure how you would GCI into a random order...? dir (the same thing as get-ChildItem) is going to order by name unless you sort by something else.
I was used to a few command line tricks in Windows that increased my productivity a lot.
Now I am told that I should move to PowerShell because it's more POWERful. It took me a while to get a little bit hang of it (objects, piping, etc.), and there are a lot of great tutorials on how to get a few things done.
However, some (relatively) basic trick still puzzle me. For instance, what is the equivalent of the FOR structure in PowerShell?
For example,
FOR %i IN (*.jpg) DO Convert %i -resize 800x300 resized/%i
The above line takes all of photos in a folder and uses the ImageMagick's Convert tool to resize the images and restores the resized imaged in a sub-folder called RESIZED.
In PowerShell I tried the command:
Dir ./ | foreach {convert $_.name -resize 800x300 resized/$_name}
This can't work despite all of the googling around I did. What is missing?
Note that / rather than \ is used as the path separator in this answer, which works on Windows too and makes the code compatible with the cross-platform PowerShell Core editions.
tl;dr:
$convertExe = './convert' # adjust path as necessary
Get-ChildItem -File -Filter *.jpg | ForEach-Object {
& $convertExe $_.Name -resize 800x300 resized/$($_.Name)
}
Read on for an explanation and background information.
The equivalent of:
FOR %i IN (*.jpg)
is:
Get-ChildItem -File -Filter *.jpg
or, with PowerShell's own wildcard expressions (slower, but more powerful):
Get-ChildItem -File -Path *.jpg # specifying parameter name -Path is optional
If you're not worried about restricting matches to files (as opposed to directories), Get-Item *.jpg will do too.
While dir works as a built-in alias for Get-ChildItem, I recommend getting used to PowerShell's own aliases, which follow a consistent naming convention; e.g., PowerShell's own alias for Get-ChildItem is gci
Also, in scripts it is better to always use the full command names - both for readability and robustness.
As you've discovered, to process the matching files in a loop you must pipe (|) the Get-ChildItem command's output to the ForEach-Object cmdlet, to which you pass a script block ({ ... }) that is executed for each input object, and in which $_ refers to the input object at hand.
(foreach is a built-in alias for ForEach-Object, but note that there's also a foreach statement, which works differently, and it's important not to confuse the two.)
There are 2 pitfalls for someone coming from the world of cmd.exe (batch files):
In PowerShell, referring to an executable by filename only (e.g., convert) does not execute an executable by that name located in the current directory, for security reasons.
Only executables in the PATH can be executed by filename only, and unless you've specifically placed ImageMagick's convert.exe in a directory that comes before the SYSTEM32 directory in the PATH, the standard Windows convert.exe utility (whose purpose is to convert FAT disk volumes to NTFS) will be invoked.
Use Get-Command convert to see what will actually execute when you submit convert; $env:PATH shows the current value of the PATH environment variable (equivalent of echo %PATH%).
If your custom convert.exe is indeed in the current directory, invoke it as ./convert - i.e., you must explicitly reference its location.
Otherwise (your convert.exe is either not in the PATH at all or is shadowed by a different utility) specify the path to the executable as needed, but note that if you reference that path in a variable or use a string that is single- or double-quoted (which is necessary if the path contains spaces, for instance), you must invoke with &, the call operator; e.g.,
& $convertExe ... or & "$HOME/ImageMagic 2/convert" ...
PowerShell sends objects through the pipeline, not strings (this innovation is at the heart of PowerShell's power). When you reference and object's property or an element by index as part of a larger string, you must enclose the expression in $(...), the subexpression operator:
resized/$($_.Name) - Correct: property reference enclosed in $(...)
resized/$_.Name - !! INCORRECT - $_ is stringified on its own, followed by literal .Name
However, note that a stand-alone property/index reference or even method call does not need $(...); e.g., $_.Name by itself, as used in the command in the question, does work, and retains its original type (is not stringified).
Note that a variable without property / index access - such as $_ by itself - does not need $(...), but in the case at hand $_ would expand to the full path. For the most part, unquoted tokens that include variable references are treated like implicitly double-quoted strings, whose interpolation rules are summarized in this answer of mine; however, many additional factors come into play, which are summarized here; edge cases are highlighted in this question.
At the end of the day, the safest choice is to double-quote strings that contain variable references or subexpressions:
"resized/$($_.Name)" - SAFEST
Use:
Get-ChildItem | foreach {convert $_.name -resize 800x300 resized/$($_.name)}
Or, perhaps, you need to pass the full name (with path), also showing a shorter syntax (using aliases):
gci | % {convert $_.fullname -resize 800x300 resized/$($_.name)}
Also, you might want to supply the full path to the executable.
Revised based on comments given below
There are many applications with the name "Convert". If I do
Get-Command Convert
on my computer. It shows me an app that is part of the Windows system. If PowerShell is running the wrong app on you, it's never going to work.
The solution will be to point PowerShell at the convert tool inside the ImageMagick program folder. A Google search on "ImageMagick PowerShell" will lead you to lots of people who have faced the same problem as you.
I am trying to watch a log file for occurrences of a word ("Purge").
I want to display the last 10 matching lines (at the time the command runs) and keep watching the file for newly appended lines that match the criteria.
So far, I got something working with PowerShell:
get-content .\CallAudit.Engine.log -tail 10 -Wait | where {$_ -match "Purge"}
This works, but it gets the last (any) 10 lines and only then applies the filter.
I want to
get only the matching last 10 lines
have the command keep watching the file.
Can this be done?
-Tail 10 won't help you because your purge-filter is executed after it has read the last 10 lines (which may or may not contain purge).
I would split this into two calls. One to list the last 10 values, and one to monitor.
Get-Content -Path .\CallAudit.Engine.log | Where-Object {$_ -match "Purge"} | Select-Object -Last 10
Get-Content -Path .\CallAudit.Engine.log -Wait -Tail 0 | Where-Object {$_ -match "Purge"}
Just use the right tool for the job which is less.exe. It is most often used as a pager (equivalent to more on Windows) but it is awesome tool that can be used to quickly examine files and monitor them.
For your case you need to use the following less options:
&/ - filtering lines (the same as where-object .... match)
F - forward forever (the same asgc .. -Wait -Tail)
If you file is log.txt and you need to monitor for numbers only this is the command:
less.exe +"&[0-9]+`nF" log.txt
This is how output looks like:
Change [0-9]+ to match your pattern and leave everything else. Another example, just show build errors real time and hide everything else
less.exe +"&^ERROR`nF" build.log
You can get less from chocolatey: cinst less.
The benefits of using less over pure powershell here are:
cross platform
less is very powerful and used by number of windows tools such as git, powershell community extensions etc. Its good to have such tool in your arsenal.
Scenario: Folder with more than one file(There are a maximum of 5 files). Each file starts with a character(does not repeat) followed by numbers. e.g: A123,B234,C123...
Objective: Rename the files according to a predetermined mapping. e.g: if A=1, B=2 etc. Then the File Starting with "A" becomes "1.", the file starting with "B" becomes "2." and so on. e.g: A123 => 1.A123
My Solution: I am not fluent in PowerShell but here is my attempt in achieving the above objective.
powershell "cd C:\Temp ; dir | ForEach-Object{if ($_.Name -Like "A*") {Rename-Item $_ "1.$_"} else {if ($_.Name -like "B*") {Rename-Item $_ "2.$_"} else{if($_.Name -like "C*"){Rename-Item $_ "3.$_"}}}}"
I needed the script to be executed from cmd and also in a specific folder (hence the cd and then the composed rename command).
This gets the job done but I would really appreciate if anyone could simplify things and show me a more prettier way at dealing with the situation.
So you can convert a letter to a number using something like:
[int][char]"F"
That will output 70. So, for your need you just need to get the first character of the file name, which is a simple SubString(0,1) call, then run it through ToUpper() to make sure you don't get any lower case letters, and then do the [int][char] bit to it, and subtract 64.
powershell "cd C:\Temp ; dir | ForEach-Object{$NewNameNum = [int][char]$_.Name.Substring(0,1).ToUpper() - 64;Rename-Item $_ "$NewNameNum.$_"}
Edit: Ok, so your original question is misleading, and should be edited to more accurately represent your request. If you are not assigning A=1, B=2, C=3 as a direct translation I can see 2 good options. First is a hashtable lookup.
PowerShell "$NmbrConv = #{'A'=3;'B'=1;'C'=9;'D'=2};dir c:\temp\*|%{$NewNameNum = $NmbrConv[$_.Name.Substring(0,1)];Rename-Item $_ "$NewNameNum.$_"}
This defines what letters convert to what numbers, then for each file just references the hashtable to get the number.
The other option is the Switch command. Running it in-line gets kind of ugly, but here's what it would look like formatted nicely.
Switch(GCI C:\Temp){
"^a" {$NewNameNum=3}
"^b" {$NewNameNum=1}
"^c" {$NewNameNum=9}
"^d" {$NewNameNum=2}
default {Rename-Item $_ "$NewNameNum.$_"}
}
Then if you need it all in one line you remove new lines and replace them with semicolons.
powershell 'Switch(GCI C:\Temp){"^a" {$NewNameNum=3};"^b" {$NewNameNum=1};"^c" {$NewNameNum=9};"^d" {$NewNameNum=2};default {Rename-Item $_ "$NewNameNum.$_"}}'