引号和斜线在多层中幸存下来

问题描述

目标 我需要有效地运行复制 (cp) 命令,但保留显式引号。这是必要的,以便 z/OS Unix 系统服务 Korn shell 将副本的目标正确识别为传统的 MVS 数据集。

复杂的是,此步骤是自动化流程的一部分。该命令由 Perl 生成。 Perl 是通过 ssh 在单独的 Docker 容器上执行的。除了 Perl 所需的转义之外,这增加了另一层需要解决的转义。

基本上,docker 正在做类似的事情

<table style="width: 337px; margin-left: auto; margin-right: auto" border="1">
  <tbody>
    <tr>
      <td style="width: 90px" colspan="9">
        <table style="width: 329px" border="1">
          <tbody>
        <tr class="hobbies">
        <td style="width: 105.6875px" onmouSEOver="changeImg('skating')">
            &nbsp; &nbsp; &nbsp; Skating
        </td>
        <td style="width: 110.3125px" onmouSEOver="changeImg('swimming')">
            &nbsp; &nbsp; &nbsp; Swimming
        </td>
        <td style="width: 130px" onmouSEOver="changeImg('cooking')">
            &nbsp; &nbsp; &nbsp; &nbsp; Cooking
        </td>
    </tr>
          </tbody>
        </table>
      </td>
    </tr>
    <tr style="text-align: center">
      <td style="width: 90px" colspan="9">
        <!-- Change the src attribute onmouSEOver -->
        <img id="imgtochange" src="skating.jpg" alt="skating" />
      </td>
    </tr>
  </tbody>
</table>
<br />

生成必要的 SSH 命令,将它们发送到试图运行它们的大型机。当我运行 Perl 脚本时,它会生成命令

perl myprogram.perl

然后主机返回错误

sshpass -p passwd ssh woodsmn@bldbmsb.boulder.mycompany.com export _UNIX03=NO;cp -P "RECFM=FB,LRECL=287,BLKSIZE=6027,SPACE=\(TRACK,\(1,1\)\)" /u/woodsmn/SSC.D051721.T200335.S90.CP037 "//'WOODSMN.SSC.D051721.T200335.S90.CP037'"

需要sshpass,因为我的系统管理员拒绝打开授权用户,所以我唯一的选择是运行sshpass并输入密码。密码暴露被包含在内,我们不担心这个。

第一条命令

cp: target "//'WOODSMN.SSC.D051721.T200335.S90.CP037'" is not a directory

告诉 z/OS 将 -P 选项视为 MVS 数据集控制块的指示符。也就是说,这是我们告诉系统的地方,嘿,这是一个固定长度的 287 个字符,在轨道中分配等。数据集将被假定为新的。

对于复制命令,我希望 z/OS 复制 HFS 文件(基本上是一个普通的 UNIX 文件

export _UNIX03=NO

进入完全合格的MVS数据集

/u/woodsmn/SSC.D051721.T200335.S90.CP037

有时 MVS 命令假定一个高级限定符基本上是用户 userid,并允许用户忽略它。在这种情况下,我已经明确指定了这一点。

要让 z/OS 将目标视为数据集,需要在它前面加上两个斜杠 (/),所以 // 要使用完全限定的数据集,名称需要用撇号 (') 括起来

但是,为了避免在 Korn shell 中混淆,目标需要用双引号 (") 括起来。

因此,不知何故,在 Perl、Docker 容器内运行我的 SSH 命令的 shell(可能是 bash)和 z/OS 上的接收 Korn shell 之间,它没有被正确解释。

我缩小的 Perl 看起来像:

WOODSMN.SSC.D051721.T200335.S90.CP037

请原谅以上任何 Perl 的不当之处,因为我是 Perl 的新手,尽管感谢善意的建设性指针。

解决方法

首先,让我们构建要传递给程序的值。我们稍后会担心构建 shell 命令。

my @OPTIONS = (
   -P => join(',',"RECFM=FB","LRECL=287","BLKSIZE=6027","SPACE=($SSCJCL_STORAGE_UNIT,($SSCJCL_PRIMARY_EXTENTS,$SSCJCL_SECONDARY_EXTENTS))",),);

my $FULLY_QUALIFIED_LOCAL_FILE = "$SSCJCL_SOURCE_DIRECTORY/$COST_FILE";
my $FULLY_QUALIFIED_HFS_FILE   = "$SSCJCL_HFS_LOCATION/$COST_FILE";
my $FULLY_QUALIFIED_MVS_FILE   = "$SSCJCL_STAGING_HLQ.$COST_FILE";

my $FULLY_QUALIFIED_MVS_ARGUMENT = "//'$FULLY_QUALIFIED_MVS_FILE'";

简单易行。


现在是构建要执行的命令的时候了。关键是要避免尝试同时进行多个级别的转义。首先构建远程命令,然后然后构建本地命令。

use String::ShellQuote qw( shell_quote );

my $scp_cmd = shell_quote(
   "sshpass",-p => $SSCJCL_USER_PW,"scp",$FULLY_QUALIFIED_LOCAL_FILE,"$SSCJCL_USER_ID\@$SSCJCL_HOST_NAME:$FULLY_QUALIFIED_HFS_FILE",);

run3 $scp_cmd,...;

my $remote_cmd =
   '_UNIX03=NO ' .
   shell_quote(
      "cp",@OPTIONS,$FULLY_QUALIFIED_HFS_FILE,$FULLY_QUALIFIED_MVS_ARGUMENT,);

my $ssh_cmd = shell_quote(
   "sshpass","ssh",$remote_cmd,);

run3 $ssh_cmd,...;

但是由于您使用的是 run3,所以有一个更好的解决方案。您可以完全避免在本地主机上创建 shell,从而完全避免必须为其创建命令!这是通过传递对包含程序及其参数的数组的引用而不是传递 shell 命令来完成的。

use String::ShellQuote qw( shell_quote );

my @scp_cmd = (
   "sshpass",);

run3 \@scp_cmd,);

my @ssh_cmd = (
   "sshpass",);

run3 \@ssh_cmd,...;

顺便说一句,在命令行中传递密码是不安全的;机器上的其他用户可以看到它们。


顺便说一下,VAR=VAL cmd(作为单个命令)为 cmd 设置了环境变量。我使用了上面的速记。

,

指定磁盘单元的参数是“TRK”而不是“TRACK”,所以必须是

-P "RECFM=FB,LRECL=287,BLKSIZE=6027,SPACE=\(TRK,\(1,1\)\)"

此外,当从 SSH 会话以交互方式运行这样的命令时,我从来不必逃避括号。所以这对我有用

-P "RECFM=FB,SPACE=(TRK,(1,1))"

然后,错误

cp: target "//'WOODSMN.SSC.D051721.T200335.S90.CP037'" is not a directory

表示 cp 知道它必须复制多个源文件,因此它要求最终路径名是目录。似乎确认 cp 没有在远程主机上运行,​​而是在您的本地 shell 上运行(正如有人指出的那样是由于没有转义分号造成的)。并且您的本地 UNIX 不理解 z/OS 特定的MVS 数据集符号//'your.mvs.data.set'

除了导出 _UNIX03=NO,您还可以替换

-P "RECFM=FB,1))"

-W "seqparms='RECFM=FB,1))'"

那么,只需要运行一个命令。