简体   繁体   中英

rename files script - how to use current directory for context menu

I have the following script with the line below pulling a context/browse menu for the users to easily navigate to a file of their choice and rename a file. Instead of using a static location for the directory, how can I grab the current directory that the script is launched from, or current directory?

EDIT: I forgot to mention I also want to be able to rename each file individually(prompt user for each file in the folder selected) instead of renaming every file the same thing.

FYI: I use a .bat file to fire up this powershell script.

##########################################################################
#Functions
##########################################################################

# Show input box popup and return the value entered by the user.
function Read-InputBoxDialog([string]$Message, [string]$WindowTitle, [string]$DefaultText)
{
Add-Type -AssemblyName Microsoft.VisualBasic
return [Microsoft.VisualBasic.Interaction]::InputBox($Message, $WindowTitle, $DefaultText)
}

# Show an Open Folder Dialog and return the directory selected by the user.
function Read-FolderBrowserDialog([string]$Message, 
[string]$InitialDirectory, [switch]$NoNewFolderButton)
{
$browseForFolderOptions = 0
if ($NoNewFolderButton) { $browseForFolderOptions += 512 }

$app = New-Object -ComObject Shell.Application
$folder = $app.BrowseForFolder(0, $Message, $browseForFolderOptions, $InitialDirectory)
if ($folder) { $selectedDirectory = $folder.Self.Path } else { $selectedDirectory = '' }
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($app) > $null
return $selectedDirectory
}

##########################################################################
#Start Code
##########################################################################

$i = 1

$directoryPath = Read-FolderBrowserDialog -Message "Please select a directory" -NoNewFolderButton -InitialDirectory 'P:\'
if (![string]::IsNullOrEmpty($directoryPath)) { Write-Host "You selected the directory: $directoryPath" }
else { "You did not select a directory." }



$acctdol = Read-InputBoxDialog -Message "Please enter the Account_DOL" -WindowTitle "Account_DateOfLoan" -DefaultText "########_MMDDYYYY_XXX"
if ($acctdol -eq $null) { Write-Host "You clicked Cancel" }
#elseif ($acctdol -eq "Something") { Write-Host "Thanks for typing Something" }
else { Write-Host "You entered $acctdol" }

If(!$acctdol){
$isnull++
}
else{
Get-ChildItem -Path $($directoryPath)*.pdf -Recurse -Force | 
ForEach-Object {
$newname = "${acctdol}_{0}.pdf" -f $i 
$i++
Rename-Item -Path $_.FullName -NewName $newname
}
}


##########################################################################
#Closing
##########################################################################
if ($Host.Name -eq "ConsoleHost")
{ 
Write-Host "Press any key to continue..."
$Host.UI.RawUI.FlushInputBuffer()   # Make sure buffered input doesn't "press a key" and skip the ReadKey().
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
}

You do not specify if the PS script is in the same location as the batch file, so I'm not going to assume that it is and treat this like the path to the script is hard coded into the batch file, and that the batch file could be anywhere on the machine.

As Squashman points out, %~dp0 is the current path in the batch file. What I would do is create a parameter in the PS script that accepts a path, and then pass the current path for the batch file to the PS script when you call it. So start the script off like:

[cmdletBinding()]
Param(
    [string]$CallingPath
)

Then you can refer to that for your BrowseForFolder call if you want. In the batch file you would call it like:

%windir%\system32\windowspowershell\v1.0\powershell.exe -file c:\Path\To\script.ps1 '%~dp0'

Edit: As for naming each file, I'm guessing that giving the user the option to name the files sequentially or name each file would be a good idea, then if they choose to name each one prompt for each file. For asking their preference I like the Windows.Forms.MessageBox, for which I have this function that I toss into the beginning of scripts that need it:

Function Show-MsgBox ($Text,$Title="",[Windows.Forms.MessageBoxButtons]$Button = "OK",[Windows.Forms.MessageBoxIcon]$Icon="Information"){
[Windows.Forms.MessageBox]::Show("$Text", "$Title", [Windows.Forms.MessageBoxButtons]::$Button, $Icon) | ?{(!($_ -eq "OK"))}
}

Then you can just do:

$NameEach = Show-MsgBox -Text "Do you want to rename each file sequentially based on the Account_DOL?`nIf you select No you will be prompted for a name for each file." -Title "Auto-Rename?" -Button YesNo -Icon Question

If you're writing this in the ISE it'll pop up options for what buttons and icons are available for you to select, or tab complete works for both the ISE and console.

Then for naming files I would use the Windows.Forms.SaveFileDialog window (I like using the Windows.Forms dialogs because they're familiar to people). Here's a function for that:

Function Get-OutputFilePath{
[CmdletBinding()]
Param(
    [String]$Filter = "All Files (*.*)|*.*",
    [String]$InitialDirectory,
    [Parameter(ValueFromPipelineByPropertyName=$true)]
    [Alias('DefaultFileName')]
    [String]$FullName,
    [Switch]$Force)
    BEGIN{
        [void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
    }
    PROCESS{
        If($FullName -match "\\.+$" -and !$InitialDirectory){$InitialDirectory = Split-Path $FullName;$FullName = Split-Path $FullName -Leaf}ElseIf(!$InitialDirectory){$InitialDirectory=[Environment]::GetFolderPath('Desktop')}
        $SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
        $SaveFileDialog.initialDirectory = $InitialDirectory
        Try{$SaveFileDialog.filter = $Filter}Catch{Throw $_;Break}
        $SaveFileDialog.FileName = $FullName
        $SaveFileDialog.OverwritePrompt = !$Force
        If($SaveFileDialog.ShowDialog() -eq "OK"){$SaveFileDialog.FileName}
    }
}

It looks worse than it is, really. But with that included in your functions you can do this in your ForEach loop when you're cycling through file to rename them:

$newname = If($NameEach -eq 'Yes'){$_|Get-OutputFilePath -Filter 'PDF File (*.pdf)|*.pdf|All Files (*.*)|*.*'|Split-Path -Leaf}Else{"${acctdol}_{0}.pdf" -f $i}
If($_.Name -ne $newname){$_|Rename-Item -NewName $newname}

That will prompt them with a Save File dialog defaulting to the correct directory, and name for the current file, and then set the $newname variable to whatever they select. Plus it does some things that are handy like prompting if they want to overwrite files, validating file names and paths, and stuff like that. Last thing I did was make sure they specified a new name so that we don't get errors trying to rename a file to the exact same thing.

Bonus function! I notice that you pause at the end of the script, so I wanted to pass along the function I use to pause since it doesn't fail in the ISE, and allows you to pass a message to it to display. In the ISE it pops up a dialog box with the message and an OK button, in the console it displays the message and a 'Press any key to continue...' message, much like yours does. For your script I'm sure what you have works perfectly, I just wanted to share.

Function Invoke-Pause ($Text){
    If($psISE){
        [Windows.Forms.MessageBox]::Show("$Text", "Script Paused", [Windows.Forms.MessageBoxButtons]"OK", [Windows.Forms.MessageBoxIcon]"Information") | ?{(!($_ -eq "OK"))}
    }Else{
        Write-Host $Text
        Write-Host "Press any key to continue ..."
        $Host.UI.RawUI.FlushInputBuffer()
        $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM