Tìm và thay thế văn bản trong một tệp bằng các lệnh


Câu trả lời:


1053
sed -i 's/original/new/g' file.txt

Giải trình:

  • sed = Truyền phát trực tuyến
  • -i = tại chỗ (nghĩa là lưu lại vào tệp gốc)
  • Chuỗi lệnh:

    • s = lệnh thay thế
    • original = một biểu thức chính quy mô tả từ cần thay thế (hoặc chỉ chính từ đó)
    • new = văn bản để thay thế nó bằng
    • g = toàn cầu (nghĩa là thay thế tất cả và không chỉ là lần xuất hiện đầu tiên)
  • file.txt = tên tệp


3
@Akiva Nếu bạn bao gồm các ký tự đặc biệt regex trong tìm kiếm của bạn sedsẽ khớp với chúng. Thêm một -rcờ nếu bạn muốn sử dụng REs mở rộng thay thế.
cscarney

32
@mcExchange Nếu cụ thể là /ký tự mà bạn cần khớp, bạn chỉ có thể sử dụng một số ký tự khác làm dấu phân cách (ví dụ 's_old/text_new/text_g'). Nếu không, bạn có thể đặt \ trước bất kỳ $ * . [ \ ^để có được ký tự chữ.
cscarney

3
@BrianZ Theo như hệ thống tệp có liên quan thì đầu ra của sed là một tệp mới có cùng tên. Đây là một trong những lỗi thường được báo cáo không phải là lỗi
cscarney

16
Lệnh OSX sed -i '.bak' 's/original/new/g' file.txtcũng có thể được chạy với phần mở rộng có độ dài bằng không sed -i '' 's/original/new/g' file.txt, sẽ không tạo ra bản sao lưu.
Kirk

19
Người dùng MacOS sẽ phải thêm '' "sau -i làm tham số cho -i ed.gs/2016/01/26/os-x-sed-invalid-command-code để tệp sẽ được ghi đè.
geoyws

32

Có một số cách khác nhau để làm điều này. Một là sử dụng sedvà Regex. SED là một Trình chỉnh sửa luồng để lọc và chuyển đổi văn bản. Một ví dụ như sau:

marco@imacs-suck: ~$ echo "The slow brown unicorn jumped over the hyper sleeping dog" > orly
marco@imacs-suck: ~$ sed s/slow/quick/ < orly > yarly
marco@imacs-suck: ~$ cat yarly
The quick brown unicorn jumped over the hyper sleeping dog

Một cách khác có thể có ý nghĩa hơn < strin> stroutlà với đường ống!

marco@imacs-suck: ~$ cat yarly | sed s/unicorn/fox/ | sed s/hyper/lazy/ > nowai
marco@imacs-suck: ~$ cat nowai 
The quick brown fox jumped over the lazy sleeping dog

6
lưu ý cattrong cat file | sed '...'là không cần thiết. Bạn có thể nói trực tiếp sed '...' file.
fedorqui

1
Thật vậy, điều này có thể được giảm hơn nữa: sed -i'.bak' -e 's/unicorn/fox/g;s/hyper/brown/g' yarlysẽ lấy tệp yarly và thực hiện 2 thay đổi tại chỗ trong khi tạo bản sao lưu. Sử dụng time bash -c "$COMMAND"theo thời gian nó cho thấy phiên bản này nhanh hơn ~ 5 lần.
pbhj

23

Có vô số cách để đạt được nó. Tùy thuộc vào mức độ phức tạp của những gì người ta cố gắng đạt được khi thay thế chuỗi và tùy thuộc vào công cụ mà người dùng quen thuộc, một số phương pháp có thể được ưa thích hơn các phương pháp khác.

Trong câu trả lời này, tôi đang sử dụng input.txttệp đơn giản , bạn có thể sử dụng để kiểm tra tất cả các ví dụ được cung cấp ở đây. Nội dung tập tin:

roses are red , violets are blue
This is an input.txt and this doesn't rhyme

BASH

Bash không thực sự có nghĩa là để xử lý văn bản, nhưng thay thế đơn giản có thể được thực hiện thông qua mở rộng tham số , đặc biệt ở đây chúng ta có thể sử dụng cấu trúc đơn giản ${parameter/old_string/new_string}.

#!/bin/bash
while IFS= read -r line
do
    case "$line" in
       *blue*) printf "%s\n" "${line/blue/azure}" ;;
       *) printf "%s\n" "$line" ;;
    esac
done < input.txt

Tập lệnh nhỏ này không thực hiện thay thế tại chỗ, nghĩa là bạn sẽ phải lưu văn bản mới vào tệp mới và loại bỏ tệp cũ hoặc mv new.txt old.txt

Lưu ý bên lề: nếu bạn tò mò về lý do tại sao while IFS= read -r ; do ... done < input.txtđược sử dụng, về cơ bản, cách đọc từng dòng tệp của shell. Xem cái này để tham khảo.

AWK

AWK, là một tiện ích xử lý văn bản, khá thích hợp cho nhiệm vụ đó. Nó có thể thay thế đơn giản và nâng cao hơn nhiều dựa trên các biểu thức thông thường . Nó cung cấp hai chức năng: sub()gsub(). Cái đầu tiên chỉ thay thế lần xuất hiện đầu tiên, trong khi lần thứ hai - thay thế lần xuất hiện trong toàn bộ chuỗi. Chẳng hạn, nếu chúng ta có chuỗi one potato two potato, đây sẽ là kết quả:

$ echo "one potato two potato" | awk '{gsub(/potato/,"banana")}1'
one banana two banana

$ echo "one potato two potato" | awk '{sub(/potato/,"banana")}1'                                      
one banana two potato 

AWK có thể lấy một tệp đầu vào làm đối số, do đó, thực hiện cùng một việc với input.txt, sẽ dễ dàng:

awk '{sub(/blue/,"azure")}1' input.txt

Tùy thuộc vào phiên bản AWK mà bạn có, nó có thể có hoặc không có chỉnh sửa tại chỗ, do đó, thông lệ là lưu và thay thế văn bản mới. Ví dụ như một cái gì đó như thế này:

awk '{sub(/blue/,"azure")}1' input.txt > temp.txt && mv temp.txt input.txt

SED

Sed là một biên tập viên dòng. Nó cũng sử dụng các biểu thức thông thường, nhưng đối với các thay thế đơn giản, nó đủ để làm:

sed 's/blue/azure/' input.txt

Điều tốt về công cụ này là nó có chỉnh sửa tại chỗ, bạn có thể bật bằng -icờ.

Perl

Perl là một công cụ khác thường được sử dụng để xử lý văn bản, nhưng nó là ngôn ngữ có mục đích chung và được sử dụng trong mạng, quản trị hệ thống, ứng dụng máy tính để bàn và nhiều nơi khác. Nó đã mượn rất nhiều khái niệm / tính năng từ các ngôn ngữ khác như C, sed, awk và các ngôn ngữ khác. Thay thế đơn giản có thể được thực hiện như vậy:

perl -pe 's/blue/azure/' input.txt

Giống như sed, perl cũng có cờ -i.

Con trăn

Ngôn ngữ này rất linh hoạt và cũng được sử dụng trong rất nhiều ứng dụng. Nó có rất nhiều hàm để làm việc với các chuỗi, trong số đó là replace(), vì vậy nếu bạn có biến như thế var="Hello World", bạn có thể làmvar.replace("Hello","Good Morning")

Cách đơn giản để đọc tệp và thay thế chuỗi trong đó sẽ là như vậy:

python -c "import sys;lines=sys.stdin.read();print lines.replace('blue','azure')" < input.txt

Tuy nhiên, với Python, bạn cũng cần xuất ra tệp mới, bạn cũng có thể thực hiện từ bên trong tập lệnh. Ví dụ, đây là một đơn giản:

#!/usr/bin/env python
import sys
import os
import tempfile

tmp=tempfile.mkstemp()

with open(sys.argv[1]) as fd1, open(tmp[1],'w') as fd2:
    for line in fd1:
        line = line.replace('blue','azure')
        fd2.write(line)

os.rename(tmp[1],sys.argv[1])

Kịch bản lệnh này được gọi với input.txtđối số dòng lệnh. Lệnh chính xác để chạy tập lệnh python với đối số dòng lệnh sẽ là

 $ ./myscript.py input.txt

hoặc là

$ python ./myscript.py input.txt

Tất nhiên, đảm bảo rằng ./myscript.pytrong thư mục làm việc hiện tại của bạn và theo cách đầu tiên, hãy đảm bảo rằng nó được thiết lập có thể thực thi được vớichmod +x ./myscript.py

Python cũng có thể có các biểu thức chính quy, đặc biệt, có remô-đun, có re.sub()chức năng, có thể được sử dụng để thay thế nâng cao hơn.


1
Đẹp tổng hợp! Một cách khác có thể không được đề cập ở đây là sử dụng trlệnh trong unix
Tapajit Dey

1
@TapajitDey Vâng, tr là một công cụ tuyệt vời, nhưng lưu ý rằng nó là để thay thế bộ ký tự (ví dụ tr abc cdesẽ dịch ađể c, bđể dNó là một chút khác nhau từ thay thế toàn bộ từ như với. sedHoặcpython
Sergiy Kolodyazhnyy

22

Bạn có thể sử dụng Vim trong chế độ Ex:

ex -s -c '%s/OLD/NEW/g|x' file
  1. % chọn tất cả các dòng

  2. s thay thế

  3. g thay thế tất cả các trường hợp trong mỗi dòng

  4. x viết nếu thay đổi đã được thực hiện (họ có) và thoát


21

Thông qua lệnh gsub của awk,

awk '{gsub(/pattern/,"replacement")}' file

Thí dụ:

awk '{gsub(/1/,"0");}' file

Trong ví dụ trên, tất cả các số 1 được thay thế bằng 0 bất kể cột nằm ở đâu.


Nếu bạn muốn thay thế trên một cột cụ thể, thì hãy làm như thế này,

awk '{gsub(/pattern/,"replacement",column_number)}' file

Thí dụ:

awk '{gsub(/1/,"0",$1);}' file

Nó chỉ thay thế 1 bằng 0 trên cột đầu tiên.

Thông qua Perl,

$ echo 'foo' | perl -pe 's/foo/bar/g'
bar

Tôi đã sử dụng cái này trên thiết bị đầu cuối MacOS và nó chẳng làm gì cả ...
Jim

Đã thử nghiệm trên Alpine Linux (trong Docker container) và không có đầu ra
Salathiel Genèse

@ SalathielGenèse bạn đang cố gắng đạt được điều gì?
Avinash Raj

Tôi đang xem tập tin với inotifywaitthuộc shenv, và báo cáo dữ liệu ở định dạng CSV (vì định dạng tùy chỉnh là buggy). Sau đó tôi đã tìm ra không có cách đơn giản nào để xử lý tài liệu CSV trong các tập lệnh shell ... Và tôi muốn nó rất nhẹ. Vì vậy, tôi bắt đầu một kịch bản khá đơn giản để phân tích và báo cáo CSV. Tôi đã đọc thông số CSV và nhận thấy nó được xây dựng kỹ lưỡng hơn tôi mong đợi và hỗ trợ giá trị đa dòng được gói trong dấu ngoặc kép. Tôi đã dựa vào sedmã thông báo nhưng sớm nhận ra rằng ngay cả những gì sedgọi là đa dòng cũng lên đến hai dòng. Sau đó, nếu một trong các giá trị CSV của tôi trải dài trên hai dòng thì sao?
Salathiel Genèse

tốt hơn để hỏi vấn đề của bạn như câu hỏi.
Avinash Raj

8

seds tream ed itor , trong đó bạn có thể sử dụng |(ống) để gửi con suối chuẩn (STDIN và STDOUT cụ thể) thông qua sedvà thay đổi chúng theo chương trình một cách nhanh chóng, làm cho nó một công cụ hữu ích trong truyền thống triết lý Unix; nhưng cũng có thể chỉnh sửa các tập tin trực tiếp bằng cách sử dụng -itham số được đề cập dưới đây.
Hãy xem xét những điều sau đây :

sed -i -e 's/few/asd/g' hello.txt

s/được sử dụng để s ubstitute biểu thức tìm thấy fewvới asd:

Số ít, những người dũng cảm.


Các asd, dũng cảm.

/glà viết tắt của "toàn cầu", có nghĩa là làm điều này cho toàn bộ dòng. Nếu bạn rời khỏi /g(với s/few/asd/, luôn cần có ba dấu gạch chéo bất kể điều gì) và fewxuất hiện hai lần trên cùng một dòng, chỉ lần đầu tiên fewđược thay đổi thành asd:

Số ít đàn ông, ít phụ nữ, dũng cảm.


Đàn ông asd, ít phụ nữ, dũng cảm.

Điều này hữu ích trong một số trường hợp, như thay đổi các ký tự đặc biệt ở đầu dòng (ví dụ, thay thế các ký hiệu lớn hơn một số người sử dụng để trích dẫn tài liệu trước đó trong các chủ đề email bằng một tab ngang trong khi để lại bất đẳng thức đại số được trích dẫn sau dòng không bị ảnh hưởng), nhưng trong ví dụ của bạn, nơi bạn xác định rằng bất cứ nơi nào few xảy ra nó nên được thay thế, hãy chắc chắn rằng bạn có điều đó /g.

Hai tùy chọn (cờ) sau đây được kết hợp thành một , -ie:

-itùy chọn được sử dụng để chỉnh sửa i n đặt trên các tập tin hello.txt.

-etùy chọn này cho biết lệnh e xpression / lệnh chạy, trong trường hợp này s/.

Lưu ý: Điều quan trọng là bạn sử dụng -i -eđể tìm kiếm / thay thế. Nếu bạn làm như vậy -ie, bạn tạo một bản sao lưu của mọi tệp có chữ 'e' được thêm vào.


2

Bạn có thể làm như thế này:

locate <part of filaname to locate> | xargs sed -i -e "s/<Old text>/<new text>/g" 

Ví dụ: để thay thế tất cả các lần xuất hiện [logdir ',' '] (không có []) bằng [logdir', os.getcwd ()] trong tất cả các tệp là kết quả của lệnh định vị, hãy:

ex1:

locate tensorboard/program.py | xargs sed -i -e "s/old_text/NewText/g"

ex2:

locate tensorboard/program.py | xargs sed -i -e "s/logdir', ''/logdir', os.getcwd()/g"

trong đó [tenorboard / program.py] là tệp để tìm kiếm


Chào. Sự lựa chọn của bạn về chuỗi ( logdir', ''-> /logdir', os.getcwd()) làm cho câu trả lời này khó phân tích. Ngoài ra, đáng để xác định rằng câu trả lời của bạn trước tiên định vị các tệp để sử dụng sed, bởi vì đó không phải là một phần của câu hỏi.
mwfearnley

Xin chào, câu trả lời này là cả tìm kiếm và thay thế tất cả nếu nó tìm thấy <văn bản cũ> trong tệp.
Nguyễn Tuấn Anh

Tôi chọn câu trả lời này cho tất cả những gì họ sử dụng tenorboard trong máy ảnh, những người muốn thay đổi lệnh từ: tenorboard --logdir = '/ path / to / log / thư mục /' để sử dụng: chỉ tenorboard, khi ở trong thư mục log. nó rất thuận tiện
Nguyễn Tuấn Anh
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.