Có cách nào để làm cho sed yêu cầu tôi xác nhận trước mỗi lần thay thế không? Một cái gì đó tương tự như 'c' khi sử dụng thay thế bên trong vim.
Liệu sed có làm điều này không?
Có cách nào để làm cho sed yêu cầu tôi xác nhận trước mỗi lần thay thế không? Một cái gì đó tương tự như 'c' khi sử dụng thay thế bên trong vim.
Liệu sed có làm điều này không?
Câu trả lời:
Làm điều đó với sed
có lẽ sẽ không thể vì nó là một trình chỉnh sửa luồng không tương tác. Gói gọn sed
trong một kịch bản sẽ đòi hỏi quá nhiều suy nghĩ. Nó dễ dàng hơn để làm điều đó với vim
:
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' file.in
Vì nó đã được đề cập trong các bình luận bên dưới, đây là cách nó sẽ được sử dụng trên nhiều tệp khớp với một mẫu tên tập tin cụ thể trong thư mục hiện tại:
for fname in file*.txt; do
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
Hoặc, nếu trước tiên bạn muốn đảm bảo rằng tệp thực sự chứa một dòng khớp với mẫu đã cho trước, trước khi thực hiện thay thế,
for fname in file*.txt; do
grep -q 'PATTERN' "$fname" &&
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
Hai vòng lặp shell ở trên được sửa đổi thành find
các lệnh thực hiện cùng một thứ nhưng đối với tất cả các tệp có tên cụ thể ở đâu đó trong hoặc dưới một số top-dir
thư mục,
find top-dir -type f -name 'file*.txt' \
-exec vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' {} \;
find top-dir -type f -name 'file*.txt' \
-exec grep -q 'PATTERN' {} \; \
-exec vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' {} \;
Hoặc, sử dụng các vòng lặp shell ban đầu và có find
tên đường dẫn nguồn cấp dữ liệu vào chúng:
find top-dir -type f -name 'file*.txt' -exec sh -c '
for pathname do
vim -c "%s/PATTERN/REPLACEMENT/gc" -c "wq" "$pathname"
done' sh {} +
find top-dir -type f -name 'file*.txt' -exec sh -c '
for pathname do
grep -q "PATTERN" "$pathname" &&
vim -c "%s/PATTERN/REPLACEMENT/gc" -c "wq" "$pathname"
done' sh {} +
Bạn làm gì, trong mọi trường hợp, không muốn làm một cái gì đó giống như for filename in $( grep -rl ... )
từ
grep
kết thúc chạy trước cả khi bắt đầu vòng lặp đầu tiên, không liên tục vàgrep
sẽ được chia thành các từ trên khoảng trắng và những từ này sẽ trải qua tên tập tin toàn cầu (điều này không đủ tiêu chuẩn tên đường dẫn chứa khoảng trắng và ký tự đặc biệt).Liên quan:
sed
là nó có thể hoạt động trên nhiều tệp, ví dụ, sed -i 's/old/new/g' /path/to/*.txt
hoặc một cái gì đó tương tự.
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
Bạn có thể có được điều này bằng cách làm như vậy:
:%s/OLD_TEXT/NEW_TEXT/gc
Cụ thể, thêm c
dấu phân cách thứ ba.
Lưu ý rằng tùy chọn 'c' chỉ hoạt động trong Vim; bạn sẽ không thể sử dụng nó với sed
dòng lệnh.
sed
.
vim
, vì vậy câu trả lời này được áp dụng; mặc dù, rõ ràng vim và sed có những khả năng khác nhau
Bạn có thể để sed
thực hiện điều đó trên tệp và sau đó lưu kết quả vào một tệp tạm thời mà sau đó bạn có thể tương tác vá vào tệp gốc bằng cách sử dụng sdiff
(xem http://www.gnu.org/software/diffutils/manual/diffutils.html # Gọi-sdiff ):
sed -r 's/something/something_else/g' my_file > tmp_file
sdiff -o my_file -s -d my_file tmp_file
searchandreplace () {
if [ -z $2 ] ; then
realpath * | xargs grep -ins --directories=skip --color=always "$1" | nl -s "."
else
realpath * | xargs grep -ins --directories=skip --color=always "$1" | nl -s "."
exp=$(realpath * | xargs grep -ins --directories=skip --color=never "$1" | cut -d':' -f1,2 | awk -F':' '{print $2 $1}' | sed -E "s|^[0-9]+|sed -i \'&s/|; s|//|/"$1"/"$2"/g\' /|")
IFS=$'\n'
for d in $exp
do
read -p "Exec $(echo -e -n "\033[1;31m$d\033[0m")? " -e REP
if [ "$REP" = y ]; then
echo `bash -c $d`;
fi
done
fi
}
Nếu bạn cung cấp searchandreplace "AAA"
chuỗi này với một đối số duy nhất - - nó chỉ tìm kiếm chuỗi đó trong thư mục hiện tại, nhưng nếu bạn cung cấp chuỗi đó bằng các đối số kép searchandreplace AAA BBB
- thì ngoài các đối số trên, nó cũng yêu cầu bạn thay thế dòng đầu tiên bằng dòng thứ hai dòng "cho mỗi dòng.