Command line to move the contents of an unnamed folder - command-line

I am trying to do a batch that fixes my flash drive from shortcut virus infection. I want to move the files from an unnamed folder (Alt+255) to the root directory of my flash drive, what is hard is that I can't do that with the short name of the folder because it is variable (one time it is 00A0~1 the other is 9DEC~1) so I am thinking of making a command that loops through the folders and prompts them to the user to decide if this is the unnammed folder or no like so (" " is that the folder? (Y/N)) similar to the output of this command:
for /d %d in (*) do rmdir /s "%d"
, but it moves all the contents of it to the root directory not delete it. How to do that?

You might be better off using VBScript or PowerShell for this instead.
VBScript:
Set fso = CreateObject("Scripting.FileSystemObject")
source = "K:\"
quarantine = "C:\quarantine"
Set re = New RegExp
re.Pattern = "^[^a-z0-9_-]"
re.IgnoreCase = True
For Each fldr In fso.GetFolder(source).SubFolders
If re.Test(fldr.Name) Then
answer = MsgBox("Move " & fldr.Path & "'?", vbYesNo, "Folder Check")
If answer = vbYes Then fldr.Move quarantine & "\"
End If
Next
PowerShell:
$source = "K:\"
$quarantine = "C:\quarantine"
Get-ChildItem $source -Force | ? {
$_.PSIsContainer -and $_.Name -match '^[^a-z0-9_-]'
} | % {
$answer = Read-Host "Move $($_.FullName)? [Y/n]"
if ( $answer.Length -eq 0 -or $answer[0] -eq "y" ) {
$_.MoveTo((Join-Path $quarantine $_.Name))
}
}
Both solutions check the source folder for folder names not starting with an alphanumeric character, underscore, or hyphen, prompt the user, and (on confirmation) move that folder to the quarantine folder.
If you want to use the PowerShell version on Windows XP, you have to install the Windows Management Framework first. PowerShell is included with that package.

This might work for you:
for /d %%i in (*) do echo %%~i |findstr "ECHO is" >nul&& move "%%~i" ELSEWHERE
This may move the folder   to ELSEWHERE.
for /d %%i in (*) do echo %%~i |findstr "ECHO is" >nul&& rd "%%~i"
This may remove the folder .

This may help:
#echo off
set "var="
for /f "delims=" %%a in ('dir /ad /b') do (
echo "%%a"
set /p "var=Is that it? press enter for no and Y for yes: "
if defined var (
attrib -h -s -r -a "%%a" /d
move "%%a\*.*" \
)
set "var="
)

Related

How can I use Explorer in Powershell to select a file to be processed within my Powershell script [duplicate]

Typically, asking the user to supply a file name to a batch script is a messy affair, requiring no misspellings, quotes around paths with spaces, and so forth. Unfortunately, users aren't well-known for accuracy. In situations where input file location is not known until runtime, using a GUI for file selection input reduces the likelihood of user error.
Is there a way to invoke a File... Open style gui file chooser or folder chooser from a Windows batch script?
If the script user has PowerShell or .NET installed, it is possible. See the answer below.
I'm also interested to see what other solutions anyone else can offer.
File Browser
Update 2016.3.20:
Since PowerShell is a native component of pretty much all modern Windows installations nowadays, I'm declaring the C# fallback as no longer necessary. If you still need it for Vista or XP compatibility, I moved it to a new answer. Starting with this edit, I'm rewriting the script as a Batch + PowerShell hybrid and incorporating the ability to perform multi-select. It's profoundly easier to read and to tweak as needed.
<# : chooser.bat
:: launches a File... Open sort of file chooser and outputs choice(s) to the console
:: https://stackoverflow.com/a/15885133/1683264
#echo off
setlocal
for /f "delims=" %%I in ('powershell -noprofile "iex (${%~f0} | out-string)"') do (
echo You chose %%~I
)
goto :EOF
: end Batch portion / begin PowerShell hybrid chimera #>
Add-Type -AssemblyName System.Windows.Forms
$f = new-object Windows.Forms.OpenFileDialog
$f.InitialDirectory = pwd
$f.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
$f.ShowHelp = $true
$f.Multiselect = $true
[void]$f.ShowDialog()
if ($f.Multiselect) { $f.FileNames } else { $f.FileName }
This results in a file chooser dialog.
The result of a selection outputs You chose C:\Users\me\Desktop\tmp.txt to the console. If you want to force single file selection, just change the $f.Multiselect property to $false.
(PowerShell command mercilessly leeched from the Just Tinkering Blog.) See the OpenFileDialog Class documentation for other properties you can set, such as Title and InitialDirectory.
Folder Browser
Update 2015.08.10:
Since there is already a COM method for invoking a folder chooser, it's pretty easy to build a PowerShell one-liner that can open the folder chooser and output the path.
:: fchooser.bat
:: launches a folder chooser and outputs choice to the console
:: https://stackoverflow.com/a/15885133/1683264
#echo off
setlocal
set "psCommand="(new-object -COM 'Shell.Application')^
.BrowseForFolder(0,'Please choose a folder.',0,0).self.path""
for /f "usebackq delims=" %%I in (`powershell %psCommand%`) do set "folder=%%I"
setlocal enabledelayedexpansion
echo You chose !folder!
endlocal
In the BrowseForFolder() method, the fourth argument specifies the root of the hierarchy. See ShellSpecialFolderConstants for a list of valid values.
This results in a folder chooser dialog.
The result of a selection outputs You chose C:\Users\me\Desktop to the console.
See the FolderBrowserDialog class documentation for other properties you can set, such as RootFolder. My original .NET System.Windows.Forms PowerShell and C# solutions can be found in revision 4 of this answer if needed, but this COM method is much easier to read and maintain.
This should work from XP upwards and does'nt require an hibrid file, it just runs mshta with a long command line:
#echo off
set dialog="about:<input type=file id=FILE><script>FILE.click();new ActiveXObject
set dialog=%dialog%('Scripting.FileSystemObject').GetStandardStream(1).WriteLine(FILE.value);
set dialog=%dialog%close();resizeTo(0,0);</script>"
for /f "tokens=* delims=" %%p in ('mshta.exe %dialog%') do set "file=%%p"
echo selected file is : "%file%"
pause
Windows Script Host
File Selection
Windows XP had a mysterious UserAccounts.CommonDialog WSH object which allowed VBScript and JScript to launch the file selection prompt. Apparently, that was deemed a security risk and removed in Vista.
Folder Selection
However, the WSH Shell.Application object BrowseForFolder method will still allow the creation of a folder selection dialog. Here's a hybrid batch + JScript example. Save it with a .bat extension.
#if (#a==#b) #end /*
:: fchooser2.bat
:: batch portion
#echo off
setlocal
for /f "delims=" %%I in ('cscript /nologo /e:jscript "%~f0"') do (
echo You chose %%I
)
goto :EOF
:: JScript portion */
var shl = new ActiveXObject("Shell.Application");
var folder = shl.BrowseForFolder(0, "Please choose a folder.", 0, 0x00);
WSH.Echo(folder ? folder.self.path : '');
In the BrowseForFolder() method, the fourth argument specifies the root of the hierarchy. See ShellSpecialFolderConstants for a list of valid values.
A file / folder selection may be done with pure Batch, as shown below. Of course, the feel and look is not as pleasant as a GUI, but it works very well and in my opinion it is easier to use than the GUI version. The selection method is based on CHOICE command, so it would require to download it in the Windows versions that don't include it and slightly modify its parameters. Of course, the code may be easily modified in order to use SET /P instead of CHOICE, but this change would eliminate the very simple and fast selection method that only requires one keypress to navigate and select.
#echo off
setlocal
rem Select a file or folder browsing a directory tree
rem Antonio Perez Ayala
rem Usage examples of SelectFileOrFolder subroutine:
call :SelectFileOrFolder file=
echo/
echo Selected file from *.* = "%file%"
pause
call :SelectFileOrFolder file=*.bat
echo/
echo Selected Batch file = "%file%"
pause
call :SelectFileOrFolder folder=/F
echo/
echo Selected folder = "%folder%"
pause
goto :EOF
:SelectFileOrFolder resultVar [ "list of wildcards" | /F ]
setlocal EnableDelayedExpansion
rem Process parameters
set "files=*.*"
if "%~2" neq "" (
if /I "%~2" equ "/F" (set "files=") else set "files=%~2"
)
rem Set the number of lines per page, max 34
set "pageSize=30"
set "char=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
rem Load current directory contents
set "name[1]=<DIR> .."
:ProcessThisDir
set "numNames=1"
for /D %%a in (*) do (
set /A numNames+=1
set "name[!numNames!]=<DIR> %%a"
)
for %%a in (%files%) do (
set /A numNames+=1
set "name[!numNames!]= %%a"
)
set /A numPages=(numNames-1)/pageSize+1
rem Show directory contents, one page at a time
set start=1
:ShowPage
set /A page=(start-1)/pageSize+1, end=start+pageSize-1
if %end% gtr %numNames% set end=%numNames%
cls
echo Page %page%/%numPages% of %CD%
echo/
if %start% equ 1 (set base=0) else set "base=1"
set /A lastOpt=pageSize+base, j=base
for /L %%i in (%start%,1,%end%) do (
for %%j in (!j!) do echo !char:~%%j,1! - !name[%%i]!
set /A j+=1
)
echo/
rem Assemble the get option message
if %start% equ 1 (set "mssg=: ") else (set "mssg= (0=Previous page")
if %end% lss %numNames% (
if "%mssg%" equ ": " (set "mssg= (") else set "mssg=%mssg%, "
set "mssg=!mssg!Z=Next page"
)
if "%mssg%" neq ": " set "mssg=%mssg%): "
:GetOption
choice /C "%char%" /N /M "Select desired item%mssg%"
if %errorlevel% equ 1 (
rem "0": Previous page or Parent directory
if %start% gtr 1 (
set /A start-=pageSize
goto ShowPage
) else (
cd ..
goto ProcessThisDir
)
)
if %errorlevel% equ 36 (
rem "Z": Next page, if any
if %end% lss %numNames% (
set /A start+=pageSize
goto ShowPage
) else (
goto GetOption
)
)
if %errorlevel% gtr %lastOpt% goto GetOption
set /A option=start+%errorlevel%-1-base
if %option% gtr %numNames% goto GetOption
if defined files (
if "!name[%option%]:~0,5!" neq "<DIR>" goto endSelect
) else (
choice /C OS /M "Open or Select '!name[%option%]:~7!' folder"
if errorlevel 2 goto endSelect
)
cd "!name[%option%]:~7!"
goto ProcessThisDir
:endSelect
rem Return selected file/folder
for %%a in ("!name[%option%]:~7!") do set "result=%%~Fa"
endlocal & set "%~1=%result%
exit /B
Other solution with direct run PowerShell command in Batch
rem preparation command
set pwshcmd=powershell -noprofile -command "&{[System.Reflection.Assembly]::LoadWithPartialName('System.windows.forms') | Out-Null;$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog; $OpenFileDialog.ShowDialog()|out-null; $OpenFileDialog.FileName}"
rem exec commands powershell and get result in FileName variable
for /f "delims=" %%I in ('%pwshcmd%') do set "FileName=%%I"
echo %FileName%
Batch + PowerShell + C# polyglot solution
This is the same solution as the Batch + PowerShell hybrid, but with the C# fallback stuff re-added for XP and Vista compatibility. Multiple file selection has been added at xNightmare67x's request.
<# : chooser_XP_Vista.bat
:: // launches a File... Open sort of file chooser and outputs choice(s) to the console
:: // https://stackoverflow.com/a/36156326/1683264
#echo off
setlocal enabledelayedexpansion
rem // Does powershell.exe exist within %PATH%?
for %%I in ("powershell.exe") do if "%%~$PATH:I" neq "" (
set chooser=powershell -noprofile "iex (${%~f0} | out-string)"
) else (
rem // If not, compose and link C# application to open file browser dialog
set "chooser=%temp%\chooser.exe"
>"%temp%\c.cs" (
echo using System;
echo using System.Windows.Forms;
echo class dummy {
echo public static void Main^(^) {
echo OpenFileDialog f = new OpenFileDialog^(^);
echo f.InitialDirectory = Environment.CurrentDirectory;
echo f.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
echo f.ShowHelp = true;
echo f.Multiselect = true;
echo f.ShowDialog^(^);
echo foreach ^(String filename in f.FileNames^) {
echo Console.WriteLine^(filename^);
echo }
echo }
echo }
)
for /f "delims=" %%I in ('dir /b /s "%windir%\microsoft.net\*csc.exe"') do (
if not exist "!chooser!" "%%I" /nologo /out:"!chooser!" "%temp%\c.cs" 2>NUL
)
del "%temp%\c.cs"
if not exist "!chooser!" (
echo Error: Please install .NET 2.0 or newer, or install PowerShell.
goto :EOF
)
)
rem // Do something with the chosen file(s)
for /f "delims=" %%I in ('%chooser%') do (
echo You chose %%~I
)
rem // comment this out to keep chooser.exe in %temp% for faster subsequent runs
del "%temp%\chooser.exe" >NUL 2>NUL
goto :EOF
:: // end Batch portion / begin PowerShell hybrid chimera #>
Add-Type -AssemblyName System.Windows.Forms
$f = new-object Windows.Forms.OpenFileDialog
$f.InitialDirectory = pwd
$f.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
$f.ShowHelp = $true
$f.Multiselect = $true
[void]$f.ShowDialog()
if ($f.Multiselect) { $f.FileNames } else { $f.FileName }
For a folder chooser for XP or Vista, use either the WSH solution or npocmaka's HTA solution.
Two more ways.
1.Using a hybrid .bat/hta (must be saved as a bat) script .It can use vbscript or javascript but the example is with javascrtipt.Does not create temp files.Selecting folder is not so easy and will require an external javascript libraries , but selecting file is easy
<!-- : starting html comment
:: FileSelector.bat
#echo off
for /f "tokens=* delims=" %%p in ('mshta.exe "%~f0"') do (
set "file=%%~fp"
)
echo/
if not "%file%" == "" (
echo selected file is : %file%
)
echo/
exit /b
-->
<Title>== FILE SELECTOR==</Title>
<body>
<script language='javascript'>
function pipeFile() {
var file=document.getElementById('file').value;
var fso= new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1);
close(fso.Write(file));
}
</script>
<input type='file' name='file' size='30'>
</input><hr><button onclick='pipeFile()'>Submit</button>
</body>
1.1 - without submit form proposed by rojo (see comments):
<!-- : starting html comment
:: FileSelector.bat
#echo off
for /f "tokens=* delims=" %%p in ('mshta.exe "%~f0"') do (
set "file=%%~fp"
)
echo/
if not "%file%" == "" (
echo selected file is : "%file%"
)
echo/
exit /b
-->
<Title>== FILE SELECTOR==</Title>
<body>
<script language='javascript'>
function pipeFile() {
var file=document.getElementById('file').value;
var fso= new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1);
close(fso.Write(file));
}
</script>
<input id='file' type='file' name='file' size='30' onchange='pipeFile()' >
</input>
<hr>
<button onclick='pipeFile()'>Submit</button>
<script>document.getElementById('file').click();</script>
</body>
2.As you already using powershell/net you can create selfcompiled jscript.net hybrid.It will not require temp cs file for compilation and will directly use the built-in jscrript.net compiler.There's no need of powershell too and the code is far more readable:
#if (#X)==(#Y) #end /* JScript comment
#echo off
:: FolderSelectorJS.bat
setlocal
for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
set "jsc=%%v"
)
if not exist "%~n0.exe" (
"%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
)
for /f "tokens=* delims=" %%p in ('"%~n0.exe"') do (
set "folder=%%p"
)
if not "%folder%" == "" (
echo selected folder is %folder%
)
endlocal & exit /b %errorlevel%
*/
import System;
import System.Windows.Forms;
var f=new FolderBrowserDialog();
f.SelectedPath=System.Environment.CurrentDirectory;
f.Description="Please choose a folder.";
f.ShowNewFolderButton=true;
if( f.ShowDialog() == DialogResult.OK ){
Console.Write(f.SelectedPath);
}
I will leave an 'echo' even to verify that multiple choice works in this code
echo off
set cmd=Add-Type -AssemblyName System.Windows.Forms;$f=new-object Windows.Forms.OpenFileDialog;$f.InitialDirectory= [environment]::GetFolderPath('Desktop');$f.Filter='Text Files(*.txt)^|*.txt^|All Files(*.*)^|*.*';$f.Multiselect=$true;[void]$f.ShowDialog();if($f.Multiselect) {$f.FileNames}else{$f.FileName}
set pwshcmd=powershell -noprofile -command "&{%cmd%}"
for /f "tokens=* delims=" %%I in ('%pwshcmd%') do call :sum "%%I" ret
echo =========
echo --%ret%--
pause
exit /B
:sum [mud] [ret]
echo "%~1"
set FileName=%FileName% "%~1"
set ret=%FileName%
exit /B
I has been wrote my own portable solution:
https://github.com/andry81/contools/tree/HEAD/Utilities/src/_gui/wxFileDialog/
You can download executable from here:
https://github.com/andry81/contools/tree/HEAD/Utilities/bin/contools/wxFileDialog.exe
The utility has dependency on wxWidgets 3.1.x, so you can actually build it for other operating systems.

How to get the folder name with highest number in folder name?

I have multiple folders with these names:
ABC_03_00_016_0
ABC_03_00_016_1
ABC_03_00_016_2
ABC_03_00_016_3
ABC_03_00_016_4
ABC_03_00_016_5
What I want to do is to retain the folder with largest number in the end of folder name, i.e. ABC_03_00_016_5 in above case using PowerShell or batch commands.
How to get the folder with greatest number?
Maybe this could be done more elegant, but it's probably working as you want. I'm stripping the last digits, converting & comparing them to then determine the highest one. As you can see, order does not matter:
$items =
"ABC_03_00_016_0",
"ABC_03_00_016_100",
"ABC_03_00_016_99"
[int]$highestNumberTillNow = 0
$highestitem = ""
foreach ($item in $items){
[int]$number = $item.substring($item.LastIndexOf("_")+1,$item.length-$item.LastIndexOf("_")-1)
if ($number -gt $highestNumberTillNow){
$highestNumberTillNow = $number
$highestitem = $item
}
}
write-host $highestitem
You can use this:
#echo off
setlocal enabledelayedexpansion
pushd "%~dp0"
for /f "tokens=4 delims=_" %%a in ('dir ABC_03_016_* /ad /b') do (
for /f "tokens=* delims= " %%b in ('dir ABC_03_016_* /ad /b') do (
set dir[%%a]=%%b
)
)
set dc=0
:loop
set /a dc=dc+1
if defined dir[%dc%] goto loop
goto break
:break
set /a dc=dc-1
echo The folder is !dir[%dc%]!
pause >nul
Assuming you could have more folders with a similar name in the root path like
ABC_03_00_016_0
ABC_03_00_016_1
ABC_03_00_016_2
ABC_03_00_016_3
ABC_03_00_016_4
ABC_03_00_016_5
DEF_03_00_016_0
DEF_03_00_016_1
DEF_03_00_016_10
Using PowerShell you can use something like below.
This will return the folder object(s) with the highest number at the end of the name:
$lastFolder = Get-ChildItem -Path 'D:\test' -Directory | Where-Object { $_.Name -match '(.+)_(\d+)$' } |
Group-Object -Property #{Expression={ $matches[1] }} | ForEach-Object {
$_.Group | Sort-Object -Property #{Expression={ [int]$matches[2] }} | Select-Object -Last 1
}
# for demo, just output the FullName property of the folders found
$lastFolder.FullName
Output:
D:\test\ABC_03_00_016_5
D:\test\DEF_03_00_016_10
Regex details:
( Match the regular expression below and capture its match into backreference number 1
. Match any single character that is not a line break character
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
_ Match the character “_” literally
( Match the regular expression below and capture its match into backreference number 2
\d Match a single digit 0..9
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
$ Assert position at the end of the string (or before the line break at the end of the string, if any)
If you're wanting to remove all of the directories except for the one ending with the largest number, then I'd suggest PowerShell too:
Get-ChildItem -Path 'C:\Users\Naqqash\Desktop' -Filter '*_*_*_*_*' -Directory |
Where-Object { $_.Name -Match '(.+)_(\d+)$' } |
Sort-Object -Property { [Int]$($_.Name).Split('_')[-1] } |
Select-Object -SkipLast 1 |
Remove-Item
Please remember to adjust the path on line 1 to that holding your directories.
The example above requires PowerShell v5.0
The first method can be used only if all folder names have the same length, i.e. leading zeros are used to make sure that all numbers in all folder names have same number of digits.
#echo off
set "LastFolder="
for /F "eol=| delims=" %%I in ('dir "%~dp0ABC_03_00_016_*" /AD /B /O-N 2^>nul') do set "LastFolder=%%I" & goto HaveFolder
echo Found no folder matching pattern ABC_03_00_016_* in "%~dp0".
goto :EOF
:HaveFolder
echo Last folder according to sorted folder names is: %LastFolder%
The task to get folder name with greatest last number is more difficult on number of digits differs on last number.
#echo off
set "LastFolder="
setlocal EnableExtensions EnableDelayedExpansion
set "FolderNumber=-1"
for /F "eol=| delims=" %%I in ('dir "%~dp0ABC_03_00_016_*" /AD /B 2^>nul') do (
for /F "eol=| tokens=5 delims=_" %%J in ("%%I") do (
if %%J GTR !FolderNumber! (
set "LastFolder=%%I"
set "FolderNumber=%%J"
)
)
)
endlocal & set "LastFolder=%LastFolder%"
if not defined LastFolder (
echo Found no folder matching pattern ABC_03_00_016_* in "%~dp0".
) else (
echo Last folder according to last number in name is: %LastFolder%
)
Note: The last number in folder name should have never leading zeros on using the second code above. A number with one or more leading 0 is interpreted as octal number which means 08, 09, 18, 19, etc. are invalid octal numbers and are interpreted for that reason with value 0 by command IF on making the integer comparison. There would be additional code necessary above the IF condition to first remove all leading 0 from number string before doing the integer comparison.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains %~dp0 (drive and path of batch file ending with a backslash).
dir /?
echo /?
endlocal /?
for /?
if /?
set /?
setlocal /?
Read the Microsoft article about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.

Issues with passing long string from powershell expression to cmd

I am having some issues with modifiying a Powershell/batch hybrid script taken from another stack overflow question (https://stackoverflow.com/a/15885133/1683264).
When selecting a file and echoing the command line parameter with quotation marks it works fine if the file name and path are short, however when the length is longer than the cmd window it will split it into two lines.
I think that this has something to do with using a command line parameter vs using a variable, however I am very new to both batch script and powershell and I'd appreciate any help!
<# : chooser.bat
:: launches a File... Open sort of file chooser and outputs choice(s) to the console
:: https://stackoverflow.com/a/15885133/1683264
#echo off
setlocal
for /f "delims=" %%I in ('powershell -noprofile "iex (${%~f0} | out-string)"') do (
echo "%%~I"
)
pause
goto :EOF
: end Batch portion / begin PowerShell hybrid chimera #>
Add-Type -AssemblyName System.Windows.Forms
$f = new-object Windows.Forms.OpenFileDialog
$f.InitialDirectory = pwd
$f.Filter = "DAT Files (*.dat*)|*.dat*|All Files (*.*)|*.*"
$f.ShowHelp = $true
$f.Multiselect = $true
[void]$f.ShowDialog()
if ($f.Multiselect) {$f.FileNames} else {$f.FileName}
I am looking for the output to be
"fullnameandpath.dat"
however if I have a long path and file name I get something like this:
"fullnamea"
"
"ndpath.dat"

Implement Regex in batch or powershell script to generate folders and move files in relative folders ordered by key string in file name

I use a similar script to generate and move files into folders.
$ToFolder = "$env:USERPROFILE\Desktop\to"
$FromFolder = "$env:USERPROFILE\Desktop\From"
#Create the sample folder on your desktop
#This line can be commented out if your ToFolder exists
New-Item $ToFolder -ItemType directory -Force
GCI -Path $FromFolder *.torrent | % {
if ($_.Name -match "(19|20)\d{2}") {
#Check to see if year folder already exists at the destination
#If not then create a folder based on this year
if (!(Test-Path "$ToFolder\$($Matches[0])")) {
New-Item -Path "$ToFolder\$($Matches[0])" -ItemType directory
}
#Transfer the matching file to its new folder
#Can be changed to Move-Item if happy with the results
Copy-Item -Path $_.FullName -Destination "$ToFolder\$($Matches[0])" -Force
}
}
but in my case I want move PDF and different files name and I don't know how solve. Key is title like "Il Corriere dello Sport" withouth suffixes like 02-08-2016 and without -.
Il_Corriere_dello_Sport_SICILIA_-_02-08-2016HQ
Il_Corriere_dello_Sport_STADIO_-_02-08-2016HQ
Il_Corriere_di_Arezzo_-_31-08-2016MQ
Il_Giornale_Di_Vicenza_-_23-08-2016
Il_Mattino_di_Padova_-_23-08-2016
Il_Messaggero_-_02-08-2016
Il_Messaggero_-_23-08-2016
Il__Messaggero_Veneto_-_31-08-2016HQ
Il__Tirreno_-_31-08-2016HQ
Il_Centro_-_30-08-2016
Il_Centro_CHIETI_-_23-08-2016HQ
So I need create folders like
Il_Corriere_dello_Sport_SICILIA
Il_Corriere_di_Arezzo
Il_Giornale_Di_Vicenza
Il_Mattino_di_Padova
Il_Messaggero
Il__Tirreno
P.S: underscore sign _ is not necessary so I prefer replace with space.
and then script must move files relative folders. Finally result should be
├─Il Messaggero [folder]
│ ├─Il_Messaggero_-_02-08-2016 [file]
│ └─Il_Messaggero_-_23-08-2016 [file]
├─Il Messaggero Veneto [folder]
│ └─Il__Messaggero_Veneto_-_31-08-2016HQ [file]
:
Here is a pure batch-file solution -- see all the explanatory remarks (rem):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "SPLITCHAR=-" & rem // (a single character to split the file names)
set "SEARCHSTR=_" & rem // (a certain string to be replaced by another)
set "REPLACSTR= " & rem // (a string to replace all found search strings)
set "OVERWRITE=" & rem // (set to non-empty value to force overwriting)
rem // Get file location and pattern from command line arguments:
set "LOCATION=%~1" & rem // (directory containing files to process)
set "PATTERNS=%~2" & rem // (file pattern; match all files if empty)
rem /* Prepare overwrite flag (if defined, set to character forbidden
rem in file names; this affects later check for file existence): */
if defined OVERWRITE set "OVERWRITE=|"
rem // Continue only if location is given:
if defined LOCATION (
rem // Change current working directory to given location:
pushd "%LOCATION%" && (
rem // Loop through all files matching the given pattern:
for /F "eol=| delims=" %%F in ('dir /B "%PATTERNS%"') do (
rem // Process each file in a sub-routine:
call :PROCESS "%%F" "%SPLITCHAR%" "%SEARCHSTR%" "%REPLACSTR%"
)
rem // Restore former working directory:
popd
)
)
endlocal
exit /B
:PROCESS
rem // Retrieve first argument of sub-routine:
set "FILE=%~1"
rem // Split name at (first) split character and get portion in front:
for /F "delims=%~2" %%E in ("%~1") do (
rem // Append a split character to partial name:
set "FOLDER=%%E%~2"
)
setlocal EnableDelayedExpansion
rem // Right-trim partial name:
if not "%~3"=="" set "FOLDER=!FOLDER:%~3%~2=!"
set "FOLDER=!FOLDER:%~2=!"
rem /* Check whether partial name is not empty
rem (could happen if name began with split character): */
if defined FOLDER (
rem // Replace every search string with another:
if not "%~3"=="" set "FOLDER=!FOLDER:%~3=%~4!"
rem // Create sub-directory (surpress error if it already exists):
2> nul md "!FOLDER!"
rem /* Check if target file already exists; if overwrite flag is
rem set (to an invalid character), the target cannot exist: */
if not exist "!FOLDER!\!FILE!%OVERWRITE%" (
rem // Move file finally (surpress `1 file(s) moved.` message):
1> nul move /Y "!FILE!" "!FOLDER!"
)
)
endlocal
exit /B
The script requires the directory containing all the files to process as the first command line argument. The created sub-directories are placed therein. An optional second command line argument defines a file name pattern to filter certain file types/names. Supposing it is saved as D:\Script\build-folder-hierarchy.bat, the files are contained in D:\Data, and you want to handle *.pdf files only, run it as follows:
"D:\Script\build-folder-hierarchy.bat" "D:\Data" "*.pdf"
This is a very similar approach, but with a slightly different directory handling:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "SPLITCHAR=-" & rem // (a single character to split the file names)
set "SEARCHSTR=_" & rem // (a certain string to be replaced by another)
set "REPLACSTR= " & rem // (a string to replace all found search strings)
set "OVERWRITE=" & rem // (set to non-empty value to force overwriting)
rem // Get file location and pattern from command line arguments:
set "LOCATION=%~1" & rem // (directory to move the processed files into)
set "PATTERNS=%~2" & rem // (file pattern; match all files if empty)
rem /* Prepare overwrite flag (if defined, set to character forbidden
rem in file names; this affects later check for file existence): */
if defined OVERWRITE set "OVERWRITE=|"
rem // Continue only if target location is given:
if defined LOCATION (
rem // Create target location (surpress error if it already exists):
2> nul md "%LOCATION%"
rem /* Loop through all files matching the given pattern
rem in the current working directory: */
for /F "eol=| delims=" %%F in ('dir /B "%PATTERNS%"') do (
rem // Process each file in a sub-routine:
call :PROCESS "%%F" "%LOCATION%" "%SPLITCHAR%" "%SEARCHSTR%" "%REPLACSTR%"
)
)
endlocal
exit /B
:PROCESS
rem // Retrieve first argument of sub-routine:
set "FILE=%~1"
rem // Split name at (first) split character and get portion in front:
for /F "delims=%~3" %%E in ("%~1") do (
rem // Append a split character to partial name:
set "FOLDER=%%E%~3"
)
setlocal EnableDelayedExpansion
rem // Right-trim partial name:
if not "%~4"=="" set "FOLDER=!FOLDER:%~4%~3=!"
set "FOLDER=!FOLDER:%~3=!"
rem /* Check whether partial name is not empty
rem (could happen if name began with split character): */
if defined FOLDER (
rem // Replace every search string with another:
if not "%~4"=="" set "FOLDER=!FOLDER:%~4=%~5!"
rem // Create sub-directory (surpress error if it already exists):
2> nul md "%~2\!FOLDER!"
rem /* Check if target file already exists; if overwrite flag is
rem set (to an invalid character), the target cannot exist: */
if not exist "%~2\!FOLDER!\!FILE!%OVERWRITE%" (
rem // Move file finally (surpress `1 file(s) moved.` message):
1> nul move /Y "!FILE!" "%~2\!FOLDER!"
)
)
endlocal
exit /B
This script uses the current working directory to find the files to process. It requires the target directory as the first command line argument, where the created sub-directories are placed in. An optional second command line argument defines a file name pattern to filter certain file types/names. Supposing it is saved as D:\Script\build-folder-hierarchy.bat, the files are contained in D:\Data and need to be moved to D:\Target, and you want to handle *.pdf files only, run it as follows:
cd /D "D:\Data"
"D:\Script\build-folder-hierarchy.bat" "D:\Target" "*.pdf"
Another solution is to use this powershell script
$pdfs = Get-ChildItem -Filter "*.pdf"
foreach ($pdf in $pdfs) {
# Extract the journal name from the file name
$journalName = ($pdf.Name -split "_-_")[0] -replace "_", " "
# Create a folder with the name of the journal if it doesn't already exist
$targetFolder = "$journalName"
if (!(Test-Path $targetFolder)) {
New-Item -ItemType Directory -Path $targetFolder
}
# Move the file to the journal folder
Move-Item $pdf.FullName $targetFolder
}

Copying a folder, but excluding specified sub-directories

I need help with copying a folder, but doesn't copy sub-folders with the names that are specified in a text file. The text file is located:
U:\Directory\Directory\Textfile.txt
I need to copy folders that are specified in the text file, but here's the catch, after
U:\Directory\Directory\ the folders have random names, which is why they are stored in the text file. Example of what the directory tree looks like:
U:\Directory\Directory\"12345"\Pickle <--- Pickle is the folder I want.
U:\Directory\Directory\"22345"\Pickle
^
|
This is a random name specified in the text file.
They all have the folder Pickle inside, which is what I'm after. Inside the text file are names of all the folders that are after: U:\Directory\Directory\. The text file looks like this:
1335232 <--- This is the name of the random folders.
1242334 <--- They all are located:
2342312 <--- U:\Directory\Directory\~HERE~
(etc...)
The folders should be copied from U:\Directory\Directory\"12345"\Pickle to U:\Output\
The names for all folders are numbers if this helps. Thank you Peter for trying to help me, I'm sorry if I was unclear. I hope this clears things up!
#ECHO OFF
SETLOCAL
SET relroot=u:\directory
SET subdir=randomsubfoldername
::
FOR /f %%i IN (
'dir /b /ad %relroot%\%subdir% ^|findstr /b /e /v /g:textfile.txt '
) DO ECHO %relroot%\%subdir%\%%i
The DIR command lists the directorynames (/ad) in basic form (.b) - that is, name-only. The findstr finds lines that do not (/v) begin (/b) and end (/e) with the lines in the file filename (/g:filename)
With the revised information, and noting that the original information clearly showed the penultimate directory name was the same and selection occurring on the leaf, and the single example now provided...
#ECHO OFF
SETLOCAL
ECHO Here is a test structure
ECHO -----------------------------
DIR /s /b /ad u:\directory
ECHO ------Here is the textfile---------
TYPE u:\directory\textfile.txt
ECHO ====Method 1==============
FOR /f %%i IN (u:\directory\textfile.txt) DO (
DIR /s /b /ad u:\directory | FINDSTR /r ".*\\%%i\\.*" | FINDSTR /v /r ".*\\%%i\\.*\\.*"
)
ECHO ====Method 2==============
FOR /f %%i IN (u:\directory\textfile.txt) DO (
FOR /f %%s IN (
'DIR /s /b /ad u:\directory ^| FINDSTR /r ".*\\%%i\\.*" ^| FINDSTR /v /r ".*\\%%i\\.*\\.*"'
) DO ECHO selected : %%s
)
ECHO ====Method 3 - to ignore ...\target\subdir that has any subdir ==============
FOR /f %%i IN (u:\directory\textfile.txt) DO (
FOR /f %%s IN (
'DIR /s /b /ad u:\directory ^| FINDSTR /r ".*\\%%i\\.*" ^| FINDSTR /v /r ".*\\%%i\\.*\\.*"'
) DO (
FOR /f %%c IN ( 'DIR /a:d %%s ^|FIND /c "<" ' ) DO IF %%c==2 ECHO SELECTED : %%s
)
)
Here's the run results:
Here is a test structure
-----------------------------
u:\directory\another
u:\directory\yetanother
u:\directory\572
u:\directory\another\yetanother
u:\directory\another\yetanother\572
u:\directory\another\yetanother\1572
u:\directory\another\yetanother\5722
u:\directory\another\yetanother\572\wantthis
u:\directory\another\yetanother\572\andthis
u:\directory\another\yetanother\572\maywantthisidontknow
u:\directory\another\yetanother\572\572
u:\directory\another\yetanother\572\maywantthisidontknow\ignore
u:\directory\another\yetanother\1572\ignorethis
u:\directory\another\yetanother\5722\ignorethis
u:\directory\yetanother\572
u:\directory\yetanother\572\wantthis
u:\directory\572\wantthis
------Here is the textfile---------
23
753309
572
====Method 1==============
u:\directory\another\yetanother\572\wantthis
u:\directory\another\yetanother\572\andthis
u:\directory\another\yetanother\572\maywantthisidontknow
u:\directory\another\yetanother\572\572
u:\directory\yetanother\572\wantthis
u:\directory\572\wantthis
====Method 2==============
selected : u:\directory\another\yetanother\572\wantthis
selected : u:\directory\another\yetanother\572\andthis
selected : u:\directory\another\yetanother\572\maywantthisidontknow
selected : u:\directory\another\yetanother\572\572
selected : u:\directory\yetanother\572\wantthis
selected : u:\directory\572\wantthis
====Method 3 - to ignore ...\target\subdir that has any subdir ==============
SELECTED : u:\directory\another\yetanother\572\wantthis
SELECTED : u:\directory\another\yetanother\572\andthis
SELECTED : u:\directory\another\yetanother\572\572
SELECTED : u:\directory\yetanother\572\wantthis
SELECTED : u:\directory\572\wantthis
The two FINDSTR regex structures are
FINDSTR /r ".*\\%%i\\.*"
Any number of any characters, \, the target string, \, any number of any characters
FINDSTR /v /r ".*\\%%i\\.*\\.*"
Any number of any characters, \, the target string, \, any number of any characters,\, any number of any characters
BUT - the /v on FINDSTR means except lines matching...
I can make little sense of copy the sub-folder from a parent folder with a random name.
If the requirement is to copy into the selected directory from that directory's parent directory, then after verifying that the target directories being displayed by ECHO SELECTED : %%s replace ECHO SELECTED : %%s with
(
pushd %%s
xcopy ..\* . >nul
popd
)
The >nul suppresses xcopy reports
If it means something else, more information is required.