Как вставить содержимое файла в другой файл перед шаблоном (маркером)?


32

File1 содержание:

line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 

File2 содержание:

line1-file2     "25"  
line2-file2     "24"  
Pointer-file2   "23"  
line4-file2     "22" 
line5-file2     "21"

После выполнения сценария perl / shell File2содержимое должно стать:

line1-file2     "25"  
line2-file2     "24" 
line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 
Pointer-file2   "23" 
line4-file2     "22" 
line5-file2     "21"

т.е. вставьте содержимое File1в File2перед строкой, которая содержит «Указатель».


1
Также спросили в StackOverflow
Гленн Джекман


По этой причине я уже проголосовал за вопрос ТА, чтобы закрыть его. Больше нет причин закрывать этот вопрос. Кстати, вопрос о SO намного хуже, поэтому логика требует закрыть это, а не это.
Петер говорит восстановить Монику

Ответы:


33

sed имеет функцию для этого, и может сделать модификацию inline:

sed -i -e '/Pointer/r file1' file2

Но это помещает вашу строку указателя выше файла1. Чтобы поставить его ниже, задержите вывод строки:

sed -n -i -e '/Pointer/r file1' -e 1x -e '2,${x;p}' -e '${x;p}' file2 

8
Не могли бы вы объяснить, что -e 1x -e '2,${x;p}' -e '${x;p}'делать? Я понимаю, что вы обмениваетесь данными в буфере шаблонов, а затем распечатываете их, но я не знаю, что и почему вы добавили тихую опцию -nв начале.
HDL

@ jfg956 Можно ли просто заменить и удалить часть «Указатель» из исходного файла. Это я могу выяснить с помощью второго удара sed, но возможно ли сделать это за один прогон?
Александр Цска

17

Без использования sedили awk...

Сначала найдите строку, на которой находится ваш шаблон:

line=$(grep -n 'Pointer' file2 | cut -d ":" -f 1)

Затем используйте 3 команды для вывода желаемого результата:

{ head -n $(($line-1)) file2; cat file1; tail -n +$line file2; } > new_file

Это имеет недостаток доступа в 3 раза файл file2, но может быть яснее , чем sedиз awkраствора.


10

awkделает это довольно легко.
Вставьте строку перед файлом:

awk '/Pointer/{while(getline line<"innerfile"){print line}} //' outerfile >tmp
mv tmp outerfile

Чтобы внутренний файл печатался после Pointerстроки, просто измените порядок шаблонов (вам нужно добавить точку с запятой, чтобы получить действие по умолчанию), и вы можете удалить lineпеременную:

awk '//; /Pointer/{while(getline<"innerfile"){print}}' outerfile >tmp
mv tmp outerfile

И только потому, что никто еще не использовал perl,

# insert file before line
perl -e 'while(<>){if($_=~/Pointer/){system("cat innerfile")};print}' outerfile

# after line
perl -e 'while(<>){print;if($_=~/Pointer/){system("cat innerfile")}}' outerfile

он работает, но получает удаленную строку, содержащую указатель
user1228191

аналогично, как вставить содержимое файла 1 в файл 2 после этой строки «Pointer», содержащей awk
user1228191

@ user1228191 исправил первое, добавил второе.
Кевин

Версия perl, похоже, не работает. system("cat innerfile")выводит innerfileна консоль. Я что-то пропустил?
kaartic

awk команда [gawk '/ <body> / {while (getline line <"$ HOME / bin / SrunScenario.style") {print line}} //' index.html> new_index.html] просто зацикливает и печатает миллионы строк , gawk V4.2.0 Что мне здесь не хватает?
JESii

7

Простая работа для ed:

ed -s file1 <<IN
/Pointer/-r file2
,p
q
IN

-r file1читает в указанном файле после адресной строки, которая в этом случае является строкой, предшествующей первой строке соответствия Pointer. Так что это будет вставлять содержимое file2только один раз, даже если Pointerпроисходит в несколько строк. Если вы хотите вставить его перед каждой соответствующей строкой, добавьте gфлаг lobal:

ed -s file1 <<IN
g/Pointer/-r file2
,p
q
IN

Замените ,pна, wесли вы хотите редактировать файл на месте.


Принятый sedответ работает в большинстве случаев, но если маркер находится в последней строке, команда не будет работать должным образом: она будет вставлять содержимое File1после маркера.
Я изначально пытался с:

sed '/Pointer/{r file1
N}' file2

который также работает нормально (как rи в конце цикла), но имеет ту же проблему, если маркер находится на последней строке ( Nпосле последней строки нет строки ext). Чтобы обойти это, вы можете добавить новую строку в ваш ввод:

sed '/Pointer/{              # like the first one, but this time even if the
r file1                      # marker is on the last line in File2 it
N                            # will be on the second to last line in
}                            # the combined input so N will always work;
${                           # on the last line of input: if the line is
/^$/!{                       # not empty, it means the marker was on the last
s/\n$//                      # line in File2 so the final empty line in the
}                            # input was pulled i\n: remove the latter;
//d                          # if the line is empty, delete it
}' file2 <(printf %s\\n)

Это будет вставлять file2контент перед каждой соответствующей строкой. Чтобы вставить его только перед первой совпадающей строкой, вы можете использовать loop и просто тянуть в nстроку ext, пока не дойдете до конца файла:

sed '/Pointer/{
r file2
N
:l
$!n
$!bl
}
${
/^$/!{
s/\n$//
}
//d
}' file1 <(printf %s\\n)

С этими sedрешениями вы теряете возможность редактировать на месте (но вы можете перенаправить в другой файл).


6

Используйте цикл для чтения строк в file2. Если вы найдете строку, начинающуюся с Pointer, то распечатайте файл1. Это показано ниже:

#!/bin/bash
while IFS= read -r line
do
    if [[ "$line" =~ ^Pointer.*$ ]]
    then
        cat file1
    fi
    echo "$line"
done < file2

4

Есть несколько способов сделать это sed. Одним из способов является задержка чтения, как рекомендуется в принятом ответе. Это также можно записать так:

sed -e '$!N;P;/\nPointer/r file1' -e D file2

... с небольшим явным упреждением вместо того, чтобы предвидеть, реализованным в другом месте с буфером удержания. Это неизбежно приведет к той же самой проблеме с последней строкой, которую @don_crissti отмечает, хотя, потому что N действительно увеличивает цикл строки, и команда read применяется к номеру строки.

Вы можете обойти это:

echo | sed -e '$d;N;P;/\nPointer/r file1' -e D file2 -

Не все seds будут интерпретировать, -чтобы означать стандартный ввод, но многие делают. ( POSIX говорит, sed что поддержка -должна означать стандартный вход, если разработчик хочет -означать стандартный вход ???)

Другой способ - обработать добавленное содержимое по порядку. Есть другая команда, которая планирует вывод таким же образом r, как и ead, и sedбудет применять его и read в порядке, в котором они написаны. Хотя это немного сложнее - это влечет за собой использование одного, sedчтобы aсвязать Pointerсовпадение с выводом другого sedв его сценарии.

sed '   /Pointer/!d                  #only operate on first match
        s/[]^$&\./*[]/\\&/g;H        #escape all metachars, Hold
        s|.*|/&/!p;//!d|p;g          #print commands, exchange
        s|.|r file1&a\\&|;q' file2|  #more commands, quit
        sed -nf - file2              #same input file

Так что в основном первый sedпишет второй sedскрипт, который второй sedчитает на стандартном вводе (может быть ...) и применяет по очереди. Первый sedработает только для первого совпадения для Pointerнайденного, а затем qдля ввода данных. Его работа заключается в ...

  1. s/[]^$&\./*[]/\\&/g;H
    • Убедитесь, что все символы шаблона безопасно защищены от обратной косой черты, потому что второй sedдолжен интерпретировать каждый бит, который он читает буквально, чтобы сделать его правильным. Как только это будет сделано, поместите копию в Hстарое место.
  2. s|.*|/&/!p;//!d|p; x
    • Скажите второй, sedчтобы она pнабирала каждую строку ввода, !кроме той, которую /&/мы только что сохранили по шаблону; а потом на dэле все то же самое. pСнова наберите команды sed, затем xизмените hстарый и шаблонный буферы для работы с нашей сохраненной копией.
  3. s|.|r file1&a\\&|p;q
    • Единственный символ, с которым мы здесь работаем, - это \newline, потому что он sedбудет добавлен перед тем, как мы с ним раньше справились H. Таким образом, мы вставляем команду r file1и следуем за ней с помощью нашей \newline, затем за командой ppend, a\\за aкоторой следует также \newline. Вся остальная часть нашей Hлинии поля следует этой последней \nлинии.

Сценарий, который пишет первым, выглядит примерно так:

/Pointer-file2   "23"/!p;//!d
r file1
a\
Pointer-file2   "23"

По сути, вторая sedбудет печатать каждую строку, а первая sed- для append. Для этой конкретной строки запланированы две отложенные записи в стандартный вывод: первая - это EAD, а вторая - копия строки, которую мы хотим после нее. В этом случае лечение первого даже не требуется (видите? Без обратной косой черты), но важно безопасно избежать того, что я делаю здесь, всякий раз, когда сопоставление с образцом повторно используется в качестве входных данных.rfile1sed

Во всяком случае, так что ... есть несколько способов.


2

Это довольно просто с AWK:

Файл1 в Файл2 до шаблона = "Указатель"

Сначала загрузите содержимое файла File1 в переменную

f1="$(<File1)"

затем сделайте вставку

awk -vf1="$f1" '/Pointer/{print f1;print;next}1' file2

(Или, если вы хотите вставить File1 после «Pointer»)

awk -vf1="$f1" '/Pointer/{print;print f1;next}1' file2

2

мой любимый способ: шаблонизация .

sed 's/CHANGEME/$x/g' origfile | x="$(<file2insert)" envsubst '$x' > newfile

Это заменит каждый случай CHANGEME в origfile содержимым file2insert . Удалите последний g из sed, чтобы заменить только первое вхождение CHANGEME .


Как вы можете использовать $xв своей первой команде, когда она определена только во второй команде?
Тотор

«$ x» в первой команде является только заполнителем, который оценивается envsubst во второй команде. С одинарными кавычками сценария sed $ x не оценивается вашей оболочкой.
NRC

2

[Вставить содержимое файла в другой файл ДО шаблона]

sed -i '/PATTERN/r file1' -e //N file2

[После шаблона]

sed -i '/PATTERN/r file1' file2

Nработает хорошо, но не в том случае, если PATTERN соответствует последней строке ввода
Sundeep

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.