Как соединить две строки файла по шаблону в Ruby или Bash?

Я использую сценарий Ruby, чтобы выполнить множество манипуляций и очистки, чтобы получить этот и кучу других файлов, готовых к импорту.

У меня есть действительно большой файл с некоторыми данными, которые я пытаюсь импортировать в базу данных. Есть некоторые проблемы с данными, когда символы новой строки находятся в данных там, где их быть не должно, что мешает импорту.

Я смог решить эту проблему с помощью sed, используя это:

sed -i '.original' -e ':a' -e 'N' -e '$!ba' -e 's/Oversight Bd\n/Oversight Bd/g' -e 's/Sciences\n/Sciences/g' combined_old_individual.txt"

Однако я не могу вызвать эту команду из скрипта Ruby, потому что Ruby искажает интерпретацию символов новой строки и не запускает эту команду. sed нуждается в неэкранированном символе новой строки, но при вызове системной команды из Ruby ему нужна строка, в которой необходимо экранировать символ новой строки.

Я также пытался сделать это с помощью метода файла Ruby, но он тоже не работает:

File.open("combined_old_individual.txt", "r") do |f|
  File.open("combined_old_individual_new.txt","w") do |new_file|
    to_combine = nil
    f.each_line do |line|
      if(/Oversight Bd$/ =~ line || /Sciences$/ =~ line)
        to_combine = line
      else
        if to_combine.nil?
          new_file.puts line
        else
          combined_line = to_combine + line
          new_file.puts combined_line
          to_combine = nil
        end
      end
    end
  end
end

Любые идеи, как я могу соединить строки, где первая строка заканчивается на «Bd» или «Sciences», из скрипта Ruby, были бы очень полезны.

Вот пример того, что может быть в testfile.txt:

random line
Oversight Bd
should be on the same line as the above, but isn't
last line

и результат должен быть

random line
Oversight Bdshould be on the same line as the above, but isn't
last line

person Solomon    schedule 20.01.2014    source источник
comment
Вы не даете нам образцы входных данных или желаемого результата? Не просите нас собрать наши собственные образцы, иначе результат, вероятно, не будет соответствовать тому, что вы хотите. Этот вопрос кажется не по теме, поскольку в нем недостаточно информации для диагностики проблемы. Опишите свою проблему более подробно или включите минимальный пример в сам вопрос.   -  person the Tin Man    schedule 20.01.2014
comment
Жестянщик прав: пример ввода/вывода был бы отличным.   -  person agarie    schedule 20.01.2014
comment
Эй, Жестянщик и агари, я добавил пример с вводом и выводом.   -  person Solomon    schedule 20.01.2014
comment
Я не знаю ruby, но похоже, что вы можете напечатать строку без новой строки, используя метод print вместо puts: stackoverflow.com/questions/8723120/, stackoverflow.com/questions/5080644/   -  person Digital Trauma    schedule 20.01.2014
comment
sed не нуждается в неэкранированном символе новой строки, вы делаете указываете экранированный символ новой строки в sed. Проблема в том, что sed работает построчно, и вы не можете сопоставить some_pattern\n напрямую, но должны использовать команду N после сопоставления some_pattern, чтобы получить новую строку и следующую строку в буфере.   -  person wich    schedule 22.01.2014


Ответы (3)


С ruby (моя первая попытка ruby ответ):

File.open("combined_old_individual.txt", "r") do |f|
  File.open("combined_old_individual_new.txt","w") do |new_file|
    f.each_line do |line|
      if(/(Oversight Bd|Sciences)$/ =~ line)
        new_file.print line.strip
      else
        new_file.puts line
      end
    end
  end
end
person Digital Trauma    schedule 20.01.2014
comment
Вы имели в виду print вместо printf? Также сравнение можно немного сократить: if line =~ /(Sciences|Oversight Bd_$/ - person Wayne Conrad; 21.01.2014
comment
@WayneConrad - да, это хорошие предложения. printfwrite) оба работают в этом случае, но print, вероятно, более точен. - person Digital Trauma; 22.01.2014

Вы должны понимать, что sed обычно работает построчно, поэтому вы не можете сопоставить \n в своем исходном шаблоне. Однако вы можете сопоставить шаблон в первой строке, а затем вывести следующую строку с помощью команды N, а затем запустить команду замены в буфере, чтобы удалить новую строку следующим образом:

sed -i -e '/Oversight Bd/ {;N;s/\n//;}' /your/file

Запустите из Ruby (без -i, чтобы вывод шел на стандартный вывод):

> cat test_text
aaa
bbb
ccc
aaa
bbb
ccc
> cat test.rb
cmd="sed -e '/aaa/ {;N;s/\\n//;}' test_text"
system(cmd)
> ruby test.rb
aaabbb
ccc
aaabbb
ccc
person wich    schedule 20.01.2014
comment
Привет, это будет работать прямо из командной строки с использованием sed -i '.original' -e ':a' -e 'N' -e '$!ba' -e 's/Oversight Bd\n/Oversight Bd/g' -e 's/Sciences\n/Sciences/g' combined_old_individual.txt", однако я не смог запустить его из скрипта ruby, потому что ruby ​​экранировал символ новой строки. - person Solomon; 20.01.2014
comment
Затем просто снова избегайте обратной косой черты - person wich; 20.01.2014
comment
@Solomon, не было проблем с рубином, который испортил ваши новые строки, если вы правильно их экранируете. Проблема заключалась в том, что ваша исходная команда sed была неверной, вы не можете напрямую сопоставить новую строку, вам нужно использовать команду N, как в моем ответе, тогда она будет работать нормально. - person wich; 22.01.2014

Поскольку вы задаете вопрос в bash, вот чистый -bash решение:

$ r="(Oversight Bd|Sciences)$"
$ while read -r; do printf "%s" "$REPLY"; [[ $REPLY =~ $r ]] || echo; done < combined_old_individual.txt 
random line
Oversight Bdshould be on the same line as the above, but isn't
last line
$ 
person Digital Trauma    schedule 20.01.2014