Как заменить строку в том же файле SED в сценариях Unix Shell? - PullRequest
2 голосов
/ 09 октября 2009

В отношении этот вопрос После получения совпадения идентификатора строки в первом и втором файле мне нужно заменить строку в первом файле строкой второго файла. Для этого я использую SED, как показано ниже. Но Sed заменяет только эту строку в новом файле. Как я могу добиться обновления в том же файле без временного файла (потому что это очень большие файлы).

#!/bin/ksh
while read line
do
var=`echo $line|cut -c 3-25`
while read i
do
var1=`echo $i|cut -c 3-25`
if [ $var == $var1 ];
then
sed -i s/$line/$i/ t1
else

echo "not matched"
fi
done < t2
done < t1

Даже без опции -i также я не получаю результат. Пожалуйста, помогите мне.

Редактировать : Или еще предложить мне другой оптимальный способ обхода без временных файлов или с любыми языками сценариев (менее предпочтительным).

Ответы [ 4 ]

4 голосов
/ 09 октября 2009

Посмотрите на губку, которая является частью moreutils .

, например

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd
3 голосов
/ 10 октября 2009

Вы не должны использовать sed таким образом. Теперь, когда ваш сценарий стоит, он говорит следующее:

  1. Для каждой строки в t1
  2. Шаг через все строк в t2
  3. Если одна из строк в t2 совпадает с текущей строкой в ​​t1, то выполните все строки в t1 и замените совпадения
  4. Перейти к следующей строке в t1 и повторить

Это означает, что весь файл t2 читается каждый раз, когда из t1 читается одна строка. Это невероятно неэффективно.

Нет необходимости использовать echo и cut для подстрок. В Bash и Ksh вы можете сделать:

var=${line:3:23}

Примечание: cut использует позиции символов для начала и конца диапазона, в то время как эта конструкция оболочки использует начальную позицию и количество символов, поэтому вам необходимо соответствующим образом скорректировать числа.

Если t2 - это список замен, которые нужно сделать в t1, то есть t2 - это своего рода «скрипт», тогда это может сделать то, что вам нужно:

keystart=3
keylen=23
while read line
do
    var="${line:$keystart:$keylen}"
    if (( ${#var} == keylen ))    # need "$" then don't need "$"
    then
        sed -in "/^.\{$keystart\}$var/ c$line" t1    # use double-quote so vars get expanded
    fi
done < t2

Это найдет все строк в t1, которые соответствуют каждой строке в t2, и выполнит замену.

Если, однако, t1 и t2 имеют построчное соответствие, и вы хотите выполнить подстановку только там, где соответствующие строки совпадают, то это, используя временный файл, будет наиболее эффективным:

tempfile=$(mktemp)
keystart=3
keylen=23
while read line1
do
    var1="${line1:$keystart:$keylen}"
    read line2 <&3    # use file descriptor 3 for input

    var2="${line2:$keystart:$keylen}"
    if [[ $var1 == $var2 && ${#var2} == $keylen ]]
    then
        echo "${line2}" >> $tempfile    # substitute when matched
    else
        echo "${line1}" >> $tempfile    # keep when not matched
    fi
done < t1 3< t2    # t1 is input on stdin, t2 is input on fd 3
mv t1 t1.bak && mv $tempfile t1
2 голосов
/ 09 октября 2009

Нельзя заменить одну строку другой на месте. Потому что линии обычно имеют разную длину и будут перекрывать друг друга. Если все строки в вашем файле имеют одинаковую длину, это может работать. Я также предлагаю вам использовать более подходящий язык для этой задачи (например, Perl), потому что это будет действительно сложный код в Shell. Я думаю, что вы должны искать решение с временными файлами, потому что его будет проще реализовать и его можно будет легко отладить. Только представьте, что вы будете делать, если ваш огромный файл будет обрезан из-за ошибки в скрипте.

1 голос
/ 10 октября 2009

Sed предназначен для работы в конвейере - отсюда и название «Stream EDitor». Вместо этого вы можете использовать ex-скрипт для редактирования файла на месте. Ex - это линейный текстовый редактор, на котором изначально был основан vi (не такой старый, как ed, текстовый редактор старых скинов медведя и каменных ножей, но почти). Простой пример, который вы можете изменить для своих целей, может быть следующим:

ex t1 << EOF
$lineNum
s/^.*$/$newline/
w
q
EOF

Этот скрипт сначала переходит к строке, обозначенной $ lineNum, заменяет всю строку, начиная с (^) до конца ($), содержимым $ newline, затем записывает и завершает работу. Эти команды окружены "<<EOF" и "EOF", которые составляют документ "здесь", по существу устанавливая скриптовые команды как stdin.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...