Perl 系统调用不能正确解释变量

Perl System Call not Interpretting Variable Correctly

我正在使用 Perl 将字符串 ($password) 传递到要解释的系统行。虽然它似乎没有被正确解释,因为当我去附加稀疏包时它说身份验证失败。请注意,管道后的一切都正常。

$password = chomp($password);

### Create the bash system call to create the sparse bundle with the password
my $cmd = `echo $password | hdiutil create -size 200g -type SPARSEBUNDLE -encryption -stdinpass -volname \"Encrypted Storage for Matt\" -fs \"Case-sensitive Journaled HFS+\" -verbose ~/Desktop/SparseBundle`;

一些示例输出:

SLC1087-Matt:backups matt$ ./create_sparsebundle.pl 
DIDiskImageCreatorProbe: interface  1, score     1000, CSparseBundleDiskImage
DIDiskImageCreatorProbe: interface  2, score    -1000, CSparseDiskImage
DIDiskImageCreatorProbe: interface  3, score    -1000, CRawDiskImage
DIDiskImageCreatorProbe: interface  7, score    -1000, CWOUDIFDiskImage
DIDiskImageCreatorProbe: interface  9, score    -1000, CCFPlugInDiskImage
DIDiskImageCreateWithCFURL: CSparseBundleDiskImage
CBSDBackingStore::createProbe directory, not a valid image file.
DIBackingStoreCreatorProbe: interface  0, score    -1000, CBSDBackingStore
DIBackingStoreCreatorProbe: interface  1, score     1000, CBundleBackingStore
DIBackingStoreCreatorProbe: interface  2, score        0, CRAMBackingStore
DIBackingStoreCreatorProbe: interface  3, score      100, CCarbonBackingStore
DIBackingStoreCreatorProbe: interface  5, score     -100, CCURLBackingStore
DIBackingStoreCreateWithCFURL: CBundleBackingStore
DIFileEncodingCreatorProbe: interface  2, score     1000, CEncryptedEncoding
DIFileEncodingCreateWithCFURL: CEncryptedEncoding
DIFileEncodingCreatorProbe: interface  2, score    -1000, CEncryptedEncoding
DIBackingStoreCreatorProbe: interface  0, score      100, CBSDBackingStore
DIBackingStoreCreatorProbe: interface  1, score    -1000, CBundleBackingStore
DIBackingStoreCreatorProbe: interface  2, score        0, CRAMBackingStore
DIBackingStoreCreatorProbe: interface  3, score      100, CCarbonBackingStore
DIBackingStoreCreatorProbe: interface  5, score     -100, CCURLBackingStore
DIBackingStoreCreateWithCFURL: CBSDBackingStore
DIBackingStoreCreateWithCFURL: creator returned 0
DIFileEncodingCreateWithCFURL: creator returned 0
DIBackingStoreCreateWithCFURL: creator returned 0
DIDiskImageCreateWithCFURL: creator returned 0
DI_kextWaitQuiet: about to call IOServiceWaitQuiet...
DI_kextWaitQuiet: IOServiceWaitQuiet took 0.000003 seconds
2016-05-18 20:59:09.627 diskimages-helper[68122:1796245] *** -[NSMachPort handlePortMessage:]: dropping incoming DO message because the connection is invalid
hdiutil: create: returning 0
SLC1087-Matt:backups matt$ hdiutil attach ~/Desktop/SparseBundle.sparsebundle
Enter password to access "SparseBundle.sparsebundle": 
hdiutil: attach failed - Authentication error

似乎一切正常,但密码显然是错误的。

hdiutil 实用程序需要一个以 null 结尾的密码。

也就是说,"mypass" 还不够好。它需要查看 "mypass[=20=]" 因为密码允许包含有趣的字符,例如嵌入的换行符。

所以,它看到的是 "mypass\n" 而不是 "mypass[=20=]"

可以试试这个,因为它会在 linux:

下工作
echo -n -e "mypass\x00" | hdiutil ...

但是...AFAICT,OSX 版本的 echo 没有 -e,所以您可能需要试验一下。

如果一切都失败了,将 echo 替换为:

echo "mypass" | perl -e '$_ = <STDIN>; chomp ; print $_,"\x00"'

可能有更简单的方法do/express。您可以创建第二个 perl 脚本(称之为 passme):

#!/usr/bin/perl
print $ARGV[0],"\x00";

然后将 echo 替换为 passme。我在参数周围加上双引号。


更新:

I'm pretty sure because of the stdinpass that the printf has to be piped to the command. I don't know if I'm exactly right on that.

echoprintf 将通过管道传递给命令。但是,我会推荐更多的方法来做到这一点。

正如其他人所提到的,最干净的方法可能是使用 IPC::Run3。一站式购物。

但是,您的系统[或其他 OSX 系统] 可能不会默认安装它。不知道——YMMV。您可能需要对此进行调查。所以,你可能必须自己安装它 [我想是从 CPAN (?)]

如果您的脚本供您自己使用,以上可能没问题。但是,如果您正在创建供其他人使用的脚本,它可能最终会出现在没有 Run3 的系统上,并且 sysadm 不会安装它。

因此,您必须在易用性和普遍性之间取得平衡。那将是的选择。

run3在hdiutil命令上设置了bothstdinstdout,这就是你需要的,因为你想给它密码 捕获其输出。

如果你只需要一个,你可以在 "pipe" 模式下使用 perl 的 open 函数。有关详细信息,请参阅 man perlipc。使用起来非常简单。我有一个例子——见下文。

或者,您可以 "roll your own" 通过使用 perl 的固有 pipe 调用等价于 run3 所做的事情。您将需要 两个 管道。这是一些工作,所以也许是未来的事情。

echoprintf、[和passme]方法的最大问题是[正如其他人指出的那样],因为他们使用 argv 来获取密码,他们"bleed"它[简要地]到其他进程。

所以,我们想要一种安全的方法[例如Run3 会]。这就是为什么 hdiutilstdin 而不是命令行参数上使用密码。


(1) 实际上,最简单的方法可能是将密码转储到临时文件中:

use Fcntl;

my $fd;
my $tmp = "/tmp/pwfile";

my $password = "whatever";

# NOTE: by using sysopen and setting the 600 permission atomically, we avoid a
# race condition that may allow another user to open this file
unlink($tmp);
sysopen($fd,$tmp,O_WRONLY | O_CREAT | O_EXCL,0600) or
    die("unable to open '$tmp' -- $!\n");
syswrite($fd,$password . "\x00");
close($fd)

my $cmd = `hdiutil ... < $tmp`;
unlink($tmp);

(2) 如果您不想将密码写入临时文件,无论它可能会持续多久,还有另一种方法。它类似于上面的 passme 脚本方法, 除了 它将密码作为环境变量传递下去。这避免了通过命令行参数暴露密码。

这是 passme 变体:

#!/usr/bin/perl
print($ENV{"PASSME"},"\x00");

这也可以通过内联来完成:

perl -e 'print($ENV{"PASSME"},"\x00")'

然后,您的脚本变为:

my $password = "whatever";

$ENV{"PASSME"} = $password;
my $cmd = `passme | hdiutil ...`;
delete($ENV{"PASSME"});

(3) 另一种方法是在管道模式下使用 [前面提到的] perl 的 open。将其设置为 hdiutil 的输入管道,并将输出转移到一个临时文件,您在命令执行后读回该文件。

my $fdout;
my $fdin;
my $tmp = "/tmp/results";
my $buf;

my $password = "whatever";

open($fdout,"| hdiutil ... > $tmp") ||
    die("unable to open hdiutil pope -- $!\n");
print($fdout $password,"\x00");
close($fdout);

open($fdin,"<$tmp") ||
    die("unable to open '$tmp' -- $!\n");
while ($buf = <$fdin>) {
    chomp($buf);
    # do whatever ...
}
close($fdin);

阅读有关 chomp returns 的内容。您的代码至少用整数重写了 $password 变量,大概是 01。这引起了我的注意。


chomp returns 从其所有参数中删除的字符总数。