1

I have 10 text files in a folder and a jar file which will take three inputs one by one.

  1. Text file path
  2. Vendor Name
  3. Model

I want to execute the Jar file 10 times (num of text files). I have a code that will execute 10 times and feed the first input "text file path"

@echo off
for %%a in (*.txt) do (
    echo %%a|java -jar C:\Generator.jar
)
pause

I want to give the remaining two "Vendor name and Model" as console inputs one by one automatically. The vendor name and model should be populated by splitting text file name using underscore. Is there a way to achieve this ?

Ragupathy
  • 105
  • 1
  • 8
  • 1
    `for %%a in ("*.txt") do for /F "usebackq tokens=1-3 delims=_ eol=_" %%b in ("%%~a") do ((echo/%%b&echo/%%c&echo/%%d) | java -jar C:\Generator.jar)` – aschipfl Jul 05 '17 at 06:21
  • 1
    @aschipfl - Your use of `usebackq` would require `in ('%%~a')`. Also, the first input is the entire file name, not a value parsed from the name. But worst of all, each line of input to java will have an unwanted trailing space. See [my answer](https://stackoverflow.com/a/44927438/1012053) – dbenham Jul 05 '17 at 15:11
  • @dbenham, I misunderstood the question and thought vendor name and model are the content of the text files rather than their names; that's why I used `usebackq`. And you're right about the trailing spaces, my comment was just a quick shot, I didn't really think about them. Great answer, by the way! – aschipfl Jul 05 '17 at 18:57

1 Answers1

3

You can use for /f to split the file name into two tokens at the _ delimiter. You don't want to include the .txt extension, so you need to split %%~nA instead of %%A.

You could use dir with findstr to verify that the file name has valid format, and change the outer for loop to for /f to process that result. For example, you don't want to process a file name that looks like "vendor_.txt". My findstr command verifies there is only one _ and there is at least one non-underscore character both before and after.

One would think that the following would work

@echo off
:: This does not work - each line of input to java has an unwanted trailing space
for /f "delims=" %%A in (
  'dir /a-d /b *_*.txt ^| findstr /i "^[^_][^_]*_[^_][^_]*\.txt$"'
) do for /f "delims=_ tokens=1*" %%B in ("%%~nA") do (
  echo %%A
  echo %%B
  echo %%C
) | java -jar c:\generator.jar

But there is an insidious problem due to the fact that each side of the pipe is executed in a new cmd.exe process. Each side of the pipe must be parsed and packaged up into a cmd /c .... command. The left side ends up looking something like:
C:\Windows\system32\cmd.exe /S /D /C"echo fileName &echo vendor &echo model ",
with an unwanted space at the end of each line. Adding parentheses like (echo %%A) does not help.

There is another potential problem if any of the file names contain a poison character like &. If your vendor name is "Bell&Howell", then the left side process would attempt to execute
echo Bell&Howell. The "Bell" would echo properly, but the "Howell" would be interpreted as a command and would almost surely fail with an error, but in any case would not give the desired result.

See Why does delayed expansion fail when inside a piped block of code? for more information about potential issues with pipes, along with strategies for dealing with them.

Below is an enhancement to my original code that should fix the problems. I solve the poison character issue by putting the string literals within quotes, and then use a for statement that executes within the child cmd.exe process to safely strip the quotes before ECHOing the value.

I solve the unwanted trailing space issue by delaying parsing of the command by using an %%echoX%% "macro". The double percents delays expansion of the "macro" until after we are in the child process.

@echo off
setlocal disableDelayedExpansion

:: This echoX "macro" delays parsing of the echo command to avoid the unwanted trailing space
set "echoX=(echo %%~X)"

for /f "delims=" %%A in (
  'dir /a-d /b *_*.txt ^| findstr /i "^[^_][^_]*_[^_][^_]*\.txt$"'
) do for /f "delims=_" tokens=1*" %%B in ("%%~nA") do (
  (for %%X in ("%%~A" "%%B" "%%C") do @%%echoX%%) | java -jar c:\generator.jar
)

Another option is to define a multi-line variable with embedded linefeeds, and then use CMD /V:ON to enable delayed expansion in the child process.

@echo off
setlocal disableDelayedExpansion

for %%L in (^"^
%= This creates a quoted linefeed character in the FOR variable L =%
^") do for /f "delims=" %%A in (
  'dir /a-d /b *_*.txt ^| findstr /i "^[^_][^_]*_[^_][^_]*\.txt$"'
) do for /f "delims=_" tokens=1*" %%B in ("%%~nA") do (
  set "str=%%A%%~L%%B%%~L%%C"
  cmd /v:on /c "(echo !str!)" | java -jar c:\generator.jar
)

Here is a simpler alternative that uses a temporary file instead of a pipe. It avoids the many issues that can arise with pipes.

@echo off
for /f "delims=" %%A in (
  'dir /a-d /b *_*.txt ^| findstr /i "^[^_][^_]*_[^_][^_]*\.txt$"'
) do for /f "delims=_" tokens=1*" %%B in ("%%~nA") do (
  >temp.txt (
    (echo %%~A)
    (echo %%B)
    (echo %%C)
  )
  <temp.txt java -jar c:\generator.jar
  del temp.txt
)
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • This answer deserves to be marked as a favorite, if only it would be possible +1. –  Jul 05 '17 at 15:43