每个 LF 之前添加一个 CR

每个 LF 之前添加一个 CR

bcp我在 Windows 机器上处理一个文本文件。在使用实用程序将文件中的数据加载到数据库表之前,需要删除尾随的制表符。

Bash 脚本中的以下命令删除了尾随选项卡:

sed 's/[\t]*$//' < ./input/raw.txt >> ./input/data.txt

但它将CR-转换LF为导致命令失败的LF原因。bcp

为了努力保持CR-LF我尝试了这个:

sed 's/[\t]*$/$CR/' < ./input/raw.txt >> ./input/data.txt

但这导致:

在此输入图像描述

期望的结果是:

在此输入图像描述

如何修改命令以获得所需的输出?

答案1

您需要安装该unix2dos软件包。它有两个实用程序:

unix2dos    Convert UNIX newlines to CR-LF
dos2unix    Convert DOS CR-LF to UNIX newlines

让我们创建一个包含五行的测试文件,并执行十六进制转储来检查行结尾:

$ jot -w 'line %d' 5 > foo
$ hexdump -C foo
00000000  6c 69 6e 65 20 31 0a 6c  69 6e 65 20 32 0a 6c 69  |line 1.line 2.li|
00000010  6e 65 20 33 0a 6c 69 6e  65 20 34 0a 6c 69 6e 65  |ne 3.line 4.line|
00000020  20 35 0a                                          | 5.|
00000023

我们看到每一行都以换行符(十六进制 0a)结尾。

现在我们将这些换行符转换为 DOS CR-LF 行尾,然后再次检查:

$ unix2dos foo
$ hexdump -C foo
00000000  6c 69 6e 65 20 31 0d 0a  6c 69 6e 65 20 32 0d 0a  |line 1..line 2..|
00000010  6c 69 6e 65 20 33 0d 0a  6c 69 6e 65 20 34 0d 0a  |line 3..line 4..|
00000020  6c 69 6e 65 20 35 0d 0a                           |line 5..|
00000028

现在每行以 CR-LF 结束,十六进制 0d 0a。

最后,我们可以将文件转换回原始的 UNIX 换行符:

$ dos2unix foo
$ hexdump -C foo
00000000  6c 69 6e 65 20 31 0a 6c  69 6e 65 20 32 0a 6c 69  |line 1.line 2.li|
00000010  6e 65 20 33 0a 6c 69 6e  65 20 34 0a 6c 69 6e 65  |ne 3.line 4.line|
00000020  20 35 0a                                          | 5.|
00000023

答案2

请注意,在标准中sed,删除行尾的sed 's/[\t]*$//'所有反斜杠和字符。 tGNU 实现仅在其环境中sed存在变量时才执行此操作。POSIXLY_CORRECT

sed 's/\t*$//'未指定,但至少对于 GNU 来说,无论是否在环境中,sed都会删除尾随的 TAB 。POSIXLY_CORRECT

在这里你可以这样做:

sed $'s/\t*$/\r/'

使用 ksh93 样式$'...'的引号形式,其中类似\t或 的内容\r分别扩展为 TAB 和 CR。现在许多其他 shell 都支持这一点,并且将出现在 POSIX 标准的下一版本中sh

如果 shell 变量中有 TAB 和 CR 字符,则可以不使用这些字符,$'...'例如:

eval "$(printf 'TAB="\t" CR="\r"')"

你可以这样做:

sed "s/$TAB*\$/$CR/"

但它必须在双引号内。在单引号内,不执行扩展。

现在,万一输入不以 LF 字符结尾(这将使其在 Unix 中成为无效文本),那些(sed至少对于 GNU)将生成一个以 CR 字符结尾的文件,使其在 Unix 中无效。 DOS也是如此。

要将文本文件从 Unix 转换为 DOS,您可以使用该unix2dos实用程序,这样就不会出现问题:

sed $'s/\t*$//' | unix2dos

或者使用perlsed模式:

perl -pe 's/\t*$//; s/\n/\r\n/'

perl -p其工作原理类似于sed它为每行输入运行代码,只不过在perl模式空间($_那里)具有包含行分隔符的整行。它还支持那些\t, \n,\r转义符(而标准sed仅支持\n且仅在正则表达式中),并且可以处理非文本文件。

答案3

使用(以前称为 Perl_6)

~$ cat unix2dos.raku
my $fh1 = open $*IN, :r;
#below :x opens write-only :exclusive (i.e. 'no-clobber')
my $fh2 = open $*OUT, :x, nl-out => "\r\n";

for $fh1.lines() { $fh2.put($_) };

$fh1.close;
$fh2.close;

Raku(又名 Perl6)是 Perl 家族中的一种编程语言。 Perl6 项目试图做的一件事是抽象出操作系统特定的问题,使代码更可移植,而这些问题之一就是换行处理。 Rakunl-in为文件句柄输入提供一个参数(默认为["\x0A", "\r\n"]),默认情况下自动剪切行,\n在内部使用 - 终止换行符,并nl-out为文件句柄输出提供一个参数(默认为"\n")。

OP的关键声明如下:

...但是它将 转换CR-LFLF导致 bcp 命令失败。

因此,对于上面的 Raku 脚本(在您正在使用的任何平台上),您可以打开一个文件进行写入并设置nl-out => \r\n,即换行符为 CRLF。 Raku 读取lines缓慢,因此该脚本应该节省内存。即使没有使上述脚本可执行,您也可以在命令行中调用它,如下所示:

~$ raku unix2dos.raku < ends_with_LF.txt > ends_with_CRLF.txt

上面的脚本默认采用$*INstdin,因此是“一次性”的,但 Raku也提供了读取$*ARGFILES和目录功能。dir最后,下面的第一个链接对 Raku 中的换行处理进行了精彩的总结:

https://docs.raku.org/language/newline.html
https://raku.org

相关内容