2011-08-25 36 views
10

Giả sử tôi có hai danh sách chuỗi (danh sách A và danh sách B) với cùng số lượng mục nhập, N, trong mỗi danh sách và tôi muốn thay thế tất cả các lần xuất hiện của phần tử thứ n của A bằng phần tử thứ n của B trong một tập tin trong Unix (lý tưởng bằng cách sử dụng kịch bản Bash).Cách hiệu quả để thay thế danh sách chuỗi bằng một danh sách khác trong tệp Unix là gì?

Cách hiệu quả nhất để thực hiện việc này là gì?

Cách không hiệu quả là thực hiện N cuộc gọi đến "sed s/stringA/stringB/g".

Trả lời

9

Điều này sẽ thực hiện trong một lần. Nó đọc listA và listB thành mảng awk, sau đó cho mỗi dòng của linput, nó kiểm tra từng từ và nếu từ đó được tìm thấy trong listA, từ đó được thay thế bằng từ tương ứng trong listB.

awk ' 
    FILENAME == ARGV[1] { listA[$1] = FNR; next } 
    FILENAME == ARGV[2] { listB[FNR] = $1; next } 
    { 
     for (i = 1; i <= NF; i++) { 
      if ($i in listA) { 
       $i = listB[listA[$i]] 
      } 
     } 
     print 
    } 
' listA listB filename > filename.new 
mv filename.new filename 

tôi giả sử các chuỗi trong Lista không chứa khoảng trắng (tách lĩnh vực mặc định awk của)

+1

Đẹp, tổng thể, nhưng với một vấn đề tiềm năng. Giải pháp này không nhất thiết phải giữ khoảng cách giữa các dòng trên các dòng thay đổi được thực hiện; các khoảng trắng được thay đổi thành các khoảng trắng. Vì chúng ta không biết bản chất của văn bản, nó có thể không phải là một vấn đề và thậm chí có thể trở thành một lợi thế. Dù sao, 1 từ tôi. –

+0

Điều này rõ ràng là hiệu quả hơn so với giải pháp dưới đây viết kịch bản lệnh sed. Kết thúc trong 3 phút những gì đã 3 ngày sử dụng các giải pháp sed. Cũng chỉ thay thế toàn bộ từ, mặc dù đây không phải là những gì được hỏi. –

+0

Điều này giải quyết mục đích, nhưng làm thế nào để bảo tồn các khoảng trắng? Nó xuất hiện kịch bản awk thay thế chúng chỉ với một khoảng trắng. – Guru

6

Gọi điện tới sed để viết tập lệnh sed và một ứng dụng khác để sử dụng? Nếu danh sách của bạn là trong các tập tin listAlistB, sau đó:

paste -d : listA listB | sed 's/\([^:]*\):\([^:]*\)/s%\1%\2%/' > sed.script 
sed -f sed.script files.to.be.mapped.* 

Tôi đang làm cho một số giả định sâu rộng về 'từ' không chứa hoặc thư đại tràng hoặc các ký hiệu phần trăm, nhưng bạn có thể điều chỉnh xung quanh đó. Một số phiên bản của sed có giới hạn trên về số lượng lệnh có thể được chỉ định; nếu đó là vấn đề vì danh sách từ đủ lớn, bạn có thể phải tách tập lệnh sed đã tạo thành các tệp riêng biệt được áp dụng - hoặc thay đổi để sử dụng thứ gì đó không có giới hạn (ví dụ: Perl).

Một mục khác cần lưu ý là chuỗi các thay đổi. Nếu bạn muốn trao đổi hai từ, bạn cần phải cẩn thận các danh sách từ của bạn. Nói chung, nếu bạn ánh xạ (1) wordA thành wordB và (2) wordB thành wordC, điều quan trọng là kịch bản sed có ánh xạ (1) trước hoặc sau khi ánh xạ (2).

Tập lệnh được hiển thị không cẩn thận về ranh giới từ; bạn có thể làm cho nó cẩn thận về chúng theo nhiều cách khác nhau, tùy thuộc vào phiên bản của sed bạn đang sử dụng và tiêu chí của bạn cho những gì cấu thành từ.

+0

Ngoài ra còn có vấn đề tiềm năng của một từ trong B là hoàn toàn hoặc một phần trong A. Một giải pháp thực sự có thể sẽ yêu cầu ngắt đầu vào thành các từ và thay đổi chúng một lần nếu có. – lhf

+0

điều này mang lại lỗi: $ paste -d: listA listB | sed 's/\ ([^:] * \): \ ([^:] * \)/s% \ 1% \ 2%'> sed.script sed: -e biểu thứC# 1, char 30: unterminated 's 'lệnh – user248237dfsf

+0

@user, sửa lỗi đó. –

1

Đây là khá đơn giản với Tcl:

set fA [open listA r] 
set fB [open listB r] 
set fin [open input.file r] 
set fout [open output.file w] 

# read listA and listB and create the mapping of corresponding lines 
while {[gets $fA strA] != -1} { 
    set strB [gets $fB] 
    lappend map $strA $strB 
} 

# apply the mapping to the input file 
puts $fout [string map $map [read $fin]] 

# if the file is large, do it line by line instead 
#while {[gets $fin line] != -1} { 
# puts $fout [string map $map $line] 
#} 

close $fA 
close $fB 
close $fin 
close $fout 

file rename output.file input.file 
+0

1 cho việc sử dụng Tcl! –

1

bạn có thể làm điều này trong bash. Đưa danh sách của bạn vào mảng.

listA=(a b c) 
listB=(d e f) 
data=$(<file) 
echo "${data//${listA[2]}/${listB[2]}}" #change the 3rd element. Redirect to file where necessary 
-1

Sử dụng tr (1) (dịch hoặc xóa ký tự):

cat file | tr 'abc' 'XYZ' > file_new 
mv file_new file 
+1

anh ấy muốn thay thế toàn bộ chuỗi không phải là các ký tự riêng lẻ –

2

tôi cần phải làm điều gì đó tương tự, và tôi lên vết thương tạo ra lệnh sed dựa trên một tập tin bản đồ:

$ cat file.map 
abc => 123 
def => 456 
ghi => 789 

$ cat stuff.txt 
abc jdy kdt 
kdb def gbk 
qng pbf ghi 
non non non 
try one abc 

$ sed `cat file.map | awk '{print "-e s/"$1"/"$3"/"}'`<<<"`cat stuff.txt`" 
123 jdy kdt 
kdb 456 gbk 
qng pbf 789 
non non non 
try one 123 

Hãy đảm bảo rằng trình bao của bạn hỗ trợ nhiều tham số cho sed như bạn có trong bản đồ của mình.

+0

đẹp một lớp lót !! – once

+0

Phiên bản 'sed' &' bash' thuần túy: 'sed -f <(sed 's/=> //; s # #/#; s # $ #/#; s #^# s/# 'tệp.map) stuff.txt'. – agc

Các vấn đề liên quan