batch file compare end of filename to string - batch-file

I am trying to loop through a folder of files and rename them based on the last characters of the file. These files do not have extensions. My goal is:
file ends with TXT eg ItemTXT, move it to the same location and rename as Item.txt
file does not end with TXT, move it to the same location and rename as Item.xxx
I think I on the right track, but for some reason the if/else returns the else consition every time I test
#echo off
setlocal EnableDelayedExpansion
set pat=C:\Users\king\latest30
echo %pat%
for %%g in (%pat%\*) do (
set fnamelast3="%%~nxg:~-3%"
echo filename: %%~nxg fnamelast3: %fnamelast3%
if "%fnamelast3%" neq "TXT" (
echo %pat%/%%~nxg.fil
) ELSE (
rem does send with TXT
echo %pat%/%fname%.txt
)
)

Variables are not behaving as expected
even though you are setting delayedexpansion, you are not using it.
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign " or a terminal backslash or Space. Build pathnames from the elements - counterintuitively, it is likely to make the process easier.
You should find that %%g in (%pat%\*.) will select extensionless files.
set fnamelast3="%%~nxg:~-3%"
is invalid; you can't substring metavariables like %%g. You need to use
set "fname=%%~nxg"&set "fnamelast3=!fname:~-3!"&set "fnameexcept3=!fname:~0,-3!"
then use !fnamelast3! & !fnameexcept3! to rename your file - and you no longer need to detect txt since you will only be processing extensionless files which get their last three characters used as an extension

Related

Using xcopy or copy for a single file from multiple folders

So in the batch script I'm building I am taking a single file from a folder, copying it over to a destination folder, and renaming it based on the number of times that the script has been looped. Essentially I need to take a file that's named the samething from a bunch of different folders spread across multiple computers at times and copy them into a new folder to work with. I've read up on xcopy and copy as that seemed like the thing to use but I haven't been able to find anything that lets me tell it to only copy over a single named file. I've posted what I have so far for the script below with commented lines for the sections I haven't figured out:
ECHO off
SETLOCAL enabledelayedexpansion
ECHO Note: Your combined permission list cvs can be found in the desktop folder
SET /A #=-1
:start
SET /A #+=1
:again
ECHO Please input the file path to the permissionoutput.txt
SET /p permissionoutputpath=
SET "sourcefolder=%permissionoutputpath%"
SET "destinationfolder=C:\Users\kayla\Desktop\HOLDER-CombinedPermissionsLists"
IF not exist "%sourcefolder%\permissionoutput.txt" Echo file not found&goto again
copy "%sourcefolder%\permissionoutput.txt" "%destinationfolder%\permissionoutput%#%.txt"
ECHO Add another file to combine: y or n?
SET /p addanotherfile=
if %addanotherfile%==y goto :start
UPDATE: Code corrected with answer to be fully functional for use as a reference
SET /A #=-1
:start
SET /A #+=1
:again
ECHO Please input the file path to the permissionoutput.txt
SET /p permissionoutputpath=
SET "sourcefolder=%permissionoutputpath%"
SET "destinationfolder=C:\Users\kayla\Desktop\HOLDER-CombinedPermissionsLists"
IF not exist "%sourcefolder%\permissionoutput.txt" Echo file not found&goto again
copy "%sourcefolder%\permissionoutput.txt" "%destinationfolder%\permissionoutput%#%.txt"
ECHO Add another file to combine: y or n?
SET /p addanotherfile=
if /i "%addanotherfile%"=="y" goto start
# is a legitimate variable-name. It's initialised to -1 then incremented on each loop through :start so the first value it will have when it's used is 0. (If you want to start at 1 just initialise it to 0 instead)
Next - your sets - BUT spaces are significant in a string set command are would be included in the variablename/value assigned if present in the set instruction. "quoting the assignment" ensures any stray trailing spaces on the line are not included in the value assigned.
Well - next, make sure the file exists and if it doesn't, then produce a message and loop back to :again which bypasses the increment of #.
Otherwise, simply copy the file. You're aware of its sourcename, and your destinationname is constructed by including %#% to include the current value of # (all batch variables without exception are strings - the set /a instruction merely converts from string to binary to perform the required calculation, then converts the result back to a string for storage in the environment.)
Finally, interpreting the request to add another file. if /i makes the comparison case-insensitive. Since you have no direct control over the user's response, "quoting each side" ensures the if syntax isn't violated in case the user enters "yup sure 'nuff" or some other unexpected response.
The leading colon is not required in a goto. I prefer to omit it to keep conguity with the call command where no-colon means an external routine will be called and a colon means the routine is in this batch file.

Batch script Read multi line Argument in Variable

I wanted to take list of files to delete from user as a argument. One line per argument.
How can store the list of files separated by new line in a variable.
I am using below command.
Set DeletionFiles=${p:DeleteFiles}"
for %%i in (%DeletionFiles%) do (
echo %%i
)
Then i wanted to iterated them on a loop.
${p:DeleteFiles} will get replaced by it's value from external app, which will contain list of files separated by new line.I can not change it.
#ECHO OFF
SETLOCAL
SET "deletionfiles="
:dloop
SET "deleteme="
SET /p "deleteme=Which file to delete ? "
IF DEFINED deleteme SET "deleteme=%deleteme:"=%"
IF DEFINED deleteme SET "deletionfiles=%deletionfiles%,"%deleteme%""&goto dloop
ECHO delete %deletionfiles:~1%
GOTO :EOF
There is no need to use a newline. Your for command (or a del command) will operate perfectly happily on a comma-(or space-)separated list.
Note that there are certain characters that batch uses for special purposes and batch string-processing may not process them in the expected manner. These characters include % ^ and &.
${p:DeleteFiles} will get replaced by it's value from external app,
which will contain list of files separated by new line.I can not
change it.
After the replacement the batch file looks like:
Set DeletionFiles=file1.jpg
file2.jpg
file3.jpg
"
This isn't a valid batch file anymore.
Furthermore it's a bad idea to modify the batch file itself, as this works only once.
You could place the ${p:DeleteFiles} into another file, like input.txt.
Your batch would look like
echo ${p:DeleteFiles} > input.txt
<external program for replacing the DeleteFiles> input.txt
for /F "tokens=*" %%A in (input.txt) do (
echo File: %%A
)
If I understand you correctly, your external program will generate a list of files. You then want to store this multi-line list to a variable. What do you want to do with the variable once you have it? I assume you want to delete the files, but your question isn't clear on that point, so I'll try to over-answer to cover it.
for /f "delims=" %%a in ('{command that generates your list}') do (
echo Doing stuff to %%a...
echo %%a>>listOfFilesToDelete.txt
set var=%%a
if "%var:~0,7%"="DoNotDelete" copy "%%a" \someArchiveFolder\
del "%%a"
)
This will read each line in your generated list as variable %%a. It will then do whatever command(s) you specify. This way, you can run a command on each of the files in the list. In the above code it's
Printing each line to the console embedded in some text
Outputting it to a file
Checking the first 7 characters of the line against a specified string and then copying it to a folder if it matches
And then deleting it
If you still need to reference each line from your generated list, you can even setup an array-like structure. (See Create list or arrays in Windows Batch)
setlocal EnableDelayedExpansion
:: Capture lines in an 'array'
set /a i=0
for /f "delims=" %%a in ('dir /b') do (
set /a i+=1
set var!i!=%%a
)
:: Loop through the 'array'
for /L %%a in (1,1,%i%) do (
echo Do more stuff with !var%%a!
)
Just like above, this will read each line in your generated list as variable %%a. It will then set a variable var!i! equal to the value of the current line. You can then reference each line as var1, var2, and so on, or, as the second section shows, you can loop through them all using for /L. You'll need to get a grasp on working with delayed expansion variables, though.

batch read file without new lines

I've script that reads into variable content of files, but in some cases, files has new lines. Variable has loaded olny firest line. I use:
set /p varfile=file.txt
Is it possible to read contents of the file with ignored newlines? I cannot find solition wihtin batch commands.
Although this is probably not what you need, this is what you asked for. You can capture the entire contents of a file to a single variable (up to 8191 bytes, is it?) by using a for loop. Since for /f can read the contents of a file, you can finally eliminate that set /p statement you keep misusing.
#echo off
setlocal enabledelayedexpansion
rem // capture a line break to a variable
set BR=^
rem // Leave the two lines above blank.
for /f "usebackq delims=" %%I in ("file.txt") do (
if not defined contents (
rem // set contents variable to first line of file
set "contents=%%I"
) else (
rem // append line break + next line to variable
set "contents=!contents!!BR!%%I"
)
)
echo(!contents!
Now, what are you going to do with this variable now that it contains the entire contents of a text file?

Copy string of characters from the middle of filename to the beginning

Have 2,000 files, each with 51-62 character-long filenames. For each filename, I would like to add the character-stretch in position 14-23 to the very beginning of the filename. (Filename character types and patterns are variable, so the rename must be based on position counting from the filename's beginning.)
Windows 7, command prompt FCIV solution preferred.
For example:
original:
abcdefghijklmNo0123456p8901234567890.ext
new:
No0123456pabcdefghijklmNo0123456p8901234567890.ext
#ECHO OFF
SETLOCAL
SET "sourcedir=c:\folder"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*" '
) DO (
SET "name=%%a"
CALL :transform
)
GOTO :EOF
:transform
ECHO REN "%sourcedir%\%name%" "%name:~13,10%%name%"
GOTO :eof
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO REN to REN to actually rename the files.
The variable sourcedir is set to point to the directory of interest. By setting a variable, the directoryname can be changed in one place rather than editing it in multiple places should it be used in multiple places. The set "var=value" syntax ensures any trailing spaces on the line are not included in the value assigned.
The dir command produces a list of filenames in the target directory /b means "filenames only - no header/trailer lines, no date, time size - just the filename" /a-d means "and no directory names"
This list is built in memory, and then read line-by-line by the for/f. The "delims=" ensure that the entire line is applied to the metavariable %%a so that filenames that include default separators are not truncated. Each filename is then assigned to the variable name because substringing can't be applied directly to a metavariable. The subroutine :transform is then executed.
The subroutine is quite simple - all it does is echo a rename command, using the full filename built from the variables sourcedir and name. The syntax of the ren command requires that the new name is name-only, the subdirectory is implicit. The new name is built from the substring within name starting at character 13 (the first character is character 0) for a length of 10 characters + the original name.
Since the dir list is built in memory before the for starts executing, the names delivered to the for are the original names. If a file has become renamed, it will not be re-processed as its new name is not in that original list.
The idea of ECHOing the ren line is so that the user can see whether the ren commands generated are correct. All that happens is that a report is sent to the screen. If it is then determined that the result appears correct, then changing echo ren to ren will actually execute the rename rather than reporting it. That's a decision to be made by the user. If the resultant ren is wrong (wrong count of position or length, or wrong directory or whatever) then no harm is done as the product is simply a report until the job is run with echo ren changed to ren.

Batch - Recurse directories from a variable and expand results in another variable

I'm creating a simple production environment for work and in doing so need to set specific environment variables for specific projects in batch file.
Here's what i want to achieve:
1) Define a single environment variable which would define a list of directories
2) Recurse down each directory and add all leaf folders to a final environment variable.
[EDIT] After looking back at what i originally posted i was able to remove some redundancy. But the "The input line is too long." error occurs when %LINE% gets too long. Using the short path expansion does help but it can still error out. I'll look at how to break the echo to a temp file next as suggested.
Here's what i currently have:
#echo off
set RECURSE_THESE_DIRS=C:\Users\eric\Autodesk
set TMP_FILE=%CD%TMP_FILE.%RANDOM%.txt
setLocal EnableDelayedExpansion
for %%i in (%RECURSE_THESE_DIRS%) do (
if exist %%~si\NUL (
for /f "tokens=*" %%G in ('dir /b /s /a:d %%i') do set LIST=!LIST!;%%G
)
)
set LIST=%LIST:~1%
rem !!! AT THE ECHO LINE BELOW IF %LIST% IS TOO LONG, THIS SCRIPT FAILS
rem !!! WITH The input line is too long. ERROR :(
echo %LIST%>%TMP_FILE%
endlocal
for /F "delims=" %%G in (%TMP_FILE%) do set FINAL_VAR=%%G
del /F %TMP_FILE%
So by setting RECURSE_THESE_DIRS to directories i wish to parse, i end up with a %FINAL_VAR% which i can use to specify paths for proprietary software i use. Or i could use this script to append to %PATH%, etc...
This works for me but i would love suggestions to improve/streamline my script?
The root of your problem is that batch is limited to fit the variable name, contents and = into 8192 bytes, hence your directory-list simply isn't going to fit into one variable.
Personally, I'd just spit out a dir/s/b/a-d list to a tempfile and process that file with a for/f "delims=" - after all, you'd be likely to need to process your ;-separated envvar similarly in whatever process you are proposing to execute.
For instance, here's a test producing the same error - not using filenames at all
#ECHO OFF
SETLOCAL
SET "var=hello!1234"
SET var=%var%%var%%var%%var%%var%
SET var=%var%%var%%var%%var%%var%%var%%var%%var%
SET var=%var%%var%%var%%var%%var%
SET var=%var%%var%%var%%var%
SET count=8000
:loop
SET /a count +=1
ECHO %count%
SET var=%var%x
ECHO %var%
GOTO loop
GOTO :EOF
This should fail where count=8184.
Suggestions:
Use for /d /r to handle the recursion
Maybe i'm wrong, but in your script, you traverse the directory hierarchy, adding each directory to temp file which is then readed to concatenate its lines into a variable which is then writed to temp file, to be read again in another variable. If concatenation of directories fit into a variable, why not concatenate them without any temporary file?
If concatenation is in the limit of line length, as suggested by npocmaka, and if soported by end application, user short paths. Also, instead of adding the new values in the same instruction (not line) that contains the for loop, use a call to a subrutine with the new value passed to it, and then append the value. It's slower, but command lines executed will be shorter.

Resources