I have a directory of PDFs that I need to rename according to a repeating sequence. In English, the command would go like this:
For each file in the directory, and in the order that the files are listed, alternate names between "Type 1", "Type 2" , "Type 3" until the each item has been renamed.
I'm hoping to do this in Powershell or on the command line.
The ForEach-Object cmdlet accepts a script block for the -Begin argument which can be used to initialize a counter variable. This variable can then be incremented on each iteration.
dir <directory_of_pdfs> | ForEach-Object -Begin { $i=1 } -Process { Rename-Item $_ "Type $($i++)" }
Related
I'm trying to automate renaming of many multiple files in a Windows 7 directory. I need to search a source index file (.txt or .csv which is a list of extended file names) and, where there is a partial match to the original file name, copy the first 12 characters (of the relevant string in the index file) and rename the original file accordingly (and preserving original file extension).
e.g.
(a) Files currently in the Windows directory are named as follows (hundreds of files):
23456abc.doc
76543cab.doc
92837bca.doc
(b) Values in the .txt/.csv file as follows (hundreds of values - NOTE: these do not have file extensions):
BetterName1.RandomText1.23456abc.MoreRandomText1
BetterName2.RandomText2.76543cab.MoreRandomText2
BetterName3.RandomText3.92837bca.MoreRandomText3
(c) Desired Result is for the files to be auto renamed as follows:
[by searching for filename in (a) within the list of values in (b) and, where there is a match, returning the first 12 characters as the new filename whilst preserving the original file extension]
BetterName1.doc
BetterName2.doc
BetterName3.doc
NOTE: My preference is to use an Index file for the look-up that is in .txt format. However in need I can also use a .csv
I have never used PowerShell before and am new to Windows batch scripting. I have searched around and tried to cobble together snippets of code into a Windows batch script (also tried a PowerShell script) to achieve this but my knowledge in this area is seriously lacking so unfortunately I'm still struggling away at square one.
Any assistance would be greatly appreciated. Thank you in advance.
P.S. Here is a PowerShell script that I tried to get working but to no avail.
$fdPath = 'C:\TEST\Data'
$sourcelistFiles = Get-ChildItem -Path $FDPATH\*.txt | ForEach-Object {$_.user } FullName
$findReplaceList = Import-Csv -Path $FDPATH\AllNames.csv
$totalitems = $sourcelistFiles.count
$currentrow = 0
foreach ($sourcelistFile in $sourcelistFiles)
{
$currentrow += 1
Write-Progress -Activity "Processing record $currentrow of $totalitems" -Status "Progress:" -PercentComplete (($currentrow / $totalitems) * 100)
[string] $txtSourceListFile = Get-Content $sourcelistFile | Out-String
ForEach ($findReplaceItem in $findReplaceList)
{
$txtSourceListFile = $txtSourceListFile -replace "$($findReplaceitem.FindString)", "$($findReplaceitem.ReplaceString)"
}
$txtSourceListFile | Set-Content ($sourcelistFile)
-NoNewLine
}
$FDPATH = 'C:\TEST\Data'
foreach($obj in (Import-Csv -Path $FDPATH\AllNames.csv)){
foreach($thing in $(gci -Path $FDPATH\*.txt)){
if("123.hash.avocado" -match $thing.basename){$ret = $thing.fullname}
}
$stuff = $obj -split "."
ren -Path $ret -NewName $stuff[0]
}
See if this works, it iterates through the csv then iterates through the directory to see if the directory's name is in the csv's line that is being iterated, then sets a variable to be the fullname of the file and renames it to the first name before the period.
Import-CSV LISTA.csv -Header newFileName | % { Copy-Item -Path archivo_convert.pdf -Destination "$($_.newfilename).pdf" }
In my directory path\, I have many text files with name something like this
area1.txt
area2.txt
area3.txt
Each file contains lots of one liner string that looks like this
user1
user2
user3
How do I write a Powershell script to pass all of the filename and content of file to a template so it will look like this for every iteration or combining all result in single file.
area "filename"
user "user1"
realname "user1"
Managed to write a script but it does not produce the desired outcome. Somehow it only produce last value of filename in loop and empty string after user and realname template.
foreach($file in Get-ChildItem path\ -Filter *.txt){
$result = #"
area $($file.BaseName)
area {0}
realname {0}
"#
}
foreach($file in Get-ChildItem path\ -Filter *.txt){
foreach ($line in $file |Get-Content){
$result -f $_
}
}
Need your help to point out the error in my script and help me to fix it. I really appreciate it if anyone can help.
You only need a single set of (nested) loops for this, then reference $line and $file appropriately inside the inner loop:
foreach($file in Get-ChildItem path\ -Filter *.txt){
foreach ($line in $file |Get-Content){
#"
area {1}
user {0}
realname {0}
"# -f $line,$file.BaseName
}
}
File names
In the above picture is an example of the file structure.
I have looked at many cmd lines for mass renaming but they all have either the same prefix or the same amount of characters.
Is there a way to rename multiple files with different prefixes but keep the text and file type. so 111 tiger.txt to tiger.txt and 32133_lion.txt to lion.txt.
This is relatively easy in PowerShell. The key is using a regex match to strip off the leading digits and '_' and space.
C:>type .\rd\doit.ps1
[cmdletbinding()]
Param()
Get-ChildItem "C:\src\t\rd" |
ForEach-Object {
$_.Name -match "\d*[_ ]*(.*)" | Out-Null
if ($matches[1] -ne $null) {
if ($_.Name -ne $matches[1]) {
Rename-Item $_.FullName $matches[1] -WhatIf
}
}
}
When you believe that correct renaming will be done, remove the -WhatIf from the Rename-Item command.
C:>.\rd\doit.ps1
What if: Performing the operation "Rename File" on target "Item: C:\src\t\rd\123dog.txt Destination: C:\src\t\rd\dog.txt".
What if: Performing the operation "Rename File" on target "Item: C:\src\t\rd\124cat.txt Destination: C:\src\t\rd\cat.txt".
My batch script in DOS is processing way to slow, so someone recomended I use powershell. I'm running it now for my first time on windows, but I've never used it before today. I hear it's similar to batch scripting, so I'm currently converting my batch script into a powershell script. Below is my script so far half way through conversion:
# ask user for network share and file that they would like to search
$textFilePath = Read-Host Please enter filesystem location of "filenames.txt". Include drive letter or // at start of path
$uncPath = Read-Host Please enter the UNC path you would like to search. Include // at start of path.
# REM check if network path is available. If it is, search network directory for files with same name as the strings in filenames.txt
IF (Test-Path %uncPath%) {
echo Network Path Exists. Searching %uncPath% for files with same name and extension as filenames in the filenames.txt file
for (/r %uncPath% %%G IN (*)) {for (/F "tokens=*" %%i in (%textFilePath%)) {if (%%~nxG==%%i) {echo %%~nxG,%%~fG >> filenamesOutput.txt}}}
pause
}
IF (!(Test-Path exist %uncPath%)) {
echo File not found
GOTO:userInput
}
I'm currently learning the powershell commands as I go and changing the batch command to powershell. Help with conversion would be appreciated.
AFter Edit:
Here's my original batch script:
#echo off
echo Please enter filesystem location of "filenames.txt". (Include Drive letter or // at start of path)
set /p textFilePath=Enter The Value:%=%
:userInput
REM ask user for network share and file that they would like to search
echo Please enter the UNC path you would like to search. (Include // at start of path)
set /p uncPath=Enter The Value:%=%
REM check if network path is available. If it is, search network directory for files with same name as the strings in filenames.txt
IF exist %uncPath% (
echo Network Path Exists. Searching %uncPath% for files with same name and extension as filenames in the filenames.txt file
for /r %uncPath% %%G IN (*) DO for /F "tokens=*" %%i in (%textFilePath%) DO if %%~nxG==%%i echo %%~nxG,%%~fG >> filenamesOutput.txt
pause
)
IF NOT exist %uncPath% (
echo File not found
GOTO:userInput
)
After 2nd Edit:
$VerbosePreference = "continue"
# ask user for network share and file that they would like to search
$textFilePath = Read-Host Please enter filesystem location of "filenames.txt". Include drive letter or // at start of path
$uncPath = Read-Host Please enter the UNC path you would like to search. Include // at start of path.
# check if network path is available. If it is, search network directory for files with same name as the strings in filenames.txt
IF (Test-Path $uncPath){
echo "Network Path Exists. Searching $uncPath for files with same name and extension as filenames in the filenames.txt file"
foreach($file in Get-ChildItem $uncPath -Recurse) {
# Get-Content reads in a file, line by line
foreach($line in Get-Content $_.FullName) {
# if goes in here
if($file.Name -eq $line){
echo $file.Name
"{0},{1}" -f $file.Name,$file.FullName | Out-File filenamesOutput2.txt -Append
}
}
}
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
}
IF (!(Test-Path $uncPath)){
echo "UNC path not found"
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
}
Variables:
In PowerShell, variable references are always prefixed with $ (much like in PHP or Perl).
So any variable you would assign and dereference in cmd/batch as:
set /p varname= somevalue
echo %varname%
Would in PowerShell be treated as (notice no difference between assigning and dereferencing):
$varname = "varvalue"
Write-Host $varname
So your exists/Test-Path statements should be:
if(Test-Path $uncPath){
# Loops in here
# "#" starts is a single-line comment btw
}
For loop:
In cmd, the for loop construct behaves different depending on the first switch:
for /r roughly means "loop recursively through filesystem tree"
for /f roughly means "loop through tokens in a file"
it should be noted that cmd for loops use parameters, denoted by the prefix %% (like %%G or %%i in your example)
PowerShell doesn't have this concept and just uses variables in loops. Thus, your for /r and for /f loops become:
# Get-ChildItem is equivalent to the "dir" command
# The -Recurse is pretty self-explanatory ( = /S)
foreach($file in Get-ChildItem $uncPath -Recurse) {
# Get-Content reads in a file, line by line
foreach($line in Get-Content $textFilePath) {
# if goes in here
}
}
Parameter modifiers:
In cmd, a parameter (like %%G) can be modified using a tilde (~) followed by a sequence of modifier characters.
%%~nG means "treat %%G as a path, return the name without extension"
%%~xG means "treat %%G as a path, return the file extension"
so %%~nxG naturally means "return filename WITH extension".
In PowerShell, everything is a .NET object, and in the case of $file, it's a FileInfo object. From the FileInfo object, the filename (WITH the extension) is stored in the Name property, so your if statement:
if %%~nxG==%%i
becomes:
if($file.Name -eq $line){
# echo and output goes in here
}
%%~fG means "treat %%G as a path, give me the full rooted path"
Again, the fact that $file is a FileInfo object comes in handy, the full path can be accessed from the FullName property:
"{0},{1}" -f $file.Name,$file.FullName | Out-File filenamesOutput.txt -Append
The -f operator is a simplified syntactic shortcut to String.Format, .NET's version of sprintf if you will.
Ultimately resulting in something like:
# ask user for network share and file that they would like to search
$textFilePath = Read-Host 'Please enter filesystem location of "filenames.txt". Include drive letter or \\ at start of path'
$uncPath = Read-Host 'Please enter the UNC path you would like to search. Include \\ at start of path.'
# check if network path is available. If it is, search network directory for files with same name as the strings in filenames.txt
if (Test-Path $uncPath) {
Write-Host "Network Path Exists. Searching $uncPath for files with same name and extension as filenames in the filenames.txt file"
foreach($file in Get-ChildItem $uncPath) {
foreach($line in Get-Content $textFilePath) {
if($file.Name -eq $line){
'"{0}","{1}"' -f $file.Name,$file.FullName | Out-File filenamesOutput.txt -Append
}
}
}
pause
} else {
Write-Host "File not found"
}
I'm trying to write a powershell script that will create outlook logs (using NETMON) from the local client to the exchange server. The problem is the file name is not incrementing correctly. I found this incrementing script online and i understand the logic (kinda). Am i using the right way to initialize the counter? It isn't renaming the file correctly. It gives the 2nd file just a _ then the 3rd file gets a _2 then after that it stops renaming files.
ALSO is invoke-expression waiting for my cmd to exit or will it just go to the next step of $count-- ?
#Directory to complete script in
$path = "c:\Outlook_Logs\"
cd $path
$cmdline = "Nmcap.exe /network * /captureipv4.address==X.X.X.X /file :\outlook_logs\client_$count.chn:100MB"
#Writes out number of files in directory to console
$count = (get-childitem $path -name).count
Write-Host "Number of Files: $count"
#Sorts items by decsending order
$items = Get-ChildItem | Sort Extension -desc
#Deletes oldest file by file extension number
del $items[0]
#Copy file from original directory to backup directory
Copy-Item c:\Outlook_Logs\* c:\Outlook_Logs_Temp\
#Sorts items by decsending order
$items = Get-ChildItem | Sort Extension -desc
#Renames files in quotes after NewName argument
$items | ForEach-Object -begin { $count= (get-childitem $path -name).count } -process { rename- item $_ -NewName "client_$count.cap";Invoke-Expression "$cmdline"; $count-- }
There's no need to increment the names yourself. chn captures files are created and names incrementally automatically when they reach the size specified after the colon.
What happens when you lower the size to 1MB and execute the command?