Your first alternative would make the password string available to all logged-in users via the "ps" command.
Yeah, that's why I put the second alternative. I should have mentioned it.
Your second alternative will still be stuck with line buffering
Oh! right! Here's a version that doesn't wait for a whole line to be read before
#!/usr/bin/perl
# censor_passwd
use warnings;
use strict;
my $passwd = 'hide_me';
my $replacement = 'removed';
# Used to check if the start of the password
# is at the end of the read data. The returned
# regexp will match 0 characters if not.
# For efficiency, it expects the data to be
# "reverse"d.
my $partial_re = do {
my $r = reduce { "(?:$a$b)?" } '',
reverse
map quotemeta,
map /(.)/g,
$passwd;
qr/$r/
};
binmode(STDIN);
binmode(STDOUT);
$| = 1;
my $buf = '';
for (;;) {
my $rv = sysread(STDIN, $buf, 4096, length($buf));
defined $rv
or die("Unable to read from STDIN: $!\n");
$rv
or last;
for (;;) {
my $pos = index($buf, $passwd);
last if $pos < 0;
substr($buf, $pos, length($passwd), $replacement);
print(substr($buf, 0, $pos+length($replacement), ''));
}
reverse($buf) =~ /^$partial_re/;
print(substr($buf, 0, length($buf)-$+[0], ''));
}
print($buf);
Points of interest:
-
It performs minimal input buffering. It outputs everything as soon as it receives it, without waiting for lines or words to be completed. It only buffers when the data that was read ends with the start of the password.
-
It is very efficient. It reads a block of data at a time, if more than a byte of data is available. It avoids using /...\z/ and even slower /^.*?.../ by using reverse. It avoids using captures. A major loop was replaced with a precompiled regexp.
( Caveat: What if "hide" and "_me" are received seperatly? Code updated to address this issue. )
|