Làm cách nào để chèn nội dung của tệp vào tệp khác trước mẫu (điểm đánh dấu)?


32

File1 nội dung:

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

File2 nội dung:

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

Sau khi thực thi tập lệnh perl / shell, File2nội dung sẽ trở thành:

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"

tức là dán nội dung của File1vào File2trước dòng có chứa "Con trỏ".


1
Cũng được hỏi tại StackOverflow
glenn jackman


Tôi đã bỏ phiếu đã đặt câu hỏi SO cho việc đóng cửa vì lý do này, không có thêm lý do để đóng câu hỏi này. Btw, câu hỏi là chất lượng kém hơn nhiều trên SO, vì vậy logic ra lệnh đóng cái đó và không phải cái này.
peterh nói phục hồi Monica

Câu trả lời:


33

sed có một chức năng cho điều đó và có thể thực hiện sửa đổi nội tuyến:

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

Nhưng điều này đặt dòng Con trỏ của bạn lên trên tệp1. Để đặt nó bên dưới, trì hoãn đầu ra dòng:

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

8
Bạn có thể vui lòng giải thích những gì -e 1x -e '2,${x;p}' -e '${x;p}'làm? Tôi hiểu rằng bạn trao đổi công cụ trong bộ đệm mẫu và sau đó in nó nhưng tôi không biết tại sao bạn lại thêm tùy chọn im lặng -nlúc đầu.
hdl

@ jfg956 Có thể chỉ cần thay thế và xóa phần 'Con trỏ' khỏi tệp gốc. Điều này tôi có thể tìm ra với một lần quét thứ hai của sed, nhưng liệu có thể làm điều đó trong một lần chạy không?
Alexander Cska

17

Không sử dụng sedhoặc awk...

Đầu tiên, tìm dòng của bạn trên đó là mẫu của bạn:

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

Sau đó, sử dụng 3 lệnh để xuất kết quả mong muốn:

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

Điều này có nhược điểm của việc tiếp cận 3 lần so với tập tin file2, nhưng có thể rõ ràng hơn so với một sedsố awkgiải pháp.


10

awklàm cho điều này khá dễ dàng.
Chèn dòng trước tập tin:

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

Để thực hiện in tệp bên trong sau Pointerdòng, chỉ cần chuyển đổi thứ tự của các mẫu (bạn cần thêm dấu chấm phẩy để có hành động mặc định) và bạn có thể thả linebiến:

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

Và chỉ vì chưa có ai sử dụng 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

nó hoạt động, nhưng nó đã bị xóa dòng chứa con trỏ
user1228191

tương tự, làm thế nào để Dán nội dung của tệp 1 vào tệp 2 sau dòng "Con trỏ" có chứa awk
user1228191

@ user1228191 Đã sửa lỗi thứ nhất, thêm lần thứ hai.
Kevin

Phiên bản 'perl' dường như không hoạt động. system("cat innerfile")xuất ra innerfilebàn điều khiển. Tui bỏ lỡ điều gì vậy?
kaartic

lệnh awk [gawk '/ <body> / {while (dòng getline <"$ HOME / bin / SrunScenario.style") {print line}} //' index.html> new_index.html] chỉ lặp và in hàng triệu dòng . gawk V4.2.0 Tôi đang thiếu gì ở đây?
JESii

7

Một công việc dễ dàng cho ed:

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

-r file1đọc trong tệp được chỉ định đến sau dòng địa chỉ, trong trường hợp này là dòng trước khớp dòng đầu tiên Pointer. Vì vậy, điều này sẽ chèn nội dung file2chỉ một lần ngay cả khi Pointerxảy ra trên nhiều dòng. Nếu bạn muốn chèn nó trước mỗi dòng phù hợp, hãy thêm gcờ lobal:

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

Thay thế ,pbằng wnếu bạn muốn chỉnh sửa tập tin tại chỗ.


sedCâu trả lời được chấp nhận không hoạt động trong hầu hết các trường hợp nhưng nếu điểm đánh dấu nằm ở dòng cuối cùng, lệnh sẽ không hoạt động như mong đợi: nó sẽ chèn nội dung File1sau điểm đánh dấu.
Tôi đã thử ban đầu với:

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

cũng hoạt động tốt (như rsẽ thực hiện phép thuật của nó vào cuối chu kỳ) nhưng có cùng một vấn đề nếu điểm đánh dấu nằm ở dòng cuối cùng (không có Ndòng mở rộng sau dòng cuối cùng). Để khắc phục điều đó, bạn có thể thêm một dòng mới vào đầu vào của mình:

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)

Điều này sẽ chèn file2nội dung trước mỗi dòng phù hợp. Để chỉ chèn nó trước dòng phù hợp đầu tiên, bạn có thể sử dụng một loop và chỉ cần kéo vào ndòng ext cho đến khi bạn kết thúc tệp:

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

Với các sedgiải pháp này, bạn mất khả năng chỉnh sửa tại chỗ (nhưng bạn có thể chuyển hướng đến tệp khác).


6

Sử dụng một vòng lặp để đọc các dòng trong file2. Nếu bạn tìm thấy một dòng bắt đầu bằng Pointer, sau đó in ra tệp1. Điều này được hiển thị dưới đây:

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

4

Có một vài cách để đi về w / sed. Một cách là đọc chậm như được khuyến nghị trong câu trả lời được chấp nhận. Nó cũng có thể được viết như sau:

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

... với một chút nhìn rõ phía trước thay vì nhìn phía sau được thực hiện ở nơi khác với bộ đệm giữ. Tuy nhiên, điều đó chắc chắn sẽ có cùng một vấn đề với dòng cuối cùng mà @don_crissti lưu ý, bởi vì N không làm tăng chu kỳ dòng và rlệnh ead được áp dụng theo số dòng.

Bạn có thể đi xung quanh nó:

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

Không phải tất cả seds sẽ diễn giải -ý nghĩa đầu vào tiêu chuẩn, nhưng nhiều người làm. ( POSIX nói sed nên hỗ trợ -có nghĩa là chuẩn trong nếu người triển khai muốn -có nghĩa là chuẩn trong ???)

Một cách khác là xử lý nội dung được nối theo thứ tự. Có một lệnh khác lên lịch đầu ra theo cách tương tự r, và sedsẽ áp dụng nó và rđọc theo thứ tự chúng được viết theo kịch bản. Đó là một chút tham gia nhiều hơn mặc dù - nó đòi hỏi phải sử dụng một sedđến append các Pointertrận đấu tới đầu ra của người khác sedtrong kịch bản của mình.

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

Vì vậy, về cơ bản, tập lệnh sedthứ nhất sedviết tập lệnh thứ hai , tập lệnh thứ hai sedđọc trên đầu vào tiêu chuẩn (có thể ...) và lần lượt áp dụng. Cái đầu tiên sedchỉ hoạt động trên trận đấu đầu tiên Pointerđược tìm thấy và sau đó là qđầu vào uits. Công việc của nó là ...

  1. s/[]^$&\./*[]/\\&/g;H
    • Hãy chắc chắn rằng tất cả các ký tự mẫu được thoát khỏi dấu gạch chéo một cách an toàn bởi vì ký tự thứ hai sedsẽ cần diễn giải từng bit nó đọc theo nghĩa đen để làm cho đúng. Khi đã xong, đặt một bản sao trong Hkhông gian cũ.
  2. s|.*|/&/!p;//!d|p; x
    • Nói thứ hai sedđể ptrích xuất mọi dòng đầu vào, !nhưng dòng /&/chúng ta chỉ bảo mật mẫu; và sau đó để xóa bỏ dtất cả như nhau. print các lệnh ở lần thứ hai sed, sau đó e xthay đổi hbộ đệm cũ và mẫu để làm việc trên bản sao đã lưu của chúng tôi.
  3. s|.|r file1&a\\&|p;q
    • Char duy nhất chúng tôi làm việc ở đây là một \newline bởi vì sedsẽ có trước một khi chúng tôi đưa Hra dòng trước đó. Vì vậy, chúng tôi chèn lệnh r file1và làm theo nó với \newline của chúng tôi sau đó lệnh a\\cho ppend acũng theo sau bởi một \newline. Tất cả phần còn lại của Hdòng người lớn của chúng tôi theo \newline cuối cùng đó.

Kịch bản mà người viết đầu tiên trông giống như thế này:

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

Về cơ bản, cái thứ hai sedsẽ in mọi dòng nhưng cái đầu tiên sedsẽ thiết lập nó thành append. Đối với dòng cụ thể đó, hai dòng ghi chậm vào tiêu chuẩn được lên lịch - đầu tiên là read của file1và thứ hai là một bản sao của dòng chúng tôi muốn sau nó. Việc sedlàm bác sĩ đầu tiên thậm chí không cần thiết trong trường hợp này (xem? Không có dấu gạch chéo ngược) nhưng điều quan trọng là phải thoát an toàn theo cách tôi làm ở đây bất cứ khi nào một kết quả khớp mẫu được đưa vào làm đầu vào.

Dù sao đi nữa, vì vậy ... có một vài cách.


2

Điều này khá đơn giản với AWK:

File1 vào File2 trước mẫu = "Con trỏ"

Đầu tiên tải nội dung của File1 vào một biến

f1="$(<File1)"

sau đó thực hiện chèn

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

(Hoặc, nếu bạn muốn chèn File1 sau "Con trỏ")

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

2

cách ưa thích của tôi: templating .

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

Điều này sẽ thay thế mọi sự xuất hiện CHANGEME trong origfile bằng nội dung của file2insert . Loại bỏ g cuối cùng khỏi sed để chỉ thay thế sự xuất hiện đầu tiên của THAY ĐỔI .


Làm thế nào bạn có thể sử dụng $xtrong lệnh đầu tiên của bạn, khi nó chỉ được xác định trong lệnh thứ hai của bạn?
Totor

"$ x" trong lệnh đầu tiên chỉ là một trình giữ chỗ được đánh giá bởi envsubst trong lệnh thứ hai. Với các trích dẫn duy nhất của tập lệnh sed, $ x không được đánh giá bởi trình bao của bạn.
nrc

2

[Chèn nội dung tệp vào một tệp khác TRƯỚC mẫu]

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

[Sau mẫu]

sed -i '/PATTERN/r file1' file2

Nhoạt động độc đáo, nhưng không hoạt động nếu MẪU khớp với dòng đầu vào cuối cùng
Sundeep

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.