Có sự thay thế nào cho lệnh của sed sed -i trong Solaris không?


11

Tôi có một yêu cầu trong dự án của mình là thay thế một số văn bản hiện có trong một tệp như foovới một số văn bản khác như fooofoo:

abc.txt
name
foo
foo1

Vì vậy, tôi đã cố gắng:

sed -i "s/foo/fooofoo/g" abc.txt

Tuy nhiên tôi nhận được lỗi này:

sed: lựa chọn bất hợp pháp - i

Tôi tìm thấy trong hướng dẫn mà tôi phải sử dụng:

sed -i\ "s/foo/fooofoo/g" abc.txt

Tuy nhiên điều này cũng không hoạt động.

Tôi đã tìm thấy các lựa chọn thay thế trong perlawkcả nhưng một giải pháp trong Solaris sedsẽ được đánh giá cao.

Tôi đang sử dụng phiên bản bash này:

GNU bash, phiên bản 3.2.57 (1) -release (sparc-sun-solaris2.10)


Trên Solaris 11 trở lên, chỉ cần sử dụng /usr/gnu/bin/sedđể nhận hỗ trợ -i.
alanc

Câu trả lời:


15

Sử dụng ed. Nó có sẵn trên hầu hết các nền tảng và nó có thể chỉnh sửa các tệp của bạn tại chỗ.
seddựa trên edcú pháp để thay thế các mẫu là tương tự:

ed -s infile <<\IN
,s/old/new/g
w
q
IN

Tại sao edthay vì ex, chỉ tò mò? (Tôi biết cả hai đều tuân thủ POSIX và tôi đã liên kết với thông số kỹ thuật.))
Wildcard

1
@Wildcard - Tôi có thể hỏi ngược lại - tại sao ex? Để trả lời câu hỏi của bạn: vì tôi không bao giờ sử dụng ex nên tôi không quen với nó. btw, tôi đã thấy các thiết lập có edmặt nhưng exkhông phải (nhưng không phải ngược lại) ... đáng chú ý nhất là máy tính xách tay của tôi :)
don_crissti

Câu trả lời tốt. :) Câu trả lời của tôi: Vì tôi sử dụng Vim mọi lúc, tôi rất quen thuộc với excác lệnh. Tất cả các vilệnh bạn có thể gõ bắt đầu bằng dấu hai chấm là excác lệnh. Tất nhiên có một vài phần mở rộng dành riêng cho Vim, nhưng chỉ bộ lệnh cốt lõi là vô cùng mạnh mẽ và linh hoạt và rất nhiều cho các chỉnh sửa theo kịch bản.
tự đại diện

Hệ thống Manjaro của tôi có exvà chưa ed.
giữa

8

Nếu bạn không thể cài đặt GNU sed, hãy sử dụng:

sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt

Điều này sử dụng chuyển hướng để gửi đầu ra của sed đến một tệp tạm thời. Nếu sed hoàn thành thành công, thì điều này sẽ ghi đè lên abc.txttệp tạm thời.

Như có thể thấy từ mã nguồn cho GNU sed , đây chính xác là những gì sed -i. Vì vậy, đây chỉ là về hiệu quả như sed -i.

Nếu có một cơ hội abc.tmpđã tồn tại, thì bạn có thể muốn sử dụng mktemphoặc một tiện ích tương tự để tạo tên duy nhất cho tạm thời.


Cảm ơn vì sự giúp đỡ. Tuy nhiên, có tùy chọn nào trong solaris mà chúng ta có thể cập nhật tệp hiện có mà không cần tạo tệp tạm thời không?
tpsaitwal

10
Ghét để phá vỡ nó cho bạn, nhưng ngay cả sed tạo ra một tập tin tạm thời. git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c#n84
Jeff Schaller

1
Bạn có thể làm điều đó nếu bạn không quan tâm đến các liên kết cứng - xem ví dụ của tôi. Vẫn còn một tập tin tạm thời, nhưng nó bị ẩn (chưa được đặt tên). Nếu không có tạm thời, bạn cần sử dụng ed, vì nó đọc toàn bộ tệp vào bộ nhớ.
Toby Speight

3

Nếu bạn muốn tương đương sed -i.bak, nó khá đơn giản.

Xem xét tập lệnh này cho GNU sed:

#!/bin/sh

# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"

# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'

##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########

# Prove that it worked
ls "$dir"
cat "$dir/foo"

Chúng ta chỉ cần thay thế dòng được đánh dấu bằng

cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"

Thao tác này sẽ chuyển tệp hiện có thành bản sao lưu và ghi tệp mới.

Nếu chúng ta muốn tương đương với

sed -i -e "$script" "$dir"  # no backup

sau đó nó phức tạp hơn một chút. Chúng ta có thể mở tệp để đọc dưới dạng đầu vào tiêu chuẩn, sau đó hủy liên kết tệp đó, trước khi chỉ đạo đầu ra của sed để thay thế nó:

( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )

Chúng tôi làm điều này trong một vỏ phụ, để stdin ban đầu của chúng tôi vẫn có sẵn sau này. Có thể chuyển đổi đầu vào và chuyển trở lại mà không cần một mạng con, nhưng cách này có vẻ rõ ràng hơn đối với tôi.

Lưu ý rằng chúng tôi cẩn thận sao chép trước, thay vì tạo footệp mới - điều này rất quan trọng nếu tệp được biết đến bởi nhiều tên (nghĩa là có liên kết cứng) và bạn muốn chắc chắn rằng mình không phá vỡ liên kết .


3

Trước tiên, bạn nên biết rằng i\lệnh mà bạn đang đề cập là để chèn một dòng văn bản, chú thích để lưu văn bản đã chỉnh sửa trở lại tệp. Không có cách POSIX chỉ định sử dụng sedcho điều đó.

Những gì bạn có thể làm là sử dụng ex, được chỉ định bởi POSIX :

printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt

Các xlệnh là dành cho "save và thoát." Bạn cũng có thể sử dụng wqnhưng xngắn hơn.

Các %dấu hiệu khi bắt đầu các phương tiện lệnh thay thế, "Áp dụng lệnh này để mỗi dòng trong bộ đệm." Bạn cũng có thể sử dụng 1,$s/find/replace/g.


Một sự khác biệt chính giữa exsedđó sedlà một trình soạn thảo luồng . Nó chỉ hoạt động tuần tự, từng dòng. exlinh hoạt hơn nhiều so với điều đó và trên thực tế bạn có thể thực hiện chỉnh sửa tệp văn bản tương tác trực tiếp ex. Nó là tiền thân ngay lập tức vi.


1

Sử dụng sedvà không có tệp tạm thời hiển thị :

Bạn có thể tránh tạo một "tệp tạm thời" hiển thị riêng biệt :

exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-

Giải trình

Các hệ thống giống như Unix không thực sự xóa nội dung của tệp khỏi đĩa cho đến khi cả hai không được liên kết trong hệ thống tệp không mở trong bất kỳ quy trình nào. Vì vậy, bạn có thể làm exec 3<để mở tệp trong trình bao để đọc trên tệp mô tả tệp 3, rmtệp (sẽ hủy liên kết tệp khỏi hệ thống tệp), sau đó gọi sedvới mô tả tệp 3 được sử dụng làm đầu vào của tệp.

Lưu ý rằng điều này rất khác với điều này:

# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt

Sự khác biệt là khi bạn thực hiện nó trong một lệnh, shell chỉ mở cùng một tệp cho cả hai lần đọc và để viết với tùy chọn cắt bớt tệp - vì nó vẫn là cùng một tệp, bạn sẽ mất nội dung. Nhưng nếu bạn mở nó để đọc, thì rmnó, sau đó mở cùng một tên đường dẫn để viết, bạn thực sự đang tạo một tệp mới ở cùng một tên đường dẫn (nhưng tại một vị trí inode và đĩa mới, vì bản gốc vẫn đang mở): vì vậy các nội dung vẫn có sẵn.

Sau đó, khi bạn đã hoàn tất, bạn có thể đóng bộ mô tả tệp bạn đã mở trước đó (đó là những gì exec 3<&-cú pháp đặc biệt làm), nó giải phóng tệp gốc để hệ điều hành có thể xóa (đánh dấu là không sử dụng) không gian đĩa của nó.

Hãy cẩn thận

Có một vài điều cần lưu ý về giải pháp này :.

  1. Bạn chỉ nhận được một lần "đi" qua nội dung - không có cách di động nào để trình bao "tìm kiếm" lại trong phần mô tả tệp - vì vậy một khi chương trình đọc một số nội dung, các chương trình khác sẽ chỉ thấy phần còn lại của tệp. Và sedsẽ đọc toàn bộ tập tin.

  2. Có một khả năng nhỏ là tệp gốc của bạn bị mất nếu shell / script / sed của bạn bị giết trước khi hoàn thành.


Để rõ ràng, @TobySpeight đã đăng một ví dụ về giải pháp này vào cuối câu trả lời của anh ấy, nhưng tôi nghĩ rằng nó có thể sử dụng một công phu sâu hơn.
mtraceur

Đối với người dùng downvoter: Mặc dù tôi chắc chắn rằng việc để lại một downvote khiến bạn cảm thấy tốt về bản thân mình, tôi đánh giá cao nếu bạn đóng góp nhiều hơn bằng cách cung cấp phản hồi về những vấn đề bạn gặp phải với câu trả lời này, làm thế nào để cải thiện nó, v.v. .
mtraceur

có lẽ bạn có thể sử dụng perl -i
c4f4t0r

@ c4f4t0r: Xin lỗi vì đã trả lời trễ: Có, perl -ilà một lựa chọn tốt khác. Câu hỏi đặc biệt yêu cầu một giải pháp tương thích với Solaris ' sed, trái ngược với các giải pháp perlawkhọ đã đề cập đã tìm thấy. Thêm vào đó, câu trả lời của tôi chủ yếu là để thêm một cái gì đó mà tôi nghĩ là hữu ích để biết và thiếu từ bất kỳ câu trả lời nào khác.
mtraceur
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.