2016-03-28 76 views
0

我想用另一個替換文件中的一個字符串,更具體地說,替換dovecot passwd文件(/etc/dovecot/passwd)中的用戶名:密碼條目,但只有當記錄存在時。sed搜索和替換的文件,只存在模式

所以,我需要什麼:

  • sed命令
  • ,做什麼,如果記錄不存在(見dovecot的passwd文件的例子)
  • ,在文件替換(如果沒有很好的原因outputing到新的文件和移動新文件原件)

這裏是/etc/dovecot/passd例如:

[email protected]:{CRAM-MD5}479375185777b7ba573747c111483bdd628332a70268ce283dc80bedd0f65988 
[email protected]:{CRAM-MD5}10b7176649d8bb3eb38f6b68a0c6c826ffab5656181821e598296ebd31e8f64f 

和我到目前爲止已經試過(創建新的文件,輸出到新mv原始即使記錄不存在

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

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

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

/** 
* $replace generates replace value [email protected]:{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; 
+0

參見:如何更換一個特定的行在一個文本文件中使用php?](http://stackoverflow.com/q/3004041/3776858) – Cyrus

+0

避免sed的「in-file」支持的一個原因是它不是真正的文件中; sed模仿使用臨時文件的文件內行爲,所以你唯一的好處是簡化的代碼,而如果文件系統填滿了,或者也可能與其他情況一起增加了問題的風險。我喜歡在幾乎所有命令上編寫包含錯誤處理的腳本,這樣如果出現問題,我可以在腳本中採取適當的操作。 – ghoti

+0

@ghoti好點。我懷疑實際的替換不是在文件本身完成的,但無論如何,我的解決方案儘可能簡單,因爲我不是BASH語言的掌握者。 –

回答

1

隨着GNU sed的編輯您的文件 「到位」:

a="[email protected]" 
md5="abc123" 
sed -i 's/^'"$a"'.*/'"$a"':{CRAM-MD5}'"$md5"'/' /etc/dovecot/passd 

輸出在文件/ etc /達夫科特/ passd:

 
[email protected]:{CRAM-MD5}abc123 
[email protected]:{CRAM-MD5}10b7176649d8bb3eb38f6b68a0c6c826ffab5656181821e598296ebd31e8f64f 
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" 

一些注意事項:

  • 只需要一個替代的-e flag to sed是可選的
  • 我已經在「$ passwd_file」附近引用了引號,以防文件中有空格。即使不這樣做,這是一個很好的做法。
  • 我已將-e標記添加到echo。雖然這對於使用bash是有效的,但如果你的shell不是bash,那麼這可能不被支持。您的echo以上聲明旨在推出2個換行符。 -n標記爲echo取消終端換行符。如果您希望更好地控制格式,您可以考慮printf命令的行爲比更具一致性。echo如上面最後一個示例中所示。
+0

upvote for alt solution。 –

+0

僅供參考,您的'sed'和'stat'命令行適用於Linux(包含GNU版本),但不適用於FreeBSD或OSX。這是一個很好的答案,但是如果問題不限於特定的操作系統,應該總是突出顯示操作系統特定的答案部分。 – ghoti