多层转义场景下保留引号实现z/OS MVS数据集cp复制问题求解
z/OS USS cp命令引号转义问题修复方案
根因定位
报错cp: target "//'WOODSMN.SSC.D051721.T200335.S90.CP037'" is not a directory由两个核心问题导致:
- 命令作用域错误:ssh远程执行的多命令没有整体包裹,
export _UNIX03=NO;后的cp命令实际在本地Docker的bash环境执行,自然无法识别MVS数据集路径 - 多层转义丢失:Perl、本地Docker bash、远程z/OS Korn shell三层环境都会消耗一层转义符,手动编写的转义层数不足,引号被提前解析消耗,cp命令拿到的目标路径格式不符合要求
修复方案
1. 修正远程命令作用域
ssh执行多命令时,需要把所有要在远程运行的命令用引号整体包裹,避免本地shell拆分执行:
# 修改RunSshCommand调用逻辑,将远程命令整体包裹 RunSshCommand(shell_quote("export _UNIX03=NO;cp ${OPTIONS} ${FULLY_QUALIFIED_HFS_FILE} ${FULLY_QUALIFIED_MVS_ARGUMENT}"));
2. 修正数据集目标路径转义
z/OS要求的最终cp参数格式为"//'数据集全称'",需增加转义层数确保三层解析后仍保留正确格式:
# 替换putMvsFileByHfs函数中的目标路径构建代码 my $FULLY_QUALIFIED_MVS_ARGUMENT = qq(\\"//\\'$FULLY_QUALIFIED_MVS_FILE\\'\\");
3. 修正-P参数转义
原-P参数中的括号转义层数不足,调整后代码如下:
my $MVS_FORMAT_OPTIONS = qq(-P "RECFM=FB,LRECL=287,BLKSIZE=6027,SPACE=\\\\($SSCJCL_STORAGE_UNIT,\\\\($SSCJCL_PRIMARY_EXTENTS,$SSCJCL_SECONDARY_EXTENTS\\\\))\\\\");
Perl代码优化建议
- 普通变量引用无需加
${}包裹,直接写$变量名即可,符合Perl编码规范 - 推荐使用
String::ShellQuote模块的shell_quote函数自动处理shell转义,避免手动转义遗漏 - 封装的RunScpCommand、RunSshCommand函数不要硬编码全局变量,改为参数传递可提升复用性
- 变量命名无需全部大写,小写下划线分隔的命名方式可读性更高
修复后核心代码示例
use strict; use warnings; use String::ShellQuote qw(shell_quote); use IPC::Run3; # 全局变量定义部分保持不变 my $SSCJCL_SOURCE_DIRECTORY = "/home/bluecost/"; my $SSCJCL_STORAGE_UNIT = "TRACK"; my $SSCJCL_PRIMARY_EXTENTS = "1"; my $SSCJCL_SECONDARY_EXTENTS = "1"; my $SSCJCL_HFS_LOCATION="/u/woodsmn"; my $SSCJCL_STAGING_HLQ = "WOODSMN"; my $COST_FILE="SSC.D051721.T200335.S90.CP037"; my $SSCJCL_USER_PW="mypass"; my $SSCJCL_USER_ID="woodsmn"; my $SSCJCL_HOST_NAME="bldbmsb.boulder.mycompany.com"; sub putMvsFileByHfs { my ($options, $local_file, $hfs_file, $mvs_file) = @_; RunScpCommand($local_file, $hfs_file); my $mvs_target = "//'$mvs_file'"; my $remote_cmd = join ';', ( 'export _UNIX03=NO', "cp $options $hfs_file " . shell_quote($mvs_target) ); RunSshCommand(shell_quote($remote_cmd)); } sub RunScpCommand { my ($source, $target) = @_; my ($out, $err); my $full_cmd = "sshpass -p $SSCJCL_USER_PW scp $source $SSCJCL_USER_ID\@$SSCJCL_HOST_NAME:$target"; print $full_cmd."\n"; run3 $full_cmd, undef, \$out, \$err; print $out."\n".$err."\n"; return ($out, $err); } sub RunSshCommand { my ($cmd) = @_; my ($out, $err); my $full_cmd = "sshpass -p $SSCJCL_USER_PW ssh $SSCJCL_USER_ID\@$SSCJCL_HOST_NAME $cmd"; print $full_cmd."\n"; run3 $full_cmd, undef, \$out, \$err; print $out."\n".$err."\n"; return ($out, $err); } # 调用示例 my $MVS_FORMAT_OPTIONS = qq(-P "RECFM=FB,LRECL=287,BLKSIZE=6027,SPACE=($SSCJCL_STORAGE_UNIT,($SSCJCL_PRIMARY_EXTENTS,$SSCJCL_SECONDARY_EXTENTS))"); putMvsFileByHfs( $MVS_FORMAT_OPTIONS, "$SSCJCL_SOURCE_DIRECTORY/$COST_FILE", "$SSCJCL_HFS_LOCATION/$COST_FILE", "$SSCJCL_STAGING_HLQ.$COST_FILE" );
内容的提问来源于stack exchange,提问作者Woodsman
相关产品推荐
相关产品推荐

