Batch - Remove Range/specific words

336 views Asked by At

In perforce I print the list of shelved/changes to the file with the following batch script :

for %%A IN (%ShelvedCHL%) DO (
    echo Change List: %%A 
    p4 -p %PPort% -c %PClient% unshelve -s %%A && if NOT %ERRORLEVEL%==0 exit -1
)>> list.txt

Here is my list.txt

Change List: 24536

//GSA/TOC.h#1 - unshelved, opened for edit
... /GSA/TOC.h - also opened by test@dev
... /GSA/TOC.h - also opened by test@dev
//odata/oenums.h#6 - unshelved, opened for edit
... //odata/oenums.h#6 - also opened by test@dev

I want to have the following output, basically remove sentence after - with dash as well : or even any perforce command to have less information and just list of files :

 //GSA/TOC.h#1 
 ... /GSA/TOC.h
 ... /GSA/TOC.h
 //odata/oenums.h#6
 ... //odata/oenums.h#6

I would appreciate any help, thanks in advance!

2

There are 2 answers

0
aschipfl On

At first, let us take a look at your code:

  1. I do not know the program p4, but I assume it is setting the ErrorLevel. So since this value is updated in the same block of code as you want to read it, you need to use delayed expansion, so place setlocal EnableDelayedExpansion on top of your script and use !ErrorLevel! instead of %ErrorLevel%. Another way is to replace if not !ErrorLevel!==0 by ifnot ErrorLevel 1, meaning if ErrorLevel is not greater than and not equal to 1, or expressed in a simpler way, if ErrorLevel is less than 1, but this works only if the program does not set a negative value.
  2. Even if you corrected the ErrorLevel issue, the if query would never eb executed because of the conditional command concatenation operator %%, because this lets the following command only execute in case the preceding one succeeded, meaning that its exit code1 equals zero. Therefore to execute the if statement, use the unconditional operator &. Anyway, there is also another conditional operator ||, which lets the following command only execute in case the exit code is a non-zero value; this one could replace your if condition completely.
  3. The exit command does not only quit the batch file, it also terminates the command prompt (cmd) instance which the batch script ran in. To quit the batch file only use exit /B instead.
  4. You are setting the ErrorLevel to -1 by exit -1. You can do this, of course, but usually negative values are avoided; hence let me suggest a positive value like 1 (by exit /B 1).
  5. You are opening and closing the file list.txt for every single iteration of the for loop. This reduces overall performance. Furthermore, if list.txt already exists, the data becomes appended; if you do not want that you need to place del "list.txt" 2> nul before the for loop to initially delete the file. Anyway, to write the entire file at once, put another pair of parentheses around the for loop. You can then chose whether to append to an already existing file using the redirection operator >>, or to overwrite it using operator > (without any need to delete it first).

All this results in the following improved script:

(for %%A in (%ShelvedCHL%) do (
    echo Change List: %%A 
    p4 -p %PPort% -c %PClient% unshelve -s %%A || exit /B 1
)) > "list.txt"

Depending on what %ShelvedCHL% contains (it seems to be 24536 in your sample data, so not a file path/name/mask), the for loop might even be superfluous, although I cannot know at this point...


Anyway, all of the above does still not yet account for removal of the partial string beginning with SPACE + - + SPACE, so let us implement this now:

To keep it simple, we could just modify the file list.txt after the above code, using this code (see all the explanatory rem remarks; the mentioned string manipulation is called sub-string substitution):

rem // Read file `list.txt` line by line:
(for /F "usebackq delims= eol=|" %%L in ("list.txt") do (
    rem // Assign line string to variable:
    set "LINE=%%L"
    rem // Enable delayed expansion to be able to do string manipulation:
    setlocal EnableDelayedExpansion
    rem /* Replace every occurrence of ` - ` by a single character `|`, then use this one
    rem    as a delimiter to split the line string as `for /F` requires single-character
    rem    delimiters; just using `-` is not good as they might occur in the partial
    rem    strings that need to be kept, I suppose; the `|` must not occur in them: */
    for /F "tokens=1 delims=| eol=|" %%K in ("!LINE: - =|!") do (
        rem // Disable delayed expansion to not lose `!`-marks:
        endlocal
        rem // Return the split string, that is the part before the (first) ` - `:
        echo %%K
    )
)) > "list_NEW.txt"

The resulting data is contained in the file list_NEW.txt. To have it in the original file, append the following line to the code:

move /Y "list_NEW.txt" "list.txt" > nul

1... Usually the exit code and ErrorLevel are the same, but there are in fact some rare cases where they may differ.

0
Samwise On

You can do this in p4 natively without doing hardly any scripting at all. Check out this blog post:

https://www.perforce.com/blog/fun-formatting

You can see the fields in a Perforce message by running it with the -e global opt*, like this:

p4 -e files ...

Then you can use any or all of those fields when reformatting the output, like this:

p4 -F "just the filename please: %depotFile%" files ...

*see also -Ztag which gives you an alternate dictionary of output fields