跳转至主要内容

SFTP是作为SSH一部分的FTP子系统,它允许你进行加密的文件传输。在过去的一周里,我遇到了两个关于SFTP不能正确传输文件的问题。在一个案例中,OpenVOS是文件的来源,而在另一个案例中,OpenVOS正在接收文件。在这两种情况下,文件都包含ASCII数据,问题与Microsoft Windows和OpenVOS在文本文件中终止行的方式不同有关。

让我们从OpenVOS作为源头开始。如果你用你喜欢的编辑器创建一个文本文件,你就会创建一个连续的文件。比如说

d test
%phx_vos#m16_mas>SysAdmin>Noah_Davids>test  10-09-05 08:13:49 mst
12345
67890

ready 08:13:49

图1 - 显示一个文本文件
display_file_status命令显示这是一个连续的文件,虽然数据字节数是10,但实际上文件中有20个字节。
display_file_status test
name: %phx_vos#m16_mas>SysAdmin>Noah_Davids>test
file organization: sequential file
. . .
next byte:                 20
blocks used: 1
. . .
record count: 2
data byte count:           10

ready 08:14:01

图2 - display_file状态
dump_file命令显示了文件的实际结构。每个 "行 "的前面和后面都有两个字节表示其长度。这些长度字节必须在偶数字节边界上开始,所以还有一个填充字节。值得注意的是,没有典型的行终止字符,无论是换行(0x0A)还是回车换行(0x0D0A)序列。
dump_file test%phx_vos#m16_mas>SysAdmin>Noah_Davids>test 10-09-05 08:14:07 mst
区块编号1000  00053132 333435FF 00050005 36373839 |..12345…..6789|010 30FF0005 0...............|020 FFFFFFFF FFFFFFFF FFFFFFFF |................| FFFFFFFF FFFFFFFF FFFFFFFF |................|

=

FF0 FFFFFFFF FFFFFF FFFFFFFF |................|

准备好 08:14:07

图3 - dump_file显示文件的实际结构

 

当你使用SFTP传输文件时,OpenVOS将长度字节从行中剥离并附加一个换行字符。其结果是,虽然原始文件包含10种数据类型,但传输的文件包含12种。
C:Documents and SettingsnoahMy Documentstemp>"C:Program FilesPuTTYpsftp" nd@164.152.77.128
Using username "nd".
nd@164.152.77.128's password:
Remote working directory is /SysAdmin/Noah_Davids
psftp> get test test.txt
remote:/SysAdmin/Noah_Davids/test => local:test.txt
psftp> quit
C:Documents and SettingsnoahMy Documentstemp>dir
Volume in drive C has no label.
Volume Serial Number is 38B1-9C13
Directory of C:Documents and SettingsnoahMy Documentstempblog - sftp
08/27/2010 01:50 PM <DIR> .
08/27/2010 01:50 PM <DIR> ..
08/27/2010 01:50 PM 12 test.txt
1 File(s) 12 bytes
2 Dir(s) 39,471,644,672 bytes free
图4 - 转移的文件长度与源文件长度不一致
此外,如果你在记事本之类的软件中显示该文件,它显示为一行,并在各行应该终止的地方有一些有趣的符号。
图5 - 在记事本中显示的文件
如果你用一个工具来获取文件的十六进制转储,你会看到在每一行的末尾都有一个换行字符(0x0A)。微软Windows的标准行终止序列是回车换行(0x0D0A),记事本不知道如何解释换行字符,尽管有其他编辑器可以正确显示该文件。

 

图6 - 转移文件的十六进制转储
其结果是,传输的文件似乎被破坏了,长度不同,而且你无法显示它--至少在记事本中是这样。在传输前将文件转换为流文件将纠正长度问题;现在两个文件将显示相同的长度,但行终止的问题将继续存在。

 

现在,如果OpenVOS从微软的Windows系统中接收一个文件,该怎么办?正如我已经说过的,微软Windows使用回车换行序列来终止一个行。
图7 - 在Microsoft Windows系统上创建的文件的十六进制转储
OpenVOS SFTP子系统将创建一个流文件。换行字符将被视为行终止字符,回车字符将被视为该行数据的一部分。

dump_file pc1.txt

%phx_vos#m16_mas>SysAdmin>Noah_Davids>pc1.txt 10-09-05 08:52:21 mst
区块编号1

000 61626364650D0A666768696A0D0AFFFF|abcde...fghij....|

010 FFFFFFFF FFFFFF FFFFFF FFFFFFFF |................|

=

FF0 FFFFFFFF FFFFFF FFFFFFFF |................|

准备好 08:52:21

图8 - 转移文件的转储文件
display命令将正确显示该文件。

d pc1.txt

%phx_vos#m16_mas>SysAdmin>Noah_Davids>pc1.txt 10-09-05 08:54:25 mst

abcde

fghij

准备好 08:54:25

图9 - 显示已传输的文件
但当你去实际读取文件时,回车符会出现在每一行的末尾。
test_system_calls
tsc: s$attach_port p pc1.txt
tsc: s$open
tsc: s$seq_read p
Buffer length = 6
00000000 61626364 650D |abcde. |
tsc: s$seq_read p
Buffer length = 6
00000000 66676869 6A0D |fghij. |

tsc:

图10 - 读取文件时显示回车字符
同样,结果是该文件似乎被破坏了。
下面的Perl脚本可以用来添加或删除回车字符。你想在将文件传输到微软Windows系统之前添加它们,并在将文件从微软Windows系统中传输出来之后删除它们。
# cr.pl begins here
#
# cr
# version 1.0 10-08-27
# Noah.Davids@stratus.com
#
use strict;
use warnings;
use Getopt::Long;
my ($inFile, $outFile, @files, $add, $remove);
my ($INFILE);
my ($result, $count, $verbose, $addremove);
$result = GetOptions ('in=s' => $inFile,
'out=s' => $outFile,
'add' => $add,
'remove' => $remove,
'verbose=s' => $verbose);
if (($result != 1) || !defined ($inFile) || !defined ($outFile))
{
print "nnUsage:n";
print "perl cr.pl -in PATH -out PATH [[-add] | [-remove]] [-verbose]}n";
exit;
}
if (defined ($add) && defined ($remove))
{
print "You can only specify -add or -remove not bothnn";
print "nnUsage:n";
print "perl cr.pl -in PATH -out PATH [[-add] | [-remove]] [-verbose]}n";
exit;
}
@files = glob ($inFile);
if (@files < 1) {print "nNo files found for " . $inFile . "nn";}
if (@files > 1) {print "nMore than 1 file found for " . $inFile . "nn";}
open (OUT, ">".$outFile) || die "Can't open output file " . $outFile . "nn";
open ($INFILE, $files[0]) || die "Can't open input file " . $files[0] . "nn";
if (!defined ($verbose)) { $verbose = -1; }
$count = 0;
while ($_ = <$INFILE>)
{
if (defined ($remove))
{
s/r//;
print OUT $_ ;
}
else
{
s/n//;
print OUT $_ . "rn";
}
$count++;
if (($verbose > 0) && ($count % $verbose) == 0)
{ print "Line " . $count . " of " . $files[0] . " processedn"; }
}
close $INFILE;
#

# cr ends here

图11 - 删除或添加回车符的Perl脚本
为什么FTP似乎可以工作?FTP有两种模式:ASCII和二进制。在ASCII模式下,从Microsoft Windows系统传输到OpenVOS的结果是一个连续的文件,其中的回车和换行字符都从记录中删除。在二进制模式下,从Microsoft Windows系统向Open VOS传输的结果是一个带有回车和换行字符的流文件--与SFTP的二进制模式传输完全一样。从OpenVOS到微软Windows系统的ASCII模式传输会产生一个每行末尾都有回车和换行字符的文件,而二进制模式传输会产生一个每行末尾只有换行字符的文件。同样就像SFTP的二进制模式传输一样。FTP RFC详细说明了如何终止行,但底线是,在ASCII模式下,客户和服务器可以根据本地操作系统的需要自由终止行,而在二进制模式下,客户和服务器没有这种自由裁量权。

© 2020 Stratus Technologies.