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.
echo
和 printf
将通过管道传递给命令。但是,我会推荐更多的方法来做到这一点。
正如其他人所提到的,最干净的方法可能是使用 IPC::Run3
。一站式购物。
但是,您的系统[或其他 OSX 系统] 可能不会默认安装它。不知道——YMMV。您可能需要对此进行调查。所以,你可能必须自己安装它 [我想是从 CPAN (?)]
如果您的脚本仅供您自己使用,以上可能没问题。但是,如果您正在创建供其他人使用的脚本,它可能最终会出现在没有 Run3 的系统上,并且 sysadm 不会安装它。
因此,您必须在易用性和普遍性之间取得平衡。那将是你的选择。
run3在hdiutil
命令上设置了bothstdin
和stdout
,这就是你需要的,因为你想给它密码 和 捕获其输出。
如果你只需要一个,你可以在 "pipe" 模式下使用 perl 的 open
函数。有关详细信息,请参阅 man perlipc
。使用起来非常简单。我有一个例子——见下文。
或者,您可以 "roll your own" 通过使用 perl 的固有 pipe
调用等价于 run3
所做的事情。您将需要 两个 管道。这是一些工作,所以也许是未来的事情。
echo
、printf
、[和passme
]方法的最大问题是[正如其他人指出的那样],因为他们使用 argv 来获取密码,他们"bleed"它[简要地]到其他进程。
所以,我们想要一种安全的方法[例如Run3 会]。这就是为什么 hdiutil
在 stdin
而不是命令行参数上使用密码。
(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
变量,大概是 0
或 1
。这引起了我的注意。
chomp
returns 从其所有参数中删除的字符总数。
我正在使用 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.
echo
和 printf
将通过管道传递给命令。但是,我会推荐更多的方法来做到这一点。
正如其他人所提到的,最干净的方法可能是使用 IPC::Run3
。一站式购物。
但是,您的系统[或其他 OSX 系统] 可能不会默认安装它。不知道——YMMV。您可能需要对此进行调查。所以,你可能必须自己安装它 [我想是从 CPAN (?)]
如果您的脚本仅供您自己使用,以上可能没问题。但是,如果您正在创建供其他人使用的脚本,它可能最终会出现在没有 Run3 的系统上,并且 sysadm 不会安装它。
因此,您必须在易用性和普遍性之间取得平衡。那将是你的选择。
run3在hdiutil
命令上设置了bothstdin
和stdout
,这就是你需要的,因为你想给它密码 和 捕获其输出。
如果你只需要一个,你可以在 "pipe" 模式下使用 perl 的 open
函数。有关详细信息,请参阅 man perlipc
。使用起来非常简单。我有一个例子——见下文。
或者,您可以 "roll your own" 通过使用 perl 的固有 pipe
调用等价于 run3
所做的事情。您将需要 两个 管道。这是一些工作,所以也许是未来的事情。
echo
、printf
、[和passme
]方法的最大问题是[正如其他人指出的那样],因为他们使用 argv 来获取密码,他们"bleed"它[简要地]到其他进程。
所以,我们想要一种安全的方法[例如Run3 会]。这就是为什么 hdiutil
在 stdin
而不是命令行参数上使用密码。
(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
变量,大概是 0
或 1
。这引起了我的注意。
chomp
returns 从其所有参数中删除的字符总数。