Hi I have this little command to copy files in a batch, which will help because I do this specific copy multiple times a day. The problem occurs while using the xcopy command. Everything is in order, but I am receiving this error: "Invalid path 0 files copied". Here is the code:
C:\Windows\System32\xcopy /Y "C:\Users\Ryan\Desktop\mmars_pub\" "C:\Users\Ryan\Desktop\Dropbox\MMARS\mmars_pub\"
I'm using the full path to the xcopy executable because I was having trouble configuring the path environment variable to function properly. I would imagine that it shouldn't affect the result though. I read somewhere about the "Prevent MS-DOS-based programs from detecting Windows" checkbox that should fix the issue, but I just can't seem to find that. Any help appreciated.
Original answer
Remove the ending backslash from the source folder path
C:\Windows\System32\xcopy.exe /Y "C:\Users\Ryan\Desktop\mmars_pub" "C:\Users\Ryan\Desktop\Dropbox\MMARS\mmars_pub\"
edited 2015/10/01
While the original question used a literal path, and the indicated solution will solve the problem, there is another option. For literal paths and in cases where the path is inside a variable and could (or not) end in a backslash, it is enough to ensure that the ending backslash (if present) is separated from the quote, including an ending dot.
xcopy /y "x:\source\." "x:\target"
xcopy /y "%myVariable%." "x:\target"
This ending dot will not interfere in files/folders names. If there is and ending backslash, the additional dot will simply refer to the same folder. If there is not ending backslash as in windows files and folders can not end their names with a dot, it will be discarded.
BUT if the output of the xcopy command will be processed, remember that this additional dot will be included in the paths shown.
note: The solutions are above the line. Keep reading if interested on why/where there is a problem.
Why xcopy "c:\source\" "d:\target\" fails but xcopy "c:\source" "d:\target\" works?
Both commands seems to have valid path references, and ... YES! both are valid path references, but there are two elements that work together to make the command fail:
the folder reference is quoted (note: it should be quoted, it is a good habit to quote paths as you never know when they will contain spaces or special characters)
xcopy is not an internal command handled by cmd but an executable file
As xcopy is an external command, its arguments are not handled following the cmd parser command line logic. They are handled by the Microsoft C startup code.
This parser follows two sets of rules, official rules
Arguments are delimited by white space, which is either a space or a tab.
A string surrounded by double quotation marks is interpreted as a single argument, regardless of white space contained within. A quoted
string can be embedded in an argument. Note that the caret (^) is not
recognized as an escape character or delimiter.
A double quotation mark preceded by a backslash, \", is interpreted as a literal double quotation mark (").
Backslashes are interpreted literally, unless they immediately precede a double quotation mark.
If an even number of backslashes is followed by a double quotation mark, then one backslash (\) is placed in the argv array for every
pair of backslashes (\\), and the double quotation mark (") is
interpreted as a string delimiter.
If an odd number of backslashes is followed by a double quotation mark, then one backslash (\) is placed in the argv array for every
pair of backslashes (\\) and the double quotation mark is interpreted
as an escape sequence by the remaining backslash, causing a literal
double quotation mark (") to be placed in argv.
and undocumented/non official rules (How Command Line Parameters Are Parsed)
Outside a double quoted block a " starts a double quoted block.
Inside a double quoted block a " followed by a different character (not another ") ends the double quoted block.
Inside a double quoted block a " followed immediately by another " (i.e. "") causes a single " to be added to the output, and the
double quoted block continues.
This parser sees the sequence \" found at the end of the "first" argument as a escaped quote that does not end/closes the argument, it is seen as part or the argument. And the "starting" quote of the "second" argument is just ending the double quoted block BUT not ending the argument, remember that arguments are delimited by white space.
So while it seems that the command line arguments are
v v v......argument delimiters
v.........v v..........v ......quoted blocks
xcopy "x:\souce\" "x:\target\"
^.......^ ^........^ ......argument data
arg #1 arg #2
arg #1 = x:\source\
arg #2 = x:\target\
the actual argument handled by xcopy is
v v .....argument delimiters
v......................v .....quoted block
xcopy "x:\souce\" "x:\target\"
^.....................^ .....argument data
arg #1
arg #1 = x:\source" x:\target"
When the ending backslash is removed or the additional dot included, the closing quote in the argument will not be escaped, it will close the quoted block and the space between arguments will be seen as a delimiter.
Related
I am trying to create a batch script to send a shortcut of a file to my start menu but I have ran into trouble setting the "Start in" option. I am using Shortcut from Optimum X to do it and this may be an error from their decade old program and not my code.
%~f1 = C:\Program Files (x86)\Example\Example.exe
%~dp1 = C:\Program Files (x86)\Example\
Running
shortcut /f:"%targetfolder%\%fileName%.lnk" /w:"%~f1" /a:c /t:%1
works as intended and creates a shortcut with the "Start in" set to the dir of the file. But running
shortcut /f:"%targetfolder%\%fileName%.lnk" /w:"%~dp1" /a:c /t:%1
fails when it encounters a space, for example if the path is in Program Files the error is
"The Parameter "Files" is invalid
The Syntax of the command is incorrect"
Sorry if this is not enough information I know very little Batch.
Your problem are not the spaces, but how the standard arguments are handled.
In your case, you are not passing the data to a internal command, but to a external executable that will use its own parser (the default C startup code). To this parser a backslash followed by a quote is a escaped quote that does not end the argument.
As your %~dp1 ends with a backslash, it escapes the quote, so the argument continues until the first unquoted space, in your case after Program in the /t switch. So, Files (in the /tswitch) is not recognized as a valid argument.
The usual approach is to remove the ending backslash with something like
for %%a in ("%~dp1.") do (
shortcut /f:"%targetfolder%\%fileName%.lnk" /w:"%%~fa" /a:c /t:"%~1"
)
Here the for command is used to process the path to the file with an added dot. This why when we retrieve the full path to the referenced element (%%~fa) the ending backslash will not be included.
But, if the target folder is the root of the drive, this approach will not work. The ending backslash can not be removed. So, another approach is to avoid the quote scape by simply escaping the backslash
shortcut /f:"%targetfolder%\%fileName%.lnk" /w:"%~dp1\" /a:c /t:"%~f1"
Now the quote is preceded by two backslashes that will be seen as a escaped backslash followed by the closing quote of the argument.
Is someone able to explain how cmd handles carats, in the following examples?
C:\>set ^^=test
C:\>echo %^%
test
C:\>echo ^%^%
test
C:\>echo %^^%
%^%
I figured that %^% would be handled as simply %%. I assume that the variable expansion is handled before the carat is considered, however that is a half arsed answer to a question that I'm sure could be more eloquently explained.
In batch -
#echo off
set ^^=test
echo %^%
echo ^%^%
echo %^^%
--
C:\>test.bat
test
test
ECHO is off.
It is because the order of how Batch processes each command line. Simply put, the variable expansion is performed before analyzing special characters. That is why the carat is consumed by the variable expansion before being removed as an escape character. This is also why percent characters have to be escaped by themselves %% instead of the standard carat ^ escape character.
Phase/order
1) Phase(Percent):
A double %% is replaced by a single %
Expansion of argument variables (%1, %2, etc.)
Expansion of %var%, if var does not exists replace it with nothing
For a complete explanation read this from dbenham Same thread: percent expansion
1.5) Remove all <CR> (CarriageReturn 0x0d) from the line
2) Phase(Special chars, " <LF> ^ & | < > ( ): Look at each character
If it is a quote (") toggle the quote flag, if the quote flag is active, the following special characters are no longer special: ^ & | < > ( ).
If it is a caret (^) the next character has no special meaning, the caret itself is removed, if the caret is the last character of the line, the next line is appended, the first charater of the next line is always handled as escaped character.
<LF> stops the parsing immediately, but not with a caret in front
For a full and great explanation (seriously bookmark this link!) see the answers here:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Direct Answer: https://stackoverflow.com/a/4095133/891976
Additionally to the answer of David Ruhrmann...
You create a variable named ^, as the parser will escape the second caret and remove the first one in the statement set ^^=test.
As David explained, the percent expansion phase is the first phase so it can expand even strange expressions like a <CR> character, but it's also the cause why you can't build a multiline percent expansion.
First the percents are expanded (and fails as there is only one) and then the mutliline caret is used to append the next line.
echo %va^
r%
But really confusing is the next caret example
set "^=one caret"
set "^^=two carets"
echo "%^%"
call echo "%%^%%"
The output is
"one caret"
"two carets"
It's because carets will be doubled by a CALL
I have a string like this:
Testing:"abc"def"ghi"
I want to get: abc"def"ghi and put it into a variable, is there any possible way?
I want a general way of doing that (first and last quotes, not first and fourth quotes)
This will work reliably as long as the string does not contain unquoted special characters like & | > < ^
#echo off
set str=Testing:"abc"def"ghi"extra
echo str = %str%
set "new=%str:*"=%
echo new = %new%
-- OUTPUT --
str = Testing:"abc"def"ghi"extra
new = abc"def"ghi
Explanation
There are two parts to this solution, both in the same line of code.
1) Remove all characters up through the first ".
This part uses the documented search and replace feature of variable expansion, with an asterisk before the search string. The following is an excerpt from the help gotten by typing HELP SET or SET /? from the command prompt.
Environment variable substitution has been enhanced as follows:
%PATH:str1=str2%
would expand the PATH environment variable, substituting each occurrence
of "str1" in the expanded result with "str2". "str2" can be the empty
string to effectively delete all occurrences of "str1" from the expanded
output. "str1" can begin with an asterisk, in which case it will match
everything from the beginning of the expanded output to the first
occurrence of the remaining portion of str1.
2) Find the last occurrence of " and truncate the string at that point.
The entire SET assignment expression can be enclosed in quotes, and the enclosing quotes will be discarded and all text after the last quote ignored. The statement below will define variable var to have a value of value.
set "var=value" this text after the last quote is ignored
If there is no last quote, then the entire remainder of the line is included in the value, possibly with hidden spaces.
set "var=This entire sentence is included in the value.
I am not aware of any official documentation for this feature, but it is an important tool for batch file development.
This process occurs after the expansion from part 1 has completed. So the the SET truncates at the last occurrence of " in the expanded value.
Sample batch execution:
test.bat /s v1.1 1,3,4,5
I want to split the parameter into three tokens using space as a delimiter. The result should be:
1st token = /s
2nd token = /v1.1
3rd token = 1,3,4,5
Then the 3rd token will be split again using comma as a delimiter
The code below splits the arguments using common delimiters such as space, comma, etc.
#ECHO OFF
SET PARAMS=
:_PARAMS_LOOP
SET PARAMS=%PARAMS%%1
ECHO %1
SHIFT
IF NOT "%1"=="" GOTO _PARAMS_LOOP
Execution:
test.bat /s v4.1 1,2,3,4
Result:
/s
v4.1
1
3
4
5
I just want to use space as a delimiter, then in the 3rd token(1,3,4,5) I will split it again using comma as a delimiter and echo each of it.
The issue is that cmd recognizes a space, tab, comma, semicolon, or equals sign as command line delimiters unless they are wrapped in doublequotes.
Delimiters
Some characters in the command line are ignored by batch files,
depending on the DOS version, wether they are "escaped" or not, and
often depending on their location in the command line:
commas (",") are replaced by spaces, unless they are part of a string
in doublequotes
semicolons (";") are replaced by spaces, unless they
are part of a string in doublequotes
"=" characters are sometimes
replaced by spaces, not if they are part of a string in doublequotes
the first forward slash ("/") is replaced by a space only if it
immediately follows the command, without a leading space
multiple spaces are replaced by a single space, unless they are part of a
string in doublequotes
tabs are replaced by a single space
leading spaces before the first command line argument are ignored
I know of several occasions where these seemingly useless "features" proved very
handy. Keep in mind, though, that these "features" may vary with the
operating systems used.
More on command line parsing can be found on the PATH and FOR
(especially FOR's interactive examples) pages.
http://www.robvanderwoude.com/parameters.php
I have a batch file which moves files from one folder to another. The batch file is generated by another process.
Some of the files I need to move have the string "%20" in them:
move /y "\\myserver\myfolder\file%20name.txt" "\\myserver\otherfolder"
This fails as it tries to find a file with the name:
\\myserver\myfolder\file0name.txt
Is there any way to ignore %? I'm not able to alter the file generated to escape this, such as by doubling percent signs (%%), escaping with / or ^ (caret), etc.
You need to use %% in this case. Normally using a ^ (caret) would work, but for % signs you need to double up.
In the case of %%1 or %%i or echo.%%~dp1, because % indicates input either from a command or from a variable (when surrounded with %; %variable%)
To achieve what you need:
move /y "\\myserver\myfolder\file%%20name.txt" "\\myserver\otherfolder"
I hope this helps!
The question's title is very generic, which inevitably draws many readers looking for a generic solution.
By contrast, the OP's problem is exotic: needing to deal with an auto-generated batch file that is ill-formed and cannot be modified: % signs are not properly escaped in it.
The accepted answer provides a clever solution to the specific - and exotic - problem, but is bound to create confusion with respect to the generic question.
If we focus on the generic question:
How do you use % as a literal character in a batch file / on the command line?
Inside a batch file, always escape % as %%, whether in unquoted strings or not; the following yields My %USERNAME% is jdoe, for instance:
echo My %%USERNAME%% is %USERNAME%
echo "My %%USERNAME%% is %USERNAME%"
On the command line (interactively) - as well as when using the shell-invoking functions of scripting languages - the behavior fundamentally differs from that inside batch files: technically, % cannot be escaped there and there is no single workaround that works in all situations:
In unquoted strings, you can use the "^ name-disrupter" trick: for simplicity, place a ^ before every % char, but note that you're not technically escaping % that way (see below for more); e.g., the following again yields something like My %USERNAME% is jdoe:
echo My ^%USERNAME^% is %USERNAME%
In double-quoted strings, you cannot escape % at all, but there are workarounds:
You can use unquoted strings as above, which then requires you to additionally ^-escape all other shell metacharacters, which is cumbersome; these metacharacters are: <space> & | < > "
Alternatively, unless you're invoking a batch file, , you can individually double-quote % chars as part of a compound argument (most external programs and scripting engines parse a compound argument such as "%"USERNAME"%" as verbatim string %USERNAME%):
some_exe My "%"USERNAME"%" is %USERNAME%
From scripting languages, if you know you're calling a binary executable, you may be able to avoid the whole problem by forgoing the shell-invoking functions in favor of the "shell-free" variants, such as using execFileSync instead of execSync in Node.js.
Optional background information re command-line (interactive) use:
Tip of the hat to jeb for his help with this section.
On the command line (interactively), % can technically not be escaped at all; while ^ is generally cmd.exe's escape character, it does not apply to %.
As stated, there is no solution for double-quoted strings, but there are workarounds for unquoted strings:
The reason that "^ name-disrupter" trick (something like ^%USERNAME^%) works is:
It "disrupts" the variable name; that is, in the example above cmd.exe looks for a variable named USERNAME^, which (hopefully) doesn't exist.
On the command line - unlike in batch files - references to undefined variables are retained as-is.
Technically, a single ^ inside the variable name - anywhere inside it, as long as it's not next to another ^ - is sufficient, so that %USERNAME^%, for instance, would be sufficient, but I suggest adopting the convention of methodically placing ^ before each and every % for simplicity, because it also works for cases such as up 20^%, where the disruption isn't even necessary, but is benign, so you can apply it methodically, without having to think about the specifics of the input string.
A ^ before an opening %, while not necessary, is benign, because ^ escapes the very next character, whether that character needs escaping - or, in this case, can be escaped - or not. The net effect is that such ^ instances are ultimately removed from unquoted strings.
Largely hypothetical caveat: ^ is actually a legal character in variable names (see jeb's example in the comments); if your variable name ends with ^, simply place the "disruptive" ^ somewhere else in the variable name, as long as it's not directly next to another ^ (as that would cause a ^ to appear in the resulting string).
That said, in the (very unlikely) event that your variable has a name such as ^b^, you're out of luck.
In batch files, the percent sign may be "escaped" by using a double percent sign ( %% ).
That way, a single percent sign will be used within the command line. from http://www.robvanderwoude.com/escapechars.php
I think I've got a partial solution working. If you're only looking to transfer files that have the "%20" string in their name and not looking for a broader solution, you can make a second batch file call the first with %%2 as the second parameter. This way, when your program tries to fetch the second parameter when it hits the %2 in the text name, it will replace the %2 with an escaped %2, leaving the file name unchanged.
Hope this works!
How to "escape" inside a batch file withoput modify the file**
The original question is about a generated file, that can't be modified, but contains lines like:
move /y "\\myserver\myfolder\file%20name.txt" "\\myserver\otherfolder"
That can be partly solved by calling the script with proper arguments (%1, %2, ...)
#echo off
set "per=%%"
call generated_file.bat %%per%%1 %%per%%2 %%per%%3 %%per%%4
This simply sets the arguments to:
arg1="%1"
arg2="%2"
...
How to add a literal percent sign on the command line
mklement0 describes the problem, that escaping the percent sign on the command line is tricky, and inside quotes it seems to be impossible.
But as always it can be solved with a little trick.
for %Q in ("%") do echo "file%~Q20name.txt"
%Q contains "%" and %~Q expands to only %, independent of quotes.
Or to avoid the %~ use
for /F %Q in ("%") do echo "file%Q20name.txt"
You should be able to use a caret (^) to escape a percent sign.
Editor's note: The link is dead now; either way: It is % itself that escapes %, but only in batch files, not at the command prompt; ^ never escapes %, but at the command prompt it can be used indirectly to prevent variable expansion, in unquoted strings only.
The reason %2 is disappearing is that the batch file is substituting the second argument passed in, and your seem to not have a second argument. One way to work around that would be to actually try foo.bat ^%1 ^%2... so that when a %2 is encountered in a command, it is actually substituted with a literal %2.