Powershell Removing Leading Zero from File Name - powershell

I have a folder with Several files from the that look like:
XXX_01_07_11_1.bat
XX_1_07_06_02.bat
XXXX_2_22_14_01_02.bat
Without a specific length.
I would like to rename all the files in the folder removing all the leading zeros.
for example XXX_01_07_11_1.bat will be renamed to XXX_1_7_11_1.bat .
I found some ways to do it in Bash but not with powershell.

Apply _0+(?=[1-9]) or _0+(?=\d) regex as follows:
Get-ChildItem . -File -Filter "*_0*.*" |
ForEach-Object {
Rename-Item $_.FullName $($_.Name -replace '_0+(?=\d)', '_') -WhatIf
}
Remove the risk mitigation parameter -WhatIf as soon as debugged (or use -WhatIf:$false to suppress the automatic WhatIf behaviour that results when the value of the $WhatIfPreference variable is 1, see also Get-Help about_Preference_Variables).
Regex explanation:
_ matches the character _ Low Line (underscore) literally
0+ matches the character 0 literally
+ Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
?= Positive Lookahead. Assert that above _0+ match is followed by
[1-9] a single character in the [1-9] list i.e. in the range between 1 (unicode 49) and 9 (unicode 57), or
\d a digit (equals to [0-9] list but [1-9] effectively due to greedy ´0+´ above).

The easiest way is replacing _0 to _, like this:
Get-ChildItem _my_Path | ForEach-Object {
$f_name = $_.FullName
$name = $_.Name
$new_name = $name -replace "_0","_"
Rename-Item $f_name -NewName $new_name
}
Please have a try. :)

try this:
get-childitem "c:\temp" -file -filter "*.bat" | %{
$NewName=($_.Name -split '_' | %{if ($_ -eq '0') {'0'} else {$_.TrimStart('0')}}) -join '_'
Rename-Item $_.FullName $NewName
}

Note: This is at heart similar to the accepted answer, but demonstrates that you can pipe Get-ChildItem output directly to Rename-Item, which is both more concise and more efficient.
Get-ChildItem . -Filter *.bat |
Rename-Item -NewName { $_.Name -replace '(?<=_)0+' } -WhatIf
Specify the desired target folder instead of .
-WhatIf previews the renaming; remove it to actually rename.
You can pass a script block ({ ... }) to Rename-Item -NewName, which is evaluated for each input object (file), and in which $_ refers to the file at hand.
Note that this works even though -NewName is a [string]-typed parameter. The ability to pass a script block instead - a script-block parameter is a powerful generic PowerShell feature that allows you to calculate the parameter value on a per-input-object basis via a script block - see here for details.
-replace performs regex-based string replacement and '(?<=_)0+' replaces any sequence of one or more (+) 0 chars., if preceded by (?<=) a _ char.
(?<=_) is an instance of a (positive) lookbehind assertion, which means that while the _ is matched, it won't be captured; that is, it doesn't become part of what is being replaced.
Since no replacement string is given, the zeros are effectively removed.

cd c:\temp
'X_01_00007_11_1.bat','X_1_07_06_02.bat','X_2_22_14_01_02.bat','X_000_0_07_06_02.bat'|% {
New-Item $_ -Force}
Get-ChildItem -Path C:\Temp -File |ForEach-Object {
$oldname=$_.Name
$newname = $oldname -replace '_0+(?=\d)', '_'
#or
#$newname = $oldname -replace '_0+(\d)', '_$1'
Rename-Item $oldname -NewName $newname
}
目录: C:\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2018/5/7 20:55 0 X_01_00007_11_1.bat
-a---- 2018/5/7 20:55 0 X_1_07_06_02.bat
-a---- 2018/5/7 20:55 0 X_2_22_14_01_02.bat
-a---- 2018/5/7 20:55 0 X_000_0_07_06_02.bat
PS C:\temp> dir
目录: C:\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2018/5/7 20:55 0 X_0_0_7_6_2.bat
-a---- 2018/5/7 20:55 0 X_1_7_11_1.bat
-a---- 2018/5/7 20:55 0 X_1_7_6_2.bat
-a---- 2018/5/7 20:55 0 X_2_22_14_1_2.bat

Related

create file index manually using powershell, tab delimited

Sorry in advance for the probably trivial question, I'm a powershell noob, please bear with me and give me advice on how to get better.
I want to achieve a file index index.txt that contains the list of all files in current dir and subdirs in this format:
./dir1/file1.txt 07.05.2020 16:16 1959281
where
dirs listed are relative (i.e. this will be run remotely and to save space, the relative path is good enough)
the delimiter is a tab \t
the date format is day.month.fullyear hours:minutes:seconds, last written (this is the case for me, but I'm guessing this would be different on system setting and should be enforced)
(the last number is the size in bytes)
I almost get there using this command in powershell (maybe that's useful to someone else as well):
get-childitem . -recurse | select fullname,LastWriteTime,Length | Out-File index.txt
with this result
FullName LastWriteTime Length
-------- ------------- ------
C:\Users\user1\Downloads\test\asdf.txt 07.05.2020 16:19:29 1490
C:\Users\user1\Downloads\test\dirtree.txt 07.05.2020 16:08:44 0
C:\Users\user1\Downloads\test\index.txt 07.05.2020 16:29:01 0
C:\Users\user1\Downloads\test\test.txt 07.05.2020 16:01:23 814
C:\Users\user1\Downloads\test\text2.txt 07.05.2020 15:55:45 1346
So the questions that remain are: How to...
get rid of the headers?
enforce this date format?
tab delimit everything?
get control of what newline character is used (\n or \r or both)?
Another approach could be this:
$StartDirectory = Get-Location
Get-ChildItem -Path $StartDirectory -recurse |
Select-Object -Property #{Name='RelPath';Expression={$_.FullName.toString() -replace [REGEX]::Escape($StartDirectory.ToString()),'.'}},
#{Name='LastWriteTime';Expression={$_.LastWriteTime.toString('dd.MM.yyyy HH:mm:ss')}},
Length |
Export-Csv -Path Result.csv -NoTypeInformation -Delimiter "`t"
I recommend to use proper CSV files if you have structured data like this. The resulting CSV file will be saved in the current working directory.
If the path you are running this from is NOT the current scrip path, do:
$path = 'D:\Downloads' # 'X:\SomeFolder\SomeWhere'
Set-Location $path
first.
Next, this ought to do it:
Get-ChildItem . -Recurse -File | ForEach-Object {
"{0}`t{1:dd.MM.yyyy HH:mm}`t{2}" -f ($_ | Resolve-Path -Relative), $_.LastWriteTime, $_.Length
} | Out-File 'index.txt'
On Windows the newline will be \r\n (CRLF)
If you want control over that, this should do:
$newline = "`n" # for example
# capture the lines as string array in variable $lines
$lines = Get-ChildItem . -Recurse -File | ForEach-Object {
"{0}`t{1:dd.MM.yyyy HH:mm}`t{2}" -f ($_ | Resolve-Path -Relative), $_.LastWriteTime, $_.Length
}
# join the array with the chosen newline and save to file
$lines -join $newline | Out-File 'index.txt' -NoNewline
Because your requirement is to NOT have column headers in the output file, I'm using Out-File here instead of Export-Csv

Why $ _ does not return the complete object

I'm a beginner in powershell, I've been using it for just a few weeks, I was thinking about $_ when I saw this:
Get-ChildItem should return the files on a directory
PS C:\Users\Edu-mat\Powershell> Get-ChildItem
Diretório: C:\Users\Edu-mat\Powershell
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 10/08/2018 13:38 7 Test0.txt
-a---- 10/08/2018 13:42 5 Test1.txt
-a---- 10/08/2018 13:42 7 Test2.txt
-a---- 10/08/2018 13:43 8 Test3.txt
$_ Means current object in the pipeline.
but when i did Get-ChildItem | %{write-host $_} the output was not as expected
PS C:\Users\Edu-mat\Powershell> Get-ChildItem | %{write-host $_}
Test0.txt
Test1.txt
Test2.txt
Test3.txt
WHY $_ is not returning the entire object, it just printing the name of the file ?
can someone please explain me.
$_ is returning the entire object, however Write-Host expects a string, and so the .ToString() method is called on the object. In the case of System.IO.FileInfo its ToString() is overridden to output the name of the file.
Try this and see for yourself:
Get-ChildItem | %{Write-Host $_.ToString()}
Get-ChildItem | %{Write-Host $_.GetType()}
Get-ChildItem | %{Write-Host $_.Mode}
Write-Host is for writing information out to the console, so objects are formatted as strings, similar to if you had done gci | % { "$_" } (except the latter writes to the output stream not directly to the host).
If you want to write directly to the console but the same formatting you would see if sent to the console implicitly, use Out-Host as recommended by mklement0:
Get-ChildItem | Out-Host
His comment in full:
I suggest using Out-Host directly; also, perhaps surprisingly,
Write-Host "$_" is not always the same as Write-Host $_, because the
latter results in .ToString() getting called, which defaults to a
culture-sensitive representation (where available), whereas
PowerShell's string interpolation by design always uses the invariant
culture

Get all directories in directory that has a numeric name with powershell

I will start off by saying I am new to powershell...
I can get all the directories in a specified directory but I need to only get the directories that their names are numeric. Example:
1 - include
2 - include
3 - include
10 - include
LastFailed - exclude
I also will need to order them in sequential order for later processing.
Here is what I have so far:
$Dirs = Get-ChildItem -Path ..\..\..\builds -Attributes D
foreach($D in $Dirs)
{
Write-Host $D.Name
}
To get a list of just the folders that have numeric names, you can do this:
$Path = '..\..\..\builds'
$Dirs = Get-ChildItem -Path $Path -Attributes D |
Where-Object { $_.Name -match '^\d+$' } |
Sort-Object
$Dirs
This code above uses the Where-Object clause to filter out only the folders that have a numeric-only name.
It does this by using the -match operator and the regular expression ^\d+$ where:
`^` --> start at the beginning of the line
`\d` --> look for numeric values (0-9)
`+` --> there must be 1 or more numeric values present
`$` --> the end of the string
Instead of \d it could also have been written using [0-9]
The code ends with Sort-Object because the OP wants the list of folders sorted.
because we do not tell what property to sort on here, Powershell defaults to the Name.
The above will produce a list like:
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 18-8-2018 16:13 1
d----- 18-8-2018 16:13 10
d----- 18-8-2018 16:13 2
d----- 18-8-2018 16:13 5
As you can see, the list is sorted by property Name, but.. the numbers were treated as strings, so '10' follows '1'.
What we most likely want to do here is to have the Sort-Object treat the names as real Numbers, not strings.
To fix that we add the Select-Object cmdlet in order to create our own objects with properties we need.
We can get standard properties of the DirInfo object that is coming through the pipeline, AND it is possible to create a new calculated property.
Here I add the calculated property called SortIndex that is simply the numeric value converted from the Name, which we will then use for the numeric sort:
$Dirs = Get-ChildItem -Path $Path -Attributes D |
Where-Object { $_.Name -match '^\d+$' } |
Select-Object -Property FullName, Name, LastWriteTime, #{Name = 'SortIndex'; Expression = {[int]$_.Name} } |
Sort-Object -Property SortIndex
$Dirs
Now it produces an array of objects that when printed to console looks like this:
FullName Name LastWriteTime SortIndex
-------- ---- ------------- ---------
D:\Temp\1 1 18-8-2018 16:13:22 1
D:\Temp\2 2 18-8-2018 16:13:25 2
D:\Temp\5 5 18-8-2018 16:13:28 5
D:\Temp\10 10 18-8-2018 16:13:31 10
Hope this helps.
You can utilize Where-Object to filter your collection:
$dirs = Get-ChildItem -Path ..\..\..\builds -Directory |
Where-Object -Property Name -Match '\d'
This operation uses regex to match the digit class.
If you meant you only want the directory names that start with a digit, you can use an anchor:
'^\d'
and lastly, you can sort them with Sort-Object:
$dirs = Get-ChildItem -Path ..\..\..\builds -Directory |
Where-Object -Property Name -Match '^\d' |
Sort-Object -Property Name
You can further enhance this sort by being explicit about the starting digit:
-Property {[int]($_.Name -split '[^\d]')[0]}
In this context, we're splitting on the first non-digit character and capturing the first fragment that was split off (which should be the number represented as a string, so we cast to an integer).

How to split string and rename files in PowerShell?

I want to bulk rename the files in my folder, and all of them have the format of FilenameYeara\b.pdf, for example, TestData2001a.pdf, File2015b.pdf. I want to rename all of them to something like [Yeara\b]Filename, such as [2001a]TestData. The problem is that I don't know how can I split my filename into two parts (actually three if we count the extension, .pdf part), such that I put that second part as the first part of the file name.
Get-ChildItem | Rename-Item {$_.name -replace ‘current’, ’old’ }
How can I achieve this?
This does the regex match "anything, four digits, one character, .pdf" and replaces it with those items in the new ordering.
PS D:\t> gci | ren -newname { $_ -replace '(.*)(\d{4})(.)\.pdf', '[$2$3]$1.pdf' }
Directory: D:\t
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 13/05/2016 02:54 0 File2015b.pdf
-a--- 13/05/2016 02:53 0 TestData2001a.pdf
becomes
Directory: D:\t
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 13/05/2016 02:53 0 [2001a]TestData.pdf
-a--- 13/05/2016 02:54 0 [2015b]File.pdf
(Maybe try it with -Whatif before running for real)
This should get you started
$Matches.Clear()
Get-Item | % {
$_.BaseName -match "(\D+)([0-9]{4}[ab])"
Rename-Item -Path $_.FullName -NewName "$($Matches[2])$($Matches[1])$($_.Extension)"
}

powershell filter to remove .pdf extension in the name of a file

I am trying to use powershell to get all child elements in a folder the code I am using is
Get-ChildItem -Recurse -path C:\clntfiles
this code gives output like
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 4/29/2015 9:11 AM 6919044 HD 100616 Dec2014.pdf
-a--- 5/1/2015 11:42 AM 7091019 HD 101642 Jan2015.pdf
I don't want Mode lastWriteTime Length and name of file without .pdf extension
the output should be like
Dec2014
Jan2015
I am not sure how to filter that. please advise
I'll start by posting something similar to Leptonator's answer, but simplified by using the Select-Object command (alias Select used in code because it's habit, and I'm lazy).
$files = Get-ChildItem -Recurse -path C:\clntfiles | Select -ExpandProperty BaseName
Now that gets you the file names without extension. But, you actually asked for only part of the file names, as the first file name is "HD 100616 Dec2014.pdf" and you specified that you actually only want "Dec2014" to be returned. We can do that a couple different ways, but my favorite of them would be a RegEx match (because RegEx is awesome, and I think the LastIndexOf/SubString combo is overly complicated imho).
So, a RegEx match of "\w+$" will get what you want. That is broken down like this:
\w means any letter or number
+ means 1 or more of them
$ means the end of the string/line
So that's 1 or more alpha-numeric characters at the end of the string. We pipe our array of file names into a ForEach-Object loop (alias ForEach used out of habit), and then we have:
$Files | ForEach{ [RegEx]::Matches($_,"\w+$")}
Now, this outputs a [System.Text.RegularExpressions.Match] object, which is more than you want, but it does have a property Value which is exactly what you asked for! So we use Select -Expand again for that property and the output is precisely what you asked for:
$files = Get-ChildItem -Recurse -path C:\clntfiles | Select -ExpandProperty BaseName
$files | ForEach{[regex]::Matches($_,"\w+$")} | Select -Expand Value
RegEx matches are really handy, and if you learn about them you can simplify that quite a bit more like this:
gci C:\clntfiles -Rec | ?{$_.BaseName -match "(\w+)$"} | %{$Matches[1]}
That one line, as well as the two line code above it both should output:
Dec2014
Jan2015
Something like this should do it for you..
$files = Get-ChildItem -Recurse -path C:\clntfiles
if ($files -ne $null)
{
foreach ($file in $files)
{
$file.BaseName
}
}
In my folder, it shows:
> 2014-03-28_exeresult_file
> 2014-03-30_exeresult_file
> 2014-03-31_exeresult_file
> 2014-04-02_exeresult_file
> 2014-04-03_exeresult_file
> 2014-04-04_exeresult_file
> 2014-04-06_exeresult_file
> 2014-04-08_exeresult_file
and are indeed .txt files
Hope this helps!
Use the following Get-ChildItem -Recurse -name -path C:\clntfiles. This will get you only the file names.
Working solution:
$names = Get-ChildItem -name
foreach($n in $names) {$n.Substring(0,$n.IndexOf("."))}
You can also use LastIndexOf if part of the file name is .