Thay thế một số chuỗi bằng chuỗi khác trong một số tệp


7

Tôi có một số tập tin văn bản. Tất cả các tệp văn bản này cần phải trải qua một bộ để chỉnh sửa có thể được thực hiện vim. Tôi muốn tự động hóa điều này. vimcó một số lệnh để thay thế. Giả sử các tệp văn bản cần phải trải qua các thay thế sau:

  1. thay thế cậu bé bằng cậu bé: %s/boy/Boy/g
  2. thay thế cô gái bằng cô gái: %s/girl/Girl/g
  3. xóa các dòng trống: g/^$/d

Đây chỉ là một ví dụ đơn giản, Có cách nào để viết tất cả các quy tắc này và sau đó tự động hóa quy tắc này trên một số tệp không?


Tôi đã thay đổi câu hỏi của bạn từ vi sang vim vì tôi cho rằng đó là những gì bạn đang sử dụng. Vui lòng xác nhận điều này.
slm

@braiam - Thế còn việc sử dụng vim?
slm

@slm Tôi cho rằng thẻ là đủ ... tiêu đề ban đầu không phản ánh nhiệm vụ trong tay là gì và thực sự đã gây hiểu nhầm.
Braiam

Câu trả lời:


14

Có một cách để tự động hóa điều này. Và nó bắt đầu với việc chọn đúng công cụ, cho công việc.
Trong trường hợp này, bạn nên sử dụng ví dụ sedvà không cố gắng uốn cong viđược thiết kế để sử dụng tương tác (và không phải để tự động hóa).

Cú pháp thay thế cho sedphần lớn giống như cú pháp cho vi.

 sed -i.backup 's/boy/Boy/g' file-name-1 file-name-2 ...

2
Rõ ràng có tiếng vang ở đây, 4 người khác cảm thấy muốn nói chính xác những gì bạn đã làm 8-)
slm

3
sedlà một trình soạn thảo luồng , vilà một trình soạn thảo tệp văn bản trực quan , được xây dựng trên đầu / (và lệnh là một lệnh trong ). Hầu hết các triển khai không có a . và là các lệnh Unix tiêu chuẩn. Vì vậy, có, nó có thể được thực hiện với một số triển khai sed, nhưng điều đó không có nghĩa là nó là công cụ phù hợp . exedsexvised-iexed
Stéphane Chazelas

3
Tôi đã không viết sedlà "công cụ phù hợp". Có nhiều công cụ, do đó "vd". Theo như tôi nhớ edthì không hoạt động trên nhiều tệp và bạn sẽ phải cung cấp các thao tác đọc và ghi trên stdin của nó. Tuy nhiên lần cuối cùng tôi có thể nhớ tôi edđã sử dụng là vì vikhông có sẵn trên PDP-11 vào giữa những năm 80, vì vậy tôi có thể đã hết thời với khả năng của nó
Anthon

12

Kịch bản Vim (gvim, vim) có thể thanh lịch và rất dễ thích nghi

vi -s edit.vim  test.txt

nơi edit.vim chứa (the: wq là tùy chọn)

:%s/boy/Boy/g
:%s/girl/Girl/g
:g/^$/d
:wq

nơi test.txt chứa

boys & girls

boys & girls
boy & girl

boys & girls

đây là tập lệnh vim chung để dọn sạch tệp văn bản "mucky"

:" clean.vim
:" clean up a text file
:" delete DOS returns (actually superfluous because of non-ASCII
deletion)
:%s/\r//eg
:" delete any non-ASCII including invisibles
:%s/[\x00-\x1f\x80-\xff]/ /eg
:" compress multiple spaces
:%s/\s\s\+/ /eg
:" delete end of line spaces
:%s/\s\+$//e
:" compress multiple blank lines
:v/./,/./-j
:" sort eliminating duplicate lines
:%sort -u

Hãy nhớ rằng bạn cũng có thể nguồn kịch bản từ trong vim

:source clean.vim

3

Tại sao không sử dụng sed?

Với sed, bạn có thể dễ dàng lặp lại các tập tin trong thư mục:

for f in *.txt
do
sed 's/boy/Boy/g;s/girl/Girl/g;/^\s*$/d' $f > tmp
mv tmp $f
done

Ví dụ trên sẽ thay đổi boy-> Boy, girl-> Girl và xóa các dòng trống.


2
Bạn không cần thực hiện vòng lặp for, cung cấp cho sed một danh sách các tệp trực tiếp như trong câu trả lời của Anthon bên dưới hoặc toàn cầu, * .txt trực tiếp. Ngoài ra mvlà không cần thiết. Sử dụng sed's -i.extswitch để tạo ra một bản sao lưu.
slm

1
Tôi không biết mẹo đó tôi sẽ cố nhớ nó vào lần sau. Cảm ơn vì lời khuyên và lời giải thích @slm
janrpn

2

Và một vitập lệnh nội tuyến :

$ vi test.txt -c '%s/boy/Boy/g | %s/girl/Girl/g | g/^$/d | wq'

2

LƯU Ý: Sử dụng sedlà cách thích hợp để thực hiện việc này, như được hiển thị bởi câu trả lời của @ Anthon !


Chỉ để cho bạn thấy làm thế nào bạn có thể làm điều này trong vim. Bạn có thể sử dụng argdolệnh trong vim. Từ :help argdotrong vim:

:argdo[!] {cmd}         Execute {cmd} for each file in the argument list.
Thí dụ:
    :args *.c
    :argdo set ff=unix | update

Điều này đặt tùy chọn 'fileformat' thành "unix" và ghi tệp nếu nó đã được thay đổi. Điều này được thực hiện cho tất cả *.ccác tập tin.

Thí dụ:
    :args *.[ch]
    :argdo %s/\<my_foo\>/My_Foo/ge | update

Điều này thay đổi từ "my_foo" thành "My_Foo" trong tất cả *.c*.hcác tệp. Cờ "e" được sử dụng cho :substitutelệnh để tránh lỗi cho các tệp trong đó "my_foo" không được sử dụng. :updatechỉ ghi tập tin nếu thay đổi được thực hiện.

Lệnh :args <pattern>cho biết :argdotập tin nào bạn muốn chạy sau đây {cmd}. Sau khi xác định bạn chạy :argdo {cmd} | updatenơi {cmd}có thể là sự s/boy/Boy/gthay thế của bạn .

Có phải tất cả các tập tin được mở trong hoạt động này?

Nó sẽ xuất hiện như vậy. Tôi đã làm các xét nghiệm sau đây để xác nhận điều này.

$ for i in {1..3};do echo "dog" > file${i}.txt;done

$ head file*.txt
==> file1.txt <==
dog

==> file2.txt <==
dog

==> file3.txt <==
dog

Bây giờ đi vào vimvà làm các lệnh sau:

:args file*.txt

Bạn có thể xác nhận rằng tất cả chúng đã được mở:

:ls
  2 %a   "file1.txt"                    line 1
  3      "file2.txt"                    line 0
  4      "file3.txt"                    line 0

Làm :argdo ...:

:argdo %s/dog/cat/g | update
"file1.txt" 1L, 4C written
"file2.txt" 1L, 4C written
"file3.txt" 1L, 4C written

Vì vậy, hãy ghi nhớ điều này nếu bạn có ý định sử dụng phương pháp này. Nó không hiệu quả mở tất cả các tệp phù hợp với mẫu của bạn :argsvà lần lượt áp dụng lệnh cho chúng.


0

sedlà công cụ cho bạn. Hầu hết các lệnh mà bạn sẽ sử dụng visẽ có sẵn sedvà do đó, không có nhiều đường cong học tập. Các lệnh trong viđó được bắt đầu bằng một :dựa trên trình soạn thảo cơ bản edvà chúng cũng có sẵn trong sed. :Theo mặc định, bạn sẽ sử dụng các lệnh đó mà không có và chúng được áp dụng cho toàn bộ tệp. Ví dụ, ví dụ của bạn, bạn sẽ làm

sed -i 's/boy/Boy/g
s/girl/Girl/g
/^$/d' file1 file2 ...

0

Giả sử bạn vithực sự vimhoặc gvim, nó chắc chắn có thể được thực hiện trong vimscript, nhưng có thể phức tạp khi làm việc trên nhiều tệp cùng một lúc.

Tốt hơn là công cụ phù hợp cho công việc : sed, trình soạn thảo luồng, sử dụng cú pháp rất giống như vi, nhưng được dự định để làm việc trên các tệp dưới dạng dòng theo dòng, không có tương tác.

Với tùy chọn -i, sedcó thể thay đổi trong nhiều tệp. -i.backup moves an original filefoo.txt tofoo.txt.backup , and applies changes in place tofoo.txt`:

sed -i.backup -e 's/boy/Boy/g' -e 's/girl/Girl/g' -e '/^$/d' f1.txt f2.txt ...

Bạn cũng có thể chạy cái này bên trong vivới :!:

:!sed -i.backup -e 's/boy/Boy/g' -e 's/girl/Girl/g' -e '/^$/d' f1.txt f2.txt ...

0

Tôi thứ hai @ câu trả lời của Anthon . Sử dụng sed:

$ sed -e "s/boy/Boy/g" -e "s/girl/Girl/g" -e '/^\s*$/d' file

LƯU Ý: (Điều này cũng xóa các dòng chỉ có khoảng trắng)

Thêm -ivào sednếu bạn muốn thay thế tại chỗ (các tệp của bạn nằm dưới sự kiểm soát phiên bản, phải không?). Đối với nhiều tệp:

$ for $f in $(ls mydir); do
   sed -e "s/boy/Boy/g" -e "s/girl/Girl/g" -e '/^\s*$/d' $f
done

0

Lựa chọn thay thế

Trừ khi bạn thực sự cần khả năng Vim đặc biệt, bạn có lẽ tốt hơn bằng cách sử dụng các công cụ không tương tác như sed, awkhoặc Perl / Python / Ruby / ngôn ngữ kịch bản yêu thích của bạn ở đây .

Điều đó nói rằng, bạn có thể sử dụng Vim không tương tác:

Chế độ hàng loạt im lặng

Để xử lý văn bản rất đơn giản (nghĩa là sử dụng Vim như 'sed' hoặc 'awk' được tăng cường, có thể chỉ được hưởng lợi từ các biểu thức chính quy nâng cao trong một :substitutelệnh), hãy sử dụng chế độ Ex .

REM Windows
call vim -N -u NONE -n -es -S "commands.ex" "filespec"

Lưu ý: chế độ hàng loạt im lặng ( :help -s-ex) làm rối giao diện điều khiển Windows, do đó bạn có thể phải thực hiện clsviệc dọn dẹp sau khi chạy Vim.

# Unix
vim -T dumb --noplugin -n -es -S "commands.ex" "filespec"

Chú ý: Vim sẽ chờ đợi đầu vào nếu "commands.ex"tệp không tồn tại; kiểm tra tốt hơn trước sự tồn tại của nó! Ngoài ra, Vim có thể đọc các lệnh từ stdin. Bạn cũng có thể điền vào một bộ đệm mới với văn bản đọc từ stdin và đọc các lệnh từ stderr nếu bạn sử dụng -đối số.

Tự động hóa hoàn toàn

Để xử lý nâng cao hơn liên quan đến nhiều cửa sổ và tự động hóa thực sự của Vim (nơi bạn có thể tương tác với người dùng hoặc để Vim chạy để cho phép người dùng tiếp quản), hãy sử dụng:

vim -N -u NONE -n -c "set nomore" -S "commands.vim" "filespec"

Dưới đây là tóm tắt về các đối số được sử dụng:

-T dumb           Avoids errors in case the terminal detection goes wrong.
-N -u NONE        Do not load vimrc and plugins, alternatively:
--noplugin        Do not load plugins.
-n                No swapfile.
-es               Ex mode + silent batch mode -s-ex
                Attention: Must be given in that order!
-S ...            Source script.
-c 'set nomore'   Suppress the more-prompt when the screen is filled
                with messages or output to avoid blocking.
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.