Как заменить строки в файле и сохранить его как другой файл

Я пытаюсь заменить строку в куче файлов *.txt. Я знаком с этой командой:

find "$folder" -type f -name '*.txt' -exec sed -i 's/REPLACEFROM/REPLACETO/g' {} \;

Однако это (согласно флагу -i) заменяет строки на месте.

Я хотел бы сохранить все исходные файлы без изменений и «сохранить» измененные файлы как «file1-renamed.txt», «file2-renamed.txt», …

Какой самый быстрый и элегантный способ сделать это? Может перл?

Я уверен, что это довольно просто, но мне кажется, что это сложно. :)

Заранее спасибо за помощь.


person zigamilek    schedule 10.02.2012    source источник


Ответы (5)


С sed вы можете использовать

 sed -i.back

таким образом, исходный файл будет перемещен в *.back

Или используйте этот фрагмент, чтобы быть ближе всего к вашему вопросу:

find "$folder" -type f -name '*.txt' -exec bash -c '
    cp "$1" "${1%.*}-renamed.txt"
    sed -i 's/REPLACEFROM/REPLACETO/g' "${1%.*}-renamed.txt"
' -- {} \;
person Gilles Quenot    schedule 10.02.2012
comment
Perl также имеет переключатель -i, который работает так же: -i.bak заставит исходный файл быть сохраненным с добавлением .bak к имени файла. - person Ilmari Karonen; 11.02.2012

С perl вы можете попробовать что-то вроде этого:

find "$folder" -name '*.txt' |
  xargs perl -ne 'open NEW, ">>$ARGV.new"; s/from/to/g; print NEW'

Это позволит сохранить новые файлы как old-name.txt.new. Возможно, вам придется изменить его, если вам действительно нужен формат «старое имя-переименование.txt». Также обратите внимание, что он будет добавляться к новым файлам после каждого выполнения oneliner.

Конечно, было бы проще использовать опцию -i (... perl -i.old -pe 's/from/to/g'), а затем массово переименовывать файлы в желаемый формат имени, поэтому это решение, вероятно, имеет смысл только в том случае, если исходные файлы недоступны для записи.

person Alexis    schedule 10.02.2012

Это может сработать для вас:

find "$folder" -type f -name '*.txt' |
parallel sed 's/REPLACEFROM/REPLACETO/g' ">" {.}-renamed.txt
person potong    schedule 11.02.2012

Это не совсем однострочный, но он должен быть хитрым (и ИМО его легче читать):

use strict;
use warnings;

while(@ARGV)
{
  my $file=shift;
  open(my $read,"<",$file) or die $!;
  my $output_file=$file . "_renamed";
  if($file=~/^(.+)\.txt$/)
  {
    $output_file=$1 . "-renamed.txt";
  }

  open(my $write,">",$output_file) or die $!;

  while(<$read>)
  {
    chomp;
    s/REPLACEFROM/REPLACETO/g;
    print $write "$_\n";
  }
  close($read);
  close($write);
}
person Community    schedule 10.02.2012

Я уже разместил здесь ответ, но сегодня мне пришло в голову другое решение:

for f in (find "$folder" -name '*.txt'); do perl -ne 's/from/to/g' $f > "$f.new"; done

Опять же, он сохранит измененный контент в «old-name.txt.new» (вы можете изменить его на что-то вроде ... > "$(basename $f)-renamed.txt" ..., если хотите).

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

person Alexis    schedule 11.02.2012