gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 07:27:54 26 4
gpt4 key购买 nike

目标
我需要有效地运行复制 (cp) 命令,但保留显式引号。这是必要的,以便 z/OS Unix 系统服务 Korn shell 将副本的目标正确识别为传统的 MVS 数据集。
复杂的是,此步骤是自动化过程的一部分。该命令由 Perl 生成。 Perl 是通过 ssh 在单独的 Docker 容器上执行的。除了 Perl 所需的转义之外,这增加了需要解决的另一层转义。
基本上, docker 正在做类似的事情

perl myprogram.perl
它生成必要的 SSH 命令,将它们发送到试图运行它们的大型机。当我运行 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'"
并且大型机返回错误:
cp: target "//'WOODSMN.SSC.D051721.T200335.S90.CP037'" is not a directory

sshpass 是必需的,因为我的系统管理员拒绝打开授权用户,所以我唯一的选择是运行 sshpass 并输入密码。密码暴露被包含在内,我们不担心这一点。
第一条命令
export _UNIX03=NO
告诉 z/OS 将 -P 选项视为 MVS 数据集控制块的指示符。也就是说,这是我们告诉系统的地方,嘿,这是 287 个字符的固定长度,在轨道中分配等。数据集将被假定为新的。
对于复制命令,我希望 z/OS 复制 HFS 文件(基本上是一个普通的 UNIX 文件)
/u/woodsmn/SSC.D051721.T200335.S90.CP037
进入完全合格的 MVS 数据集
WOODSMN.SSC.D051721.T200335.S90.CP037
有时 MVS 命令假定一个高级限定符基本上是用户 userid,并允许用户忽略它。在这种情况下,我已经明确指定了这一点。
要让 z/OS 将目标视为数据集,需要在它前面加上两个斜杠 (/),所以//
要使用完全限定的数据集,名称需要用撇号 (') 括起来
但是,为了避免在 Korn shell 中混淆,目标需要用双引号 (") 括起来。
因此,不知何故,在 Perl、Docker 容器内运行我的 SSH 命令的 shell(可能是 bash)和 z/OS 上的接收 Korn shell 之间,它没有被正确解释。
我缩小的 Perl 看起来像:

use strict;
use warnings;
sub putMvsFileByHfs;

use IO::Socket::IP;
use IO::Socket::SSL;
use IPC::Run3;
use Net::SCP;

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 $SCJCL_USER_ID="woodsmn";
my $SSCJCL_HOST_NAME="bldbmsb.boulder.mycompany.com";


my $MVS_FORMAT_OPTIONS="-P ".qq(")."RECFM=FB,LRECL=287,BLKSIZE=6027,SPACE=\\("
.${SSCJCL_STORAGE_UNIT}
.",\\("
.${SSCJCL_PRIMARY_EXTENTS}
.","
.${SSCJCL_SECONDARY_EXTENTS}
."\\)\\)".qq(");
putMvsFileByHfs(${MVS_FORMAT_OPTIONS}." ",
$SSCJCL_SOURCE_DIRECTORY.'/'.$COST_FILE,
${SSCJCL_HFS_LOCATION}.'/'.$COST_FILE,
${SSCJCL_STAGING_HLQ}.'.'.$COST_FILE);


# This function copys the file first from my local volume mounted to the Docker container
# to my mainframe ZFS volume. Then it attempts to copy it from ZFS to a traditional MVS
# dataset. This second part is the failinmg part.
sub putMvsFileByHfs
{
#
# First copy the file from the local file system to my the mainframe in HFS form (copy to USS)
# This part works.
#
my $OPTIONS = shift;
my $FULLY_QUALIFIED_LOCAL_FILE = shift;
my $FULLY_QUALIFIED_HFS_FILE = shift;
my $FULLY_QUALIFIED_MVS_FILE = shift;
RunScpCommand($FULLY_QUALIFIED_LOCAL_FILE,$FULLY_QUALIFIED_HFS_FILE);

#
# I am doing something wrong here
# Attempt to build the target dataset name.
#
my $dsnPrefix = qq(\"//');
my $dsnSuffix = qq('\");
my $FULLY_QUALIFIED_MVS_ARGUMENT = ${dsnPrefix}.${FULLY_QUALIFIED_MVS_FILE}.${dsnSuffix};
RunSshCommand("export _UNIX03=NO;cp ${OPTIONS}".${FULLY_QUALIFIED_HFS_FILE}." ".${FULLY_QUALIFIED_MVS_ARGUMENT});
}

# This function marshals whatever command I want to run and mostly does it. I'm not having
# any connectivity issues. My command at least reaches the server and SSH will try to run it.
sub RunScpCommand()
{
my $ssh_source= $_[0];
my $ssh_target= $_[1];
my ($out,$err);
my $in = "${SSCJCL_USER_PW}\n";
my $full_command = "sshpass -p ".${SSCJCL_USER_PW}." scp ".${ssh_source}." ".${SSCJCL_USER_ID}."@".${SSCJCL_HOST_NAME}.":".${ssh_target};
print ($full_command."\n");
run3 $full_command,\$in,\$out,\$err;
print ($out."\n");
print ($err."\n");
return ($out,$err);
}

# This function marshals whatever command I want to run and mostly does it. I'm not having
# any connectivity issues. My command at least reaches the server and SSH will try to run it.
sub RunSshCommand
{
my $ssh_command = $_[0];
my $in = "${SSCJCL_USER_PW}\n";
my ($out,$err);
my $full_command = "sshpass -p ".${SSCJCL_USER_PW}." ssh ".${SSCJCL_USER_ID}."@".${SSCJCL_HOST_NAME}." ".${ssh_command};
print ($full_command."\n");
run3 $full_command,\$in,\$out,\$err;
print ($out."\n");
print ($err."\n");
return ($out,$err);
}
请原谅上面的任何 Perl 不当行为,因为我是 Perl 的新手,尽管感谢善意的 build 性指针。

最佳答案

首先,让我们构建我们想要传递给程序的值。我们稍后会担心构建 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",
-p => $SSCJCL_USER_PW,
"ssh", $remote_cmd,
);

run3 $ssh_cmd, ...;

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

my @scp_cmd = (
"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 = (
"sshpass",
-p => $SSCJCL_USER_PW,
"ssh", $remote_cmd,
);

run3 \@ssh_cmd, ...;

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

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

关于perl - 引号和斜线在多层中幸存下来,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68124153/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com