没有Hex字符1A结尾的批处理脚本合并文件

24

我正在使用一个简单的批处理脚本合并两个 ASCII 文件,类似这样:

COPY a.txt+b.txt c.txt /y /a
问题是,C语言中最后一个字符会被设置为1A,这是SUB的十六进制表示。 c.txt文件被输入到另一个可执行文件中,但该文件不喜欢末尾的1A
生成c.txt文件后,如果我在Notepad++中打开并删除最后一个字符,则文件可以正常工作。
如何合并a.txtb.txt而不会将1A附加到c.txt的末尾?
5个回答

38

/a/b 开关的放置非常重要。它们的表现取决于它们是在源文件名后还是目标文件名后放置。

当与目标文件名一起使用时,/a 会导致添加文件结束标记(ASCII 26)。实际上,您正在指定这个标记!

当与源文件名一起使用时,

/a 指定文件为 ASCII 格式,并且被复制到第一个 ASCII 26 文件结束标记之前但不包括该标记。该字符及其后面的任何内容都将被忽略。

/b 会将整个文件复制,包括任何文件结束标记和标记后面的内容。

当与目标文件名一起使用时,

/a 会在最后添加 ASCII 26 作为最后一个字符。

/b 不会在最后添加 ASCII 26。

您的解决方案

...虽然我没有测试过,但您应该使用

COPY a.txt+b.txt /a c.txt /b /y


啊,没意识到 /a 开关的位置很重要。谢谢。 - Ayush
1
我已经尝试了所有可能的开关组合和它们的位置,但仍然在结尾处得到ASCII 26字符 :/ - Ayush
5
明白了,通过执行COPY /b a.txt+b.txt c.txt /y使其工作。感谢您指明正确的方向。 - Ayush
@Andrew的推理是正确的,但参数的位置放错了,请检查我的示例。 - papo
好的,你是对的,这个限定符实际上适用于它之前的文件。需要注意的是,它也适用于之后的所有文件。这就是让我感到困惑的地方。 - papo

4
如果命令行中的文件列表之前或之后跟随 /a,则它适用于所有列出的文件,直到 copy 遇到 /b。在这种情况下,/b 适用于 /b 之前的文件。
/a 的效果取决于其在命令行字符串中的位置: - 如果 /a 跟随源,则 copy 命令将文件视为 ASCII 文件,并复制位于第一个文件结束字符(CTRL+Z)之前的数据。 - 如果 /a 跟随目标,则 copy 命令会在文件的末尾添加文件结束字符(CTRL+Z)。
如果 /b 指示命令解释器读取目录中指定的字节数,则 /b 是 copy 的默认值,除非 copy 合并文件。
如果命令行中的文件列表之前或之后跟随 /b,则它适用于所有列出的文件,直到 copy 遇到 /a。在这种情况下,/a 适用于 /a 之前的文件。
这是一种非常冗长的方式来表达以下内容: 当合并文件时,默认选项是 /a。 这意味着在您的代码片段中,/a 选项是多余的,并且无论 /a 放在哪里都会应用。
解决方法是使用 /b,这指示它在读取时忽略 #1A [DOS 文件结束] 字符,并在写入时不输出它。
与 /a 不同,如果源文件包含 #1A 字符,则 /b 的位置很重要。如果 /b 在命令的末尾,则文件将被截断到 #1A(但不包括 #1A)。
以下任何一种都可以纠正此行为:
COPY a.txt+b.txt c.txt /y /b
COPY a.txt+b.txt /b c.txt /y
COPY /b a.txt+b.txt c.txt /y

但仅在DOS文件结尾未用于表示文件结尾的情况下,以下内容才有效:

COPY a.txt /b + b.txt c.txt /y
COPY /b a.txt + b.txt c.txt /y

注意:为了更加混淆,将/b添加到源文件后面会将/b应用于其后的每个源文件,直到出现/a为止。
在正常操作中,这种行为可能看起来最多是奇怪的。由于DOS文件系统始终记录文件大小,因此文件结束字符应该是多余的。
引用: https://en.wikipedia.org/wiki/End-of-file 这样做有两个原因:
1. 与CP/M向后兼容。CP/M文件系统仅以128字节“记录”的倍数记录文件长度,因此按照惯例,如果在记录中间结束,就使用Control-Z字符标记有意义数据的结尾。 MS-DOS文件系统始终记录文件的确切字节长度,因此在MS-DOS上从未必要。 2. 它允许程序使用相同的代码从终端和文本文件读取输入。
总之,这使得可以从设备(例如COM端口)获取输入,或者向设备输出,同时仍然能够区分不同的文件。

https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/copy

您可以将设备名称替换为源或目标的一个或多个出现。


2

copy更改为type

type a.txt>c.txt
type b.txt>>c.txt

1
这肯定是有效的,所以加1分,但它需要 n 个命令来合并 n 个文件,不像适当的 copy 命令。 - AakashM

1

您可以将开关/a(ASCII文本)更改为/b(二进制)
还可以查看copy /?

因此,生成的命令是

COPY a.txt+b.txt c.txt /y /b

1

我原本认为@Andrew的示例是错误的,但实际上比我的更正确。

问题在于,[/A | /B]指定符可以双向工作。 这有点令人困惑。 copy /? 在第一个源文件之前显示 [/A | /B],但在每个其他源文件之后以及目标文件之后也会显示

COPY ... [/A | /B ] source [/A | /B] [+ source [/A | /B] ...] [destination [/A | /B]]

该指定符实际上适用于之前的文件,但然后也适用于其后的所有文件,包括目标文件。 但是,只有在命令行中找到相反的指定符时,后面的指定符才适用于所有文件,但也适用于前面的文件。

组合复制命令默认为ASCII
示例。

copy aa + bb + cc dd
ASCII ASCII ASCII ASCII

将所有要复制的文件指定为二进制,下面三个示例具有相同的效果:

copy /b aa + bb + cc dd
bin bin bin bin

copy aa /b + bb + cc dd
bin bin bin bin

copy aa + /b bb + cc dd
bin bin bin bin

还有一些测试:

copy aa + bb /a + cc dd
ASCII ASCII ASCII ASCII

copy aa + bb /b + cc dd
ASCII BIN BIN BIN

copy /b aa + bb + cc dd /a
bin bin bin ascii

copy aa /a + bb + cc dd /b
ASCII ASCII ASCII bin

copy aa + bb + cc dd /b
ASCII ASCII ASCII bin

copy aa + bb + cc /a dd /b
ASCII ASCII ASCII bin

但是,如果将源文件作为目标文件进行重复使用,则相同文件上的目标类型将覆盖源类型:
copy aa + bb + cc aa /b
BIN ASCII ASCII BIN

copy aa + bb + cc /b aa
BIN ASCII BIN BIN

这意味着我的原始样本实际上是将所有文件作为二进制进行复制,而开头的 /A 被覆盖了。现在它做的一样,但看起来更好。
@Andrew 的样本正在做他承诺的事情,只是那里的 /A 是无用的。
如果您想将一个文件添加到另一个文件中,就不需要第三个文件。只需再次使用第一个作为目标即可。不能使用第二个,否则在读取前会被覆盖。
这是我用来合并文件列表中所有文本文件的脚本。
@echo off

set concatenated=final.js

pushd %~dp0

set error=
copy nul "%concatenated%"
if errorlevel 1 set error=true

for /f %%a in (filelist.txt) do (
    echo. && echo.
    echo.   *** %%a
    copy /B /V "%concatenated%" + "%%a" "%concatenated%"
    if errorlevel 1 set error=true
)

popd
echo.
if defined error (echo.   !!!!!!!!!  THERE WERE ERRORS  !!!!!!!!!!
) else echo.   ***  ALL DONE  ***
echo.
pause
exit /b 

我的DOS手册明确指出:"/a应用于其前面的文件名和命令中剩余的所有文件名,直到copy遇到另一个/a/b开关。" 因此它应该在文件名之后。我想在不同的Windows shell中可能会有所改变。 - Andrew Leach
这是真的。前后都是如此,但另一个可以覆盖它。真是一团糟。 - papo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接