Cách thay thế toàn bộ một dòng trong tệp văn bản bằng số dòng


152

Tôi có một tình huống mà tôi muốn một tập lệnh bash thay thế toàn bộ một dòng trong một tập tin. Số dòng luôn giống nhau, do đó có thể là một biến số được mã hóa cứng.

Tôi không cố gắng thay thế một số chuỗi con trong dòng đó, tôi chỉ muốn thay thế hoàn toàn dòng đó bằng một dòng mới.

Có bất kỳ phương pháp bash nào để làm điều này (hoặc một cái gì đó đơn giản có thể được ném vào tập lệnh .sh).

Câu trả lời:


228

Không phải là tốt nhất, nhưng điều này sẽ làm việc:

sed -i 'Ns/.*/replacement-line/' file.txt

nơi Nnên được thay thế bằng số dòng mục tiêu của bạn. Điều này thay thế dòng trong tập tin gốc. Để lưu văn bản đã thay đổi trong một tệp khác, hãy bỏ -itùy chọn:

sed 'Ns/.*/replacement-line/' file.txt > new_file.txt

1
Có cách nào để khiến sed sửa đổi tập tin đang đề cập thay vì đổ màn hình không?
dùng788171

8
Đối với tôi nó nói: sed: -e expression #1, char 26: unknown option to ``s'và dòng của tôi là : sed -i '7s/.*/<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpoint/</param-value>/' $TCE_SVN_HOME\trunk\tce\EWC\WebContent\WEB-INF\web.xml. Bất kỳ ý tưởng?
Danijel

4
Mỗi /trong văn bản thay thế sẽ được hiểu là dấu gạch chéo của slệnh trừ khi thoát ( \/). Tuy nhiên, điều dễ nhất để làm là chọn một ký tự khác, không được sử dụng làm dấu phân cách. Ví dụ , sed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk....
chepner

4
Lời giải thích đó hơi khó hiểu và dường như không hoạt động. Nếu các bạn có vấn đề với sed và khả năng thiết lập dấu phân cách của nó, bạn có thể muốn xem cái này: grymoire.com/Unix/Sed.html#uh-2 Nó nói ký tự đầu tiên đằng sau sdấu phân cách. Vì vậy, để thay đổi lệnh của bạn để sử dụng ví dụ, #nó sẽ như thế này:sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
func0der

7
Để mở rộng trên @ meonlol, macOS cũng yêu cầu -enếu -iđược đưa ra; do đó, lệnh chính xác trở thành:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
ELLIOTTCABLE

44

Tôi thực sự đã sử dụng tập lệnh này để thay thế một dòng mã trong tệp cron trên các máy chủ UNIX của công ty chúng tôi một thời gian trước. Chúng tôi đã thực thi nó như tập lệnh shell bình thường và không có vấn đề gì:

#Create temporary file with new line in place
cat /dir/file | sed -e "s/the_original_line/the_new_line/" > /dir/temp_file
#Copy the new file over the original file
mv /dir/temp_file /dir/file

Điều này không đi theo số dòng, nhưng bạn có thể dễ dàng chuyển sang hệ thống dựa trên số dòng bằng cách đặt số dòng trước s/và đặt ký tự đại diện thay thế the_original_line.


17
Sử dụng vô dụng của mèo:sed -e "s/.../.../" /dir/file > /dir/temp_file
chepner

24
Có thể vô dụng cho trình thông dịch / trình biên dịch, nhưng không phải cho một người đọc nó. @ Mã của Kyle đọc độc đáo từ trái sang phải; thành ngữ bạn đã sử dụng IMO thì không, do thực tế là động từ đứng trước danh từ.
Clayton Stanley

1
Có cách nào để hợp nhất hai dòng đó không, ví dụ như bằng cách nào đó,sed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda

22

Giả sử bạn muốn thay thế dòng 4 bằng văn bản "khác". Bạn có thể sử dụng AWK như vậy:

awk '{ if (NR == 4) print "different"; else print $0}' input_file.txt > output_file.txt

AWK coi đầu vào là "bản ghi" được chia thành "các trường". Theo mặc định, một dòng là một bản ghi. NRlà số lượng hồ sơ nhìn thấy. $0đại diện cho bản ghi hoàn chỉnh hiện tại (trong khi đó $1là trường đầu tiên từ bản ghi, v.v. theo mặc định, các trường là các từ trong dòng).

Vì vậy, nếu số dòng hiện tại là 4, hãy in chuỗi "khác" nhưng nếu không thì in dòng không thay đổi.

Trong AWK, mã chương trình được bao quanh { }chạy một lần trên mỗi bản ghi đầu vào.

Bạn cần trích dẫn chương trình AWK bằng dấu ngoặc đơn để giữ cho vỏ không cố gắng diễn giải những thứ như $0.

EDIT: Một chương trình AWK ngắn và thanh lịch hơn từ @chepner trong các bình luận bên dưới:

awk 'NR==4 {$0="different"} { print }' input_file.txt

Chỉ dành cho bản ghi (tức là dòng) số 4, thay thế toàn bộ bản ghi bằng chuỗi "khác nhau". Sau đó, cho mỗi bản ghi đầu vào, in bản ghi.

Rõ ràng các kỹ năng AWK của tôi là gỉ! Cảm ơn bạn, @chepner.

EDIT: và xem thêm một phiên bản thậm chí ngắn hơn từ @Dennis Williamson:

awk 'NR==4 {$0="different"} 1' input_file.txt

Làm thế nào điều này hoạt động được giải thích trong các ý kiến: 1luôn luôn đánh giá đúng, vì vậy khối mã liên quan luôn chạy. Nhưng không có khối mã liên quan, có nghĩa là AWK thực hiện hành động mặc định của nó là chỉ in toàn bộ dòng. AWK được thiết kế để cho phép các chương trình ngắn gọn như thế này.


3
Ngắn hơn một chút : awk 'NR==4 {$0="different"} { print }' input_file.txt.
chepner

2
@chepner: Ngắn hơn một chút:awk 'NR==4 {$0="different"}1' input_file.txt
Tạm dừng cho đến khi có thông báo mới.

@DennisWilliamson, tôi thậm chí không biết nó hoạt động như thế nào! Điều đó 1làm gì? (Lưu ý: Tôi đã thử nghiệm và nó thực sự hoạt động! Tôi chỉ không hiểu làm thế nào.)
steveha

1
Tôi nghĩ rằng sẽ có một cách để viết tắt các bản in vô điều kiện! @stevaha: 1 chỉ là giá trị thực, nghĩa là "mẫu" luôn khớp. Và hành động mặc định cho một trận đấu là in dòng hiện tại.
chepner

19

Đưa ra tệp thử nghiệm này (test.txt)

Lorem ipsum dolor sit amet,
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

lệnh sau sẽ thay thế dòng đầu tiên thành "văn bản dòng mới"

$ sed '1 c\
> newline text' test.txt

Kết quả:

newline text
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

Tìm thêm thông tin tại đây

http: //www.thegeek ware.com/2009/11/unix-sed-tutorial-append-insert-replace-and-count-file-lines/


2
Đây phải là câu trả lời chính xác. Cờ c thực hiện chính xác những gì được yêu cầu (thay thế một dòng được đánh số bằng văn bản đã cho).
adelphus

Cú pháp thay thế với sed không lặp lại tập tin trên thiết bị xuất chuẩn: stackoverflow.com/a/13438118/4669135
Gabriel Devillers

Đó là câu trả lời đúng, nhưng tại sao đường kẻ xấu lại bị phá vỡ? sed '1 cnewline text' test.txtcũng sẽ làm điều đó
dev-null

7

trong bash, thay thế N, M bằng số dòng và xxx yyy bằng những gì bạn muốn

i=1
while read line;do
  if((i==N));then
    echo 'xxx'
  elif((i==M));then
    echo 'yyy'
  else
    echo "$line"
  fi
  ((i++))
done  < orig-file > new-file

BIÊN TẬP

Thực tế trong giải pháp này có một số vấn đề, với các ký tự "\ 0" "\ t" và "\"

"\ t", có thể được giải quyết bằng cách đặt IFS = trước khi đọc: "\", ở cuối dòng với -r

IFS= read -r line

nhưng với "\ 0", biến bị cắt cụt, không có giải pháp nào trong bash thuần: Gán chuỗi chứa null-character (\ 0) cho một biến trong Bash Nhưng trong tệp văn bản bình thường không có ký tự nul \ 0

perl sẽ là một lựa chọn tốt hơn

perl -ne 'if($.==N){print"xxx\n"}elsif($.==M){print"yyy\n"}else{print}' < orig-file > new-file

Tôi thậm chí không nghĩ đến việc thử một giải pháp BASH thuần túy!
steveha

4
# Replace the line of the given line number with the given replacement in the given file.
function replace-line-in-file() {
    local file="$1"
    local line_num="$2"
    local replacement="$3"

    # Escape backslash, forward slash and ampersand for use as a sed replacement.
    replacement_escaped=$( echo "$replacement" | sed -e 's/[\/&]/\\&/g' )

    sed -i "${line_num}s/.*/$replacement_escaped/" "$file"
}

3

Câu trả lời tuyệt vời từ Chepner. Nó đang làm việc cho tôi trong bash Shell.

 # To update/replace the new line string value with the exiting line of the file
 MyFile=/tmp/ps_checkdb.flag

 `sed -i "${index}s/.*/${newLine}/" $MyFile`

ở đây
index- Dòng không
newLine- chuỗi dòng mới mà chúng tôi muốn thay thế.


Tương tự dưới đây mã được sử dụng để đọc một dòng cụ thể trong tệp. Điều này sẽ không ảnh hưởng đến tập tin thực tế.

LineString=`sed "$index!d" $MyFile` 

ở đây
!d- sẽ xóa các dòng khác dòng không $index Vì vậy, chúng tôi sẽ nhận đầu ra dưới dạng chuỗi dòng không có $indextrong tệp.


nhưng nó bash của tôi tại sao kết quả chỉ hiển thị ${newline}?
sikisis

1

Trên mac tôi đã sử dụng

sed -i '' -e 's/text-on-line-to-be-changed.*/text-to-replace-the=whole-line/' file-name

-2

Bạn thậm chí có thể truyền tham số cho lệnh sed:

kiểm tra

#!/bin/bash
echo "-> start"
for i in $(seq 5); do
  # passing parameters to sed
  j=$(($i+3))
  sed -i "${j}s/.*/replaced by '$i'!/" output.dat
done
echo "-> finished"
exit 

đầu ra gốc.dat:

a
b
c
d
e
f
g
h
i
j

Đang thực thi ./test.sh cung cấp output.dat mới

a
b
c
replaced by '1'!
replaced by '2'!
replaced by '3'!
replaced by '4'!
replaced by '5'!
i
j

1
Tại sao bạn cần một vòng lặp cho ví dụ này, chỉ làm xáo trộn giải pháp? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Cướp

Bạn không cần một vòng lặp cho việc này
chandu vutukuri
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.