I'm renaming empty file extensions with this command:
rename *. *.bla
However, I have a folder with hundreds of such subfolders, and this command requires me to manually navigate to each subfolder and run it.
Is there a command that I can run from just one upper level folder that will include all the files in the subfolders?
for /r c:\path\ %G in (*.) do ren "%G" *.bla
#echo off
for /f "tokens=* delims= " %%i in ('dir /b/s/A-d') DO (
if "%%~xi" == "" rename "%%~fi" "%%~ni.bla"
)
Thanks #Wadih M. Find and rename files with no extension?
this will allow you to enter dirs with spaces in the names. (note the double % is for batch files, use a single % for command lines.)
for /f "tokens=* delims= " %%a in ('dir /b /ad /s') do rename "%%a\*." "*.bla"
You can easily do this and many more things with the Perl module File::Find.
#!perl
use strict;
use warnings;
use File::Find;
my $extension = 'bla';
my $directory = '/tmp/test';
print "Files renamed:\n";
find( \&wanted, $directory );
sub wanted {
return if /\./;
return unless -f $File::Find::name;
if ( rename( $File::Find::name, "$File::Find::name.$extension" ) ) {
print " $File::Find::name -> $File::Find::name.$extension\n";
}
else {
print " Error: $! - $File::Find::name\n";
}
}
You can use for to iterate over subdirectories:
for /d %x in (*) do pushd %x & ren *. *.bla & popd
When using it from a batch file you would need to double the % signs:
for /d %%x in (*) do pushd %%x & ren *. *.bla & popd
Try this
for /R c:\temp %I in (*. ) DO Echo rename %I "%~nI.bla"
Using find and xargs and basename would make the expression easier to read
Prepare a rename shell script like this (corrected to handle multiple files)
#!/bin/sh
if [ $# -lt 3 ]
then
echo "usage: rename.sh sufold sufnew file1 (file2 file3 ...) "
exit
fi
sufold=$1
sufnew=$2
shift
shift
for f in $*
do
echo "to mv " $f `dirname $f`"/"`basename $f $sufold`$sufnew
mv $f `dirname $f`"/"`basename $f $sufold`$sufnew
done
Then you could invoke that to each file matched your criteria, including recursive folder search by find command
find . -name "*." | xargs rename.sh "." ".bla"
A more common sample
find . -name "*.htm" | xargs rename.sh ".htm" ".html"
Related
Here is my text file:
==================================================
Folder : D:\T\New folder
==================================================
==================================================
Folder : D:\T\Z-Ai
==================================================
==================================================
Folder : D:\T\Z-BiN
==================================================
I need to extract the paths from this file, so I have something like this:
D:\T\New folder
D:\T\Z-Ai
D:\T\Z-BiN
It seems I should use findstr TargetWord TargetFile.txt command. and Also it seems I can use regex like this: findstr /r "^[a-z][a-z]$ ^[a-z][a-z][a-z]$"
But I do not know how to loop through found targets or get the list of output. any help is really appreciated.
Based on your comment, you want to use the result to perform an xcopy task, it seems you really want something like this. Note I used example.txt as input file, and DESTINATION where you should add your destination, including the relevant xcopy switches you require:
#echo off
for /f "tokens=2*" %%i in ('type example.txt ^| findstr /i ":\\"') do xcopy "%%~j\*" DESTINATION
Alternatively we can use the findstr directly on Folder
#echo off
for /f "tokens=2*" %%i in ('type example.txt ^| findstr /i "Folder"') do xcopy "%%~j\*" DESTINATION
You can do like this :
#echo off
Title Extract list of path in a file using batch script
set "TxtList=MyList.txt"
Set "OutPutData=Output.txt"
Call :Extract "%TxtList%" "%OutPutData%"
Start "" "%OutPutData%"
Exit
::*****************************************************
:Extract <InputData> <OutPutData>
(
echo Data = WScript.StdIn.ReadAll
echo Data = Extract(Data,"[\w]:(\\[0-9\sA-Za-z\-]*)+"^)
echo WScript.StdOut.WriteLine Data
echo '************************************************
echo Function Extract(Data,Pattern^)
echo Dim oRE,oMatches,Match,Line
echo set oRE = New RegExp
echo oRE.IgnoreCase = True
echo oRE.Global = True
echo oRE.Pattern = Pattern
echo set oMatches = oRE.Execute(Data^)
echo If not isEmpty(oMatches^) then
echo For Each Match in oMatches
echo Line = Line ^& Trim(Match.Value^) ^& vbcrlf
echo Next
echo Extract = Line
echo End if
echo End Function
echo '************************************************
)>"%tmp%\%~n0.vbs"
cscript /nologo "%tmp%\%~n0.vbs" < "%~1" > "%~2"
If Exist "%tmp%\%~n0.vbs" Del "%tmp%\%~n0.vbs"
exit /b
::****************************************************
For Windows. you can use powershell.
select-string -Path c:\tmp\file.txt -Pattern '[A-Z]:(\\[0-9\ A-Za-z\-]*)+' -AllMatches | % { $_.Matches } | % { $_.Value }
In my opinion, For /F is all you need for the task. Although using Type may be useful in some situations, there's no need to use find.exe or findstr.exe for this task as you don't need to match a particular glob/pattern:
#For /F "EOL==Tokens=2*UseBackQ" %%A In ("TargetFile.txt")Do #"%__AppDir__%xcopy.exe" "%%B" "Destination\" /Options
Please note that it may be wise, if there's a chance that one or more of theses Folders do not exist, that you prepend using If Exist "%%B\". Importantly, if each of the lines containing the Folder paths, is space padded up to the end of its line, this solution will not work for you.
I am having trouble with this script. I'll explain below the codeblock.
#Echo off
pushd "\\server\folder"
SETLOCAL EnableDelayedExpansion
#FOR /F "TOKENS=2" %%A IN ('WHERE /T "testfiles*.*"') DO #(
set fdate123=%%A
echo !fdate123:~5,9!0!fdate123:~0,1!!fdate123:~2,2!
call StringLen !fdate123!
)
pause
:StringLen
Setlocal EnableDelayedExpansion
:: strLen String [RtnVar]
:: -- String The string to be measured, surround in quotes if it contains spaces.
:: -- RtnVar An optional variable to be used to return the string length.
Set "s=#%~1"
Set "len=0"
For %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%N,1!" neq "" (
set /a "len+=%%N"
set "s=!s:~%%N!"
)
)
Endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
Exit /b
What I'm trying to do it get the date modified of the file, and change the format of that date returned to YYYYMMDD. I want the date modified to be appended to the filename. I can have files from multiple days in this folder and each file may have a different date modified date.
Please do not mark this as a duplicate question, because I could not find this approach to doing this here.
I was trying to test for date string length so I can handle dates like 1/1/2019 (length of 8) vs 1/13/2019 (length of 9) vs 10/1/2019 (length of 9) vs 10/22/2019 (length of 10) then using if statements parse the date appropriately with the likes of something like !fdate123:~5,9!!fdate123:~0,1!!fdate123:~2,2! - I have not finished this yet.
I have tried getting the date with dir /T:W testfiles*.* and running a findstr but I don't understand findstr well enough to do that.
I also tried to pull it from forfiles /M BC_Services_Adjustment* /C "cmd /c echo #fdate" and moved on from that as well.
maybe somebody has a better solution, I think mine is a mess right now. Does anybody know how to get the date modified time stamp of every file in a folder, convert it a variable with YYYYMMDD format and then append it into all the files in a folder?
Note: I am not looking for powershell solutions for this question, please do not waste your time posting powershell answers.
Update #2 (5/21/19)
I tried magoo's code, I'm still needing a solution to rename the files.
#echo off
pushd "\\server\folder"
SETLOCAL EnableDelayedExpansion
FOR /F "TOKENS=2" %%A IN ('WHERE /T "*.csv"') DO (
for /f "tokens=1-3delims=/-." %%i in ("%%A") do (
set "mm=00%%i"&set "dd=00%%j"&set "yy=0000%%k"
set "fdate123=!yy:~-4!!mm:~-2!!dd:~-2!"
)
rem echo to test if date modified matches to the right filenames.
echo !fdate123! ^& %%A
rem ren "%%~nxA" "%%~nxA!fdate123!"
)
pause
I have tried with the ren "%%~nxA" "%%~nxA!fdate123!" but it's not finding the file. Probably super simple. If somebody can make magoo's code do a rename instead of just echoing the date I can award out the bounty on this question.
Be sure to specify your extentions so if your bat file is in that directory, it will not also be renamed.
#echo off
pushd "\\server\path123"
SETLOCAL EnableDelayedExpansion
FOR /r %%f IN (*.csv, *.txt) DO SET filedatetime=%%~tf & ren "%%~nf.*" "%%~nf!filedatetime:~6,4!!filedatetime:~0,2!!filedatetime:~3,2!%%~xf
FOR /F "TOKENS=2" %%A IN ('WHERE /T "testfiles*.*"') DO (
for /f "tokens=1-3delims=/-." %%i in ("%%A") do set "mm=00%%i"&set "dd=00%%j"&set "yy=0000%%k"
set "fdate123=!mm:~-2!!dd:~-2!!yy:~-4!"
echo !fdate123!
)
should allow you to construct the data according to your wishes.
The inner if assigns mm, dd and yy as appropriate, using the delimiters specified analysing %%A as a literal. Each is prefixed by an appropriate number of zeroes. The required string is then constructed using substringing selecting the last 2/4 characters of the string, so an 8-character output is obtained.
I use dd/mm/yyyy format and haven't actually tested this method, but manipulating it to your requirements should be obvious, the only issue really being how to handle yy dates as distingct from yyyy dates, if that's a concern.
I figured this out on my own, pure batch solution.
Takes date modified, appends it to any filename in a directory in YYYYMMDD format. I really was overcomplicating it, can't believe I didn't come up with this prior to setting a bounty. lol
pushd "\\server\folder"
SETLOCAL EnableDelayedExpansion
FOR /r %%f IN (*) DO SET filedatetime=%%~tf & ren "%%~nf.*" "%%~nf!filedatetime:~6,4!!filedatetime:~0,2!!filedatetime:~3,2!%%~xf"
Yes, I read that you do not want any PowerShell answers. Please be sure not to select this one as the answer. I did not waste my time writing something for you. This is for someone else who might get some benefit.
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[string]$CommandName
)
$dirlist = #('.') + ($Env:Path.Split(';'))
$extensions = #('') + ($Env:PATHEXT.Split(';'))
$results = foreach ($dir in $dirlist) {
if (($dir) -and ($dir -ne '')) {
if (Test-Path -Path $dir -ErrorAction SilentlyContinue) {
# The directory path exists.
# Check for presence of the file with any executable extension.
$dirhash = #{}
foreach ($extension in $extensions) {
Get-ChildItem -File -Path $dir -Filter "$CommandName$extension" |
ForEach-Object {
# If the file name is not already in the hash, add it to the hash
# and output it in the pipeline.
if (-not $dirhash.ContainsKey($_.Name)) {
$dirhash.Add($_.Name,1)
$_
}
}
}
}
}
}
$results | ForEach-Object {
Rename-Item -LiteralPath $_.FullName -NewName ($_.BaseName + $($_.LastWriteTime.ToString('yyyyMMdd')) + $_.Extension)
}
UPDATE:
Now that the OP's intent is known, this is a much more simple problem. Once you are confident the files will be renamed correctly, remove the -WhatIf from the Rename-Item cmdlet.
Set-Location -Path '//server/sharename'
Get-ChildItem -File -Filter 'testfiles*.*' |
ForEach-Object {
Rename-Item -LiteralPath $_.FullName -NewName ($_.BaseName + $($_.LastWriteTime.ToString('yyyyMMdd')) + $_.Extension) -WhatIf
}
I believe that the output from robocopy is in a consistent format, so I would suggest this as a possible single line batch-file option:
#PushD "\\server\folder" 2>Nul&&For /F "Tokens=1-4*Delims=/ " %%A In ('RoboCopy . $ testfiles*.* /L /NS /NJS /NJH /NDL /NC /TS')Do #Ren "%%E" "%%A%%B%%C-%%~nxE"
Alternatively, based upon your edited additional example code, this is more likely what you need:
#PushD "\\server\folder" 2>Nul&&For /F "Tokens=1-4*Delims=/ " %%A In ('RoboCopy . $ testfiles*.* /L /NS /NJS /NJH /NDL /NC /TS')Do #Ren "%%E" "testfiles%%A%%B%%C%%~xE"
If each renamed file is run through the for loop again, you may need to expand this from a single line solution to prevent it.
In both code examples, the Delimiters are /TAB
If I'm on windows 7 and I have a spreadsheet:
1, Ant
2, Brown Bear (note whitespace)
3, Cat
4, Dinosaur
And folders
C:\Directory\1\
C:\Directory\2\
C:\Directory\4\
Is there any way to put in a batch file or a vbs file or whatever the appropriate tool is in that directory that would rename those folders using the spreadsheet? With the final result being:
C:\Directory\1 Ant\
C:\Directory\2 Brown Bear\
C:\Directory\4 Dinosaur\
So far I have a batch file that will append a string to all the folders in the directory it's run in (be cautious where you run it), but I'd just be using if statements for many, many items. Any suggestions on how to connect it to a csv file?
#echo off
for /f "tokens=1,2,* delims=,(" %%a in ('dir /a:d /b') do (
if "%%a" == "10" ( ren "%%a" "%%a Elephant" )
if "%%a" == "11" ( ren "%%a" "%%a Otter")
)
Thanks in advance.
There are a couple unclear points in your question (like the names: are they in a spreadsheet or in a .csv file?) so I made some assumptions. Given a names.csv file with this contents:
1, Ant
2, Brown Bear
3, Cat
4, Dinosaur
The Batch file below achieve the rename you want:
#echo off
setlocal EnableDelayedExpansion
rem Load the array of equivalences from names.csv file:
for /F "tokens=1* delims=," %%a in (names.csv) do set "name[%%a]=%%b"
rem Rename the folders:
cd C:\Directory
for /F "delims=" %%a in ('dir /A:D /B') do ECHO ren "%%a" "%%a!name[%%a]!"
Previous Batch file just show the ren commands; if the output looks good, remove the ECHO part in order to execute the commands.
If names.csv file have a header in the first line, insert "skip=1 " before "tokens" in the first for /F ... command.
Well, if you're OK with using Powershell this little script could help:
$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
Import-Csv "D:\Test\ps\look_up.csv" |`
ForEach-Object {
$old = $directorypath + "\" + $_.orig
if(Test-Path($old)){
$newPath = $directorypath + "\" +$_.orig + " " + $_.new
ren $old $newPath
}
}
I had folders named 1,2,3 in the same folder as the script and a csv file with the following content:
orig, new
1, buga boo
2, bla
3, bu
4, none
After running the script the folders were named
Name
----
1 buga boo
2 bla
3 bu
I am totally new on writing batch script,busy with the tutorials with the example below I can learn a thing or two.I really need help to write a batch script to insert a line of text at the middle of existing text file.
For example given the file myfile.txt with the contents:
a
bcd
efg
hjiklmnop
q
rs
t
uvwxyz
The the command ./put-in-middle.sh "=== === ===" myfile.txt
should modify the file to:
a
bcd
efg
hjiklmnop
=== === ===
q
rs
t
uvwxyz
#echo off
rem Count the number of lines in the file with FIND
for /F %%a in ('find /C /V "" ^< %2') do set numLines=%%a
rem Get the number of middle line
set /A middle=numLines/2
rem Process all lines, use FINDSTR /N to insert line numbers
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %2') do (
rem Echo the original line
echo/%%b
rem If the line is the middle one...
if %%a equ %middle% (
rem Insert the new line
echo %~1
)
)
Create previous Batch file as put-in-middle.bat and execute it this way:
put-in-middle "=== === ===" myfile.txt
Notes:
Previous program does not check for errors, like missing parameters. This checking may be added, if you wish.
The slash in the command echo/%%b is inserted to avoid the message "ECHO is on" if the line is empty. If the line may contain the string "/?", then the command should be changed to echo(%%b to avoid that the echo help be displayed in this case (the left parentheses is the only character that do that).
If the file contains Batch special characters, like < > | & ), the echo/%%b command fail. In this case, a special processing of files lines must be added. The same point apply to the new inserted line.
Previous program just display in the screen the new file. If you want to replace the original file, the output must be redirected to an auxiliary file and replace the original one at end:
.
(for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %2') do (
. . .
)) > auxiliar.txt
move /Y auxiliar.txt %2
Using sed and assuming even number of lines:
sed $(( $(wc input -l | cut -d' ' -f1) / 2))'a=== === ===' input
And this is the script version put-in-middle.sh:
line=$1
file=$2
sed $(( $(wc $file -l | cut -d' ' -f1) / 2))"a$line" $file
I'm trying to combine two directories on a Windows 7 machine that have some of the same subfolders (but not all) and some of the same files (but not all). I'd like to copy
For example, I have the following directory structure and want to combine (copy or move) "Dir_A" and "Dir_Z". Where the file exists in the destination directory, I want to compare filesizes and keep the larger of the two files.
Dir A
Dir B
file 1.txt
file 2.txt
file 3.txt
Dir C
file 4.txt
Dir D
file 5.txt
file 6.txt
Dir_Z
Dir Y
file 8.txt
Dir C
file 4.txt
Dir D
file 6.txt
I'm comfortable using a cmd file or powershell. Thank you in advance.
EDIT: Modify to include spaces in the director and file names and add what I have so far.
Here's what I've been able to do so far. It appears to work with the exception of when there are spaces in the directory or filenames.
#echo off
echo :: Combine two folders, Keep the larger of any given files, Delete the other
if "%1"=="" goto :NoParam
if "%2"=="" goto :NoParam
setlocal enabledelayedexpansion
set SOURCE_DIR=%1
set DEST_DIR=%2
for /R "%SOURCE_DIR%" %%S in (*) do (
echo Source: "%%S"
#echo off
for /f "delims=" %%R in ('call MakeRelative.cmd "%%S" "%SOURCE_DIR%"') do (
set FILE_REL="%%R"
set filename=%DEST_DIR%\%%R
For %%D in ("!filename!") do (
Set Name=%%~nxD
if exist %DEST_DIR%\%%R (
echo File Exists
if %%~zD lss %%~zS (
echo Destination %%~zD is smaller than Source %%~zS
call robocopy %%~dpS %%~dpD "!Name!" /MOV
) else del %%S && Echo File is not larger, Deleting %%S
) else call robocopy %%~dpS %%~dpD "!Name!" /MOV
)
)
)
Pause
goto :eof
:NoParam
echo.
echo Syntax: %0 [Source_DIR] [Dest_DIR]
goto :eof
I make use of the MakeRelative.cmd as outlined here: http://www.dostips.com/DtCodeCmdLib.php#MakeRelative
I apologize that this is not elegant.
I'm not much of a PowerShell developer, but this should do the work and is hopefully somewhat more obvious regarding what it's doing than the batch file.
if($args.length -ne 3) {
"usage: PowerShell ThisScriptName.ps1 -command ""SourceDirectory"" ""DestinationDirectory""";
Exit
}
function CopyFileIfSourceIsLarger($SourceFile, $DestinationFile) {
$S = New-Object IO.FileInfo($SourceFile.ToString());
$D = New-Object IO.FileInfo($DestinationFile.ToString());
if ($S.Length -gt $D.Length ) {
"Overwriting smaller file " + $D.FullName;
[IO.File]::Copy($S.FullName, $D.FullName, $true);
}
}
$SourceDir = New-Object IO.DirectoryInfo($args[1]);
$DestinationDir = New-Object IO.DirectoryInfo($args[2]);
$SourceDirectoryPath = $SourceDir.FullName;
$DestinationDirectoryPath = $DestinationDir.FullName;
"Source Path: " + $SourceDirectoryPath;
"Destination Path: " + $DestinationDirectoryPath;
$SourceItems = Get-ChildItem -recurse $SourceDir;
"Synchronizing Directory Structure..."
foreach ($dir in $SourceItems | ? { $_ -is [IO.DirectoryInfo] }) {
$sourceRelative = $dir.fullname.Substring($SourceDirectoryPath.length + 1);
if ([IO.Directory]::Exists([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative)) -eq $false) {
"Creating folder " + [IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative);
[IO.Directory]::CreateDirectory([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative)) | out-null;
}
}
"Synchronizing Files..."
foreach ($file in $SourceItems | ? { $_ -is [IO.FileInfo] }) {
$sourceRelative = $file.fullname.Substring($SourceDirectoryPath.length + 1);
if ([IO.File]::Exists([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative)) -eq $true) {
CopyFileIfSourceIsLarger ([IO.Path]::Combine($SourceDirectoryPath,$sourceRelative).ToString()) ([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative).ToString());
} else {
"Creating file " + [IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative);
[IO.File]::Copy([IO.Path]::Combine($SourceDirectoryPath,$sourceRelative).ToString(),[IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative).ToString()) | out-null;
}
}
"All done."
#Thanks to:
#"Identifying Object Types": http://powershell.com/cs/blogs/tobias/archive/2008/10/14/identifying-object-types.aspx
#"Exiting early from foreach": http://stackoverflow.com/questions/10277994/powershell-how-to-exit-from-foreach-object
#"Calling functions with multiple parameters" http://stackoverflow.com/questions/4988226/powershell-multiple-function-parameters
When the script runs, files 1,2,3,5,9, and 10 get copied, and file 6 gets overwritten because it is smaller in the destination.
Here is a batch file that sets up your test case (plus some extra folders to test spaces in the names). I have commented out the two lines that clear out directories A and Z for safety but you can remove the REM statements if you are unconcerned about deleting files in a folder A and Z in the current directory.
#ECHO OFF
REM Uncomment at own risk RD /S /Q A
REM Uncomment at own risk RD /S /Q Z
MD "A\B"
echo aaaaaaaabbbbbbbbcccccccc>"A\B\file 1.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\B\file 2.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\B\file 3.txt"
MD "A\C"
echo aaaaaaaabbbbbbbbcccccccc>"A\C\file 4.txt"
MD "A\D"
echo aaaaaaaabbbbbbbbcccccccc>"A\D\file 5.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\D\file 6.txt"
MD "A\FOLDER E"
echo aaaaaaaabbbbbbbbcccccccc>"A\FOLDER E\file 9.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\FOLDER E\file 10.txt"
MD "Z\Y"
echo aaaaaaaabbbbbbbbcccccccc>"Z\Y\file 8.txt"
MD "Z\C"
echo aaaaaaaabbbbbbbbccccccccdddddddd>"Z\C\file 4.txt"
MD "Z\D"
echo aaaaaaaa>"Z\D\file 6.txt"