Thay thế trong tệp văn bản ** mà không có ** biểu thức chính quy


68

Tôi cần thay thế một số văn bản trong một tệp văn bản bằng một thay thế. Thông thường tôi sẽ làm một cái gì đó như

sed -i 's/text/replacement/g' path/to/the/file

Vấn đề là cả hai textreplacementlà các chuỗi phức tạp chứa dấu gạch ngang, dấu gạch chéo, dấu gạch chéo, dấu ngoặc kép, v.v. Nếu tôi thoát khỏi tất cả các nhân vật cần thiết bên trong textmọi thứ sẽ nhanh chóng không thể đọc được. Mặt khác, tôi không cần sức mạnh của các biểu thức thông thường: tôi chỉ cần thay thế văn bản theo nghĩa đen.

Có cách nào để thay thế văn bản mà không sử dụng các biểu thức thông thường với một số lệnh bash không?

Nó sẽ là khá tầm thường để viết một kịch bản làm điều này, nhưng tôi nghĩ rằng nên có một cái gì đó đã tồn tại.


Cần thiết để làm điều đó thông qua bash? Một giải pháp đơn giản sẽ là mở trong Word và thực hiệnfind and replace all
Akash

17
@akash Vì các hệ thống bashluôn được phát hành với Microsoft Word? ;) Không chỉ đùa thôi. OP có thể muốn làm điều này trên một máy từ xa hoặc cho một loạt các tập tin.
slhck

@slhck :) Chà, tôi đoán gedit nên có một tùy chọn tương tự
Akash

Một lựa chọn sẽ là bằng cách nào đó thoát chính xác mọi thứ trước khi chuyển nó sang sed, đây có lẽ là một nỗ lực vô ích khi xem xét tất cả các chuyển đổi và sự khác biệt nền tảng.
l0b0

Câu trả lời:


6

Khi bạn không cần sức mạnh của biểu thức thông thường, đừng sử dụng nó. Điều đó là tốt.
Nhưng, đây không thực sự là một biểu thức thông thường .

sed 's|literal_pattern|replacement_string|g'

Vì vậy, nếu /là vấn đề của bạn, hãy sử dụng |và bạn không cần phải thoát khỏi cái cũ.

ps: về các bình luận, cũng xem câu trả lời Stackoverflow này trên Escape một chuỗi cho mẫu tìm kiếm sed .


Cập nhật: Nếu bạn ổn khi sử dụng Perl, hãy thử \Q\Enhư thế này,
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBrickcũng đã đề xuất một mẹo tương tự với cú pháp Perl mạnh hơn trong một nhận xét tại đây


Cảm ơn bạn, tôi không biết về sự khác biệt giữa / và |
Andrea

64
Tôi không chắc câu trả lời này hữu ích ... Sự khác biệt duy nhất giữa s|||s///là nhân vật tách biệt là khác nhau và do đó một nhân vật không cần phải trốn thoát. Bạn cũng có thể làm như vậy s###. Vấn đề thực sự ở đây là OP không muốn phải lo lắng về việc thoát khỏi nội dung của literal_pattern(hoàn toàn không theo nghĩa đen và sẽ được hiểu là một biểu thức chính quy).
Benj

15
Điều này sẽ không tránh được việc giải thích các nhân vật đặc biệt khác. Điều gì xảy ra nếu tìm kiếm 1234.*aaavới giải pháp của bạn, nó phù hợp hơn nhiều so với dự định 1234\.\*aaa.
Matteo

20
Câu trả lời này không nên được chấp nhận
Steven Lu

2
Điều này bỏ lỡ điểm hoàn toàn. Các văn bản được kết hợp có thể chứa bất kỳ wierdness. Trong trường hợp của tôi, đó là một mật khẩu ngẫu nhiên. Bạn biết những người đó đi như thế nào
Christian Bongiorno

13
export FIND='find this'
export REPLACE='replace with this'
ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" path/to/file

Đây là giải pháp an toàn 100% duy nhất ở đây, bởi vì:

  • Đó là một trạm biến áp tĩnh, không phải là regrec, không cần phải thoát bất cứ thứ gì (do đó, vượt trội hơn so với sử dụng sed)
  • Nó sẽ không bị hỏng nếu chuỗi của bạn chứa }char (do đó, vượt trội so với giải pháp Perl đã gửi)
  • Nó sẽ không phá vỡ với bất kỳ nhân vật, bởi vì ENV['FIND']được sử dụng, không $FIND. Với $FINDhoặc văn bản của bạn được nội tuyến trong mã Ruby, bạn có thể gặp lỗi cú pháp nếu chuỗi của bạn không được giải mã '.

Tôi đã sử dụng export FIND='find this; export REPLACE='replace with this';trong kịch bản bash của tôi để ENV['FIND']ENV['replace']có giá trị mong đợi. Tôi đã thay thế một số chuỗi mã hóa thực sự dài trong một tập tin. Đây chỉ là vé.
DMfll

Đây là một câu trả lời tốt vì nó đáng tin cậy và ruby ​​có mặt khắp nơi. Dựa trên câu trả lời này, bây giờ tôi sử dụng kịch bản shell này .
loevborg

Thật không may, không hoạt động khi TÌM chứa nhiều dòng.
adrelanos

Không có gì có thể ngăn nó hoạt động với nhiều dòng trong TÌM. Sử dụng trích dẫn kép \ n.
Bây giờ

7

Các replacelệnh sẽ thực hiện điều này.

https://linux.die.net/man/1/replace

Thay đổi tại chỗ:

replace text replacement -- path/to/the/file

Để xuất sắc:

replace text replacement < path/to/the/file

Thí dụ:

$ replace '.*' '[^a-z ]{1,3}' <<EOF
> r1: /.*/g
> r2: /.*/gi
> EOF
r1: /[^a-z ]{1,3}/g
r2: /[^a-z ]{1,3}/gi

Các replacelệnh đi kèm với MySQL hoặc MariaDB.


3
hãy tính đến việc thay thế tht không được chấp nhận và có thể không bị từ chối trong tương lai
Rogelio

1
Tại sao trên trái đất, lệnh cơ bản như vậy đi kèm với một cơ sở dữ liệu?
masterxilo

3
@masterxilo Một câu hỏi hay hơn có thể là - tại sao một lệnh cơ bản như vậy không đi kèm với các hệ điều hành hiện đại? ;-)
Mark Thomson


3

kiểm tra kịch bản Perl của tôi. nó thực hiện chính xác những gì bạn cần mà không sử dụng biểu thức chính quy hoặc ngầm định:

https://github.com/Samer-Al-iraqi/Linux-str numplace

str_replace Search Replace File # replace in File in place

STDIN | str_replace Search Replace # to STDOUT

rất tiện dụng phải không? Tôi đã phải học Perl để làm điều đó. bởi vì tôi thực sự cần nó


2

Bạn có thể làm điều đó bằng cách thoát khỏi mô hình của bạn. Như thế này:

keyword_raw='1/2/3'
keyword_regexp="$(printf '%s' "$keyword_raw" | sed -e 's/[]\/$*.^|[]/\\&/g')"
# keyword_regexp is now '1\/2\/3'

replacement_raw='2/3/4'
replacement_regexp="$(printf '%s' "$replacement_raw" | sed -e 's/[\/&]/\\&/g')"
# replacement_regexp is now '2\/3\/4'

echo 'a/b/c/1/2/3/d/e/f' | sed -e "s/$keyword_regexp/$replacement_regexp/"
# the last command will print 'a/b/c/2/3/4/d/e/f'

Tín dụng cho các giải pháp này có tại đây: https://stackoverflow.com/questions/407523/escape-a-opes-for-a-sed-replace-potype

Note1: điều này chỉ hoạt động cho các từ khóa không trống. Từ khóa trống không được chấp nhận bởi sed ( sed -e 's//replacement/').

Note2: thật không may, tôi không biết một công cụ phổ biến KHÔNG sử dụng regrec-s để giải quyết vấn đề. Bạn có thể viết một công cụ như vậy trong Rust hoặc C, nhưng nó không có ở đó theo mặc định.


Điều này hoàn toàn bỏ lỡ quan điểm của OP. Rõ ràng bạn có thể thoát khỏi mô hình, nhưng đối với một số mẫu này thì thật tẻ nhạt.
icecreamsword

@icecreamsword bạn đã đọc câu trả lời của tôi dưới dòng đầu tiên chưa? Kịch bản không thoát tự động .
VasyaNovikov

1

Tôi đã ghép lại một vài câu trả lời khác và đưa ra điều này:

function unregex {
   # This is a function because dealing with quotes is a pain.
   # http://stackoverflow.com/a/2705678/120999
   sed -e 's/[]\/()$*.^|[]/\\&/g' <<< "$1"
}
function fsed {
   local find=$(unregex "$1")
   local replace=$(unregex "$2")
   shift 2
   # sed -i is only supported in GNU sed.
   #sed -i "s/$find/$replace/g" "$@"
   perl -p -i -e "s/$find/$replace/g" "$@"
}

Không hoạt động với dòng mới. Cũng không giúp thoát khỏi dòng mới với \n. Bất kì giải pháp nào?
adrelanos

1

Bạn có thể sử dụng str numplace của php :

php -R 'echo str_replace("\|!£$%&/()=?^\"'\''","replace",$argn),PHP_EOL;'<input.txt >output.txt

Lưu ý: Tuy nhiên, bạn vẫn cần thoát dấu ngoặc đơn 'và dấu ngoặc kép ".


0

Node.JS tương đương với @Nowaker:

export FNAME='moo.txt'
export FIND='search'
export REPLACE='rpl'
node -e 'fs=require("fs");fs.readFile(process.env.FNAME,"utf8",(err,data)=>{if(err!=null)throw err;fs.writeFile(process.env.FNAME,data.replace(process.env.FIND,process.env.REPLACE),"utf8",e=>{if(e!=null)throw e;});});'

0

Đây là một cách làm việc "gần như".

Sử dụng vi hoặc vim.

Tạo một tệp văn bản với sự thay thế của bạn trong đó:

:% sno / chuỗi tìm kiếm của tôi \ "-: # 2; g ('. j'); \\"> / my thay thế =
: x

sau đó thực thi vi hoặc vim từ dòng lệnh:

vi -S commandfile.txt path/to/the/file

:% sno là lệnh vi để thực hiện tìm kiếm và thay thế mà không cần phép thuật.

/ là dấu phân cách tôi chọn.

: x lưu và thoát vi.

Bạn cần thoát dấu gạch chéo ngược '\' the forwardslash '/' có thể được thay thế bằng ví dụ: một dấu hỏi '?' hoặc thứ gì khác không có trong chuỗi tìm kiếm hoặc thay thế của bạn, ống '|' không làm việc cho tôi tho.

ref: https://stackoverflow.com/questions/6254820/perform-a-non-regex-search-replace-in-vim https://vim.fandom.com/wiki/Search_without_need_to_escape_slash http://linuxcommand.org/ lc3_man_pages / vim1.html

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.