仅当模式存在时,才在文件中搜索和替换

时间:2016-03-28 09:35:58

标签: bash sed

我想将文件中的一个字符串替换为另一个字符串,更具体地说,在dovecot passwd文件(/etc/dovecot/passwd)中替换username:password条目,但仅限于记录存在。

所以,我需要的是:

  • sed命令
  • 如果记录不存在则不执行任何操作(请参阅dovecot passwd示例)
  • 替换文件(如果没有很好的理由输出到新文件并将新文件移动到原始文件)

以下是/etc/dovecot/passd示例:

johndoe@example.com:{CRAM-MD5}479375185777b7ba573747c111483bdd628332a70268ce283dc80bedd0f65988
janedoe@example.com:{CRAM-MD5}10b7176649d8bb3eb38f6b68a0c6c826ffab5656181821e598296ebd31e8f64f

到目前为止我已尝试过(创建新文件,输出到新文件,mv到原始,即使记录不存在

#!/usr/bin/env php
<?php

$passwd_file = '/etc/dovecot/passwd';
$time = time();
$newtempfile = '/tmp/' . $time . 'dvctpwd';

/**
 * $search generates replace target johndoe@example.com:{CRAM-MD5}479375185777b7ba573747c111483bdd628332a70268ce283dc80bedd0f65988
 * if johndoe@example.com gives correct password to dovecot's doveadm pw -u johndoe@example.com -poldpassword
**/
$search  = $argv[1] .':' . exec("doveadm pw -u " . $argv[1] . " -p" .  $argv[2]);

/**
 * $replace generates replace value johndoe@example.com:{CRAM-MD5}...
 * i.e. scheme with new password
**/
$replace = $argv[1] .':' . exec("doveadm pw -u " . $argv[1] . " -p" .  $argv[3]);

$command = <<<DD
sed -e "s|$search|$replace|" $passwd_file > $newtempfile && mv $newtempfile $passwd_file && echo "Password changed!\n";
DD;

$output = passthru($command);
echo $output . PHP_EOL;

2 个答案:

答案 0 :(得分:1)

虽然您可以使用 sed 使用 -i 标记进行替换,但文件的上次修改时间将始终更新:

sed -i "s|$search|$replace|" "$passwd_file"

确实,请注意文件出生的时间,上次访问的时间以及上次修改的时间将使用上面的命令更新。这可以通过在上面的sed命令之前和之后执行以下命令来显示:

stat -c "%W %X %Y %Z" "$passwd_file"

此外,无论是否发生替换,上述sed命令都将返回0。

所以,如果你绝对不能更新文件,包括时间戳,你可以这样做:

grep -q "$search" "$passwd_file" &&
sed -i "s|$search|$replace|" "$passwd_file"

如果您需要显示该文件已更新的消息,您可以使用list

grep -q "$search" "$passwd_file" &&
{
   sed -i "s|$search|$replace|" "$passwd_file"
   echo -e "Password changed!\n"
}

最后,如果您希望在密码未更改时显示消息,则可以执行以下操作:

grep -q "$search" "$passwd_file" &&
{
   sed -i "s|$search|$replace|" "$passwd_file"
   printf "Password changed!\n\n"
} || printf "Password not changed!\n\n"

要将上述内容作为一行执行,请务必在列表中添加分号,包括最后一个元素:

grep -q "$search" "$passwd_file" && { sed -i "s|$search|$replace|" "$passwd_file"; printf "Password changed!\n\n"; } || printf "Password not changed!\n\n"

一些注意事项:

  • 只需一次替换,sed的 -e 标志是可选的
  • 我已在&#34; $ passwd_file&#34; 周围添加了引号,以防该文件中包含空格。即使它没有,但这是一个很好的做法。
  • 我已将 -e 标记添加到 echo 。虽然这适用于bash,但如果你的shell不是bash,那么可能不支持。您上面的 echo 语句旨在推出2个换行符。 echo -n 标志会抑制终端换行符。如果您希望对上面的最后一个示例中显示的格式有更多控制,则可以考虑 printf 命令,其行为比 echo 更一致。

答案 1 :(得分:1)

使用GNU sed编辑文件&#34;就地&#34;:

a="johndoe@example.com"
md5="abc123"
sed -i 's/^'"$a"'.*/'"$a"':{CRAM-MD5}'"$md5"'/' /etc/dovecot/passd

文件/ etc / dovecot / passd中的输出:

johndoe@example.com:{CRAM-MD5}abc123
janedoe@example.com:{CRAM-MD5}10b7176649d8bb3eb38f6b68a0c6c826ffab5656181821e598296ebd31e8f64f