2016-03-28 5 views
0

Я хотел бы заменить одну строку в файле на другую, чтобы быть более конкретным, заменить имя пользователя: пароль в файле dovecot passwd (/etc/dovecot/passwd), но только если запись существует.Поиск и замена Sed в файле, только если существует шаблон

Итак, что мне нужно:

  • sed команда
  • что не делает ничего, если запись не существует (см голубятни 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

Одна из причин избежать поддержки «in-file» sed заключается в том, что он не является in-file; sed имитирует поведение в файле с использованием временного файла, поэтому единственным преимуществом является упрощенный код, в то время как вы увеличиваете риск проблем, если файловая система заполняется или, возможно, с другими условиями. Мне нравится писать скрипты с обработкой ошибок почти по каждой команде, так что если что-то не удается, я могу предпринять соответствующие действия в скрипте. – ghoti

+0

@ghoti хорошо пункт. Я подозревал, что фактическая замена не выполняется в самом файле, но в любом случае я получаю решение так просто, как может быть, так как я не владею языком BASH. –

ответ

1

С GNU СЭД для редактирования файла "на месте":

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

Вывод в файл/и т.д./голубятня/passd:

 
[email protected]:{CRAM-MD5}abc123 
[email protected]:{CRAM-MD5}10b7176649d8bb3eb38f6b68a0c6c826ffab5656181821e598296ebd31e8f64f 
1

В то время как вы можете использовать СЭД сделать замены в месте с -i флага, последнее изменением времени файла всегда будет обновляться:

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

Действительно, обратите внимание, что время рождения файла, время последнего доступа, а также время последней модификации будут обновлены командой выше. Это можно показать, выполнив следующую команду как до, так и после указанной SED команды:

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

Кроме того, выше СЕПГ команда возвращает 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 флаг для sed необязателен
  • Я добавил цитаты из «$ passwd_file», если в этом файле есть пробелы. Даже если это не так, это хорошая практика.
  • Я добавил -e flag to echo. Хотя это действительно для использования с bash, если ваша оболочка не bash, то это может не поддерживаться. Ваше заявление echo предназначено для выпуска 2 новых строк. -n flag to echo подавляет вывод новой строки. Вы можете рассмотреть команду printf, которая имеет более последовательное поведение, чем echo, если вы хотите иметь больше контроля над форматированием, как показано в последнем примере выше.
+0

upvote для решения alt. –

+0

Для справки, ваши командные строки 'sed' и' stat' работают в Linux (с версиями GNU), но не во FreeBSD или OSX. Это хороший ответ, но отдельные части ответа на конкретные вопросы должны всегда выделяться как таковые, если вопрос не ограничивается конкретной ОС. – ghoti

Смежные вопросы