Як вставити вміст файлу в інший файл перед шаблоном (маркером)?


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
glenn jackman


Я вже проголосував питанням щодо закриття з цієї причини, більше немає причин закривати це питання. До речі, питання щодо гіршої якості щодо SO, тому логіка диктує закрити це, а не це.
Peterh каже відновити Моніку

Відповіді:


33

sed має для цього функцію і може робити модифікацію вбудованою:

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 після цього "Покажчик", що містить рядок, використовуючи 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лобальний прапор:

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 робить збільшення циклу лінії і rСвинець команда застосовується по номеру рядка.

Ви можете її обійти:

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
    • Єдиний \nшарм, з яким ми працюємо тут, - це ewline, тому що він sedбуде попереджати його, коли ми Hпереносимо рядок раніше. Отже, ми вставляємо команду r file1і слідуємо за нею нашою \newline, потім команда a\\для append, а також \newline. Весь решта нашої Hлінії eld слідує за останньою \newline.

Сценарій, який пише перший, виглядає приблизно так:

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

В основному другий sedбуде надрукувати кожен рядок, але той, який перший, sedвстановлює його на aдодаток. Для цього конкретного рядка заплановано два затримки запису до стандартного виходу - перший - це ead, а другий - це копія рядка, який ми хочемо після нього. Лікування першого лікаря в цьому випадку навіть не потрібно (див. «Відсутність косої риски»), але важливо безпечно бігти так, як я роблю тут, коли зміна шаблону замінюється як вхідний.rfile1sed

Так чи інакше, так ... є кілька способів.


2

З AWK це досить просто:

Файл1 у File2 перед pattern = "Покажчик"

Спочатку завантажте вміст File1 у змінну

f1="$(<File1)"

потім зробіть вставку

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

(Або якщо ви хочете вставити File1 після "Вказівника")

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.