มีวิธีที่จะทำให้ sed ขอให้ฉันยืนยันก่อนที่จะเปลี่ยนแต่ละครั้งหรือไม่ บางสิ่งที่คล้ายกับ 'c' เมื่อใช้แทนที่ส่วนภายในเป็นกลุ่ม
sed ทำเช่นนี้ได้ทั้งหมดหรือไม่?
มีวิธีที่จะทำให้ sed ขอให้ฉันยืนยันก่อนที่จะเปลี่ยนแต่ละครั้งหรือไม่ บางสิ่งที่คล้ายกับ 'c' เมื่อใช้แทนที่ส่วนภายในเป็นกลุ่ม
sed ทำเช่นนี้ได้ทั้งหมดหรือไม่?
คำตอบ:
การทำด้วยsed
อาจจะไม่สามารถทำได้เนื่องจากเป็นโปรแกรมแก้ไขสตรีมแบบไม่โต้ตอบ การห่อsed
สคริปต์จะต้องใช้ความคิดมากเกินไป มันง่ายกว่าที่จะทำกับvim
:
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' file.in
เนื่องจากมีการกล่าวถึงในความคิดเห็นด้านล่างนี่คือวิธีที่จะใช้กับไฟล์หลายไฟล์ที่ตรงกับรูปแบบชื่อไฟล์เฉพาะที่กำลังวนรอบในไดเรกทอรีปัจจุบัน:
for fname in file*.txt; do
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
หรือถ้าคุณต้องการให้แน่ใจก่อนว่าไฟล์มีบรรทัดที่ตรงกับรูปแบบที่กำหนดก่อนจริง ๆ ก่อนที่จะทำการทดแทน
for fname in file*.txt; do
grep -q 'PATTERN' "$fname" &&
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
เชลล์สองวงด้านบนถูกปรับเปลี่ยนเป็นfind
คำสั่งที่ทำสิ่งเดียวกัน แต่สำหรับไฟล์ทั้งหมดที่มีชื่อเฉพาะบางแห่งในหรือภายใต้top-dir
ไดเรกทอรีบางตัว
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' {} \;
หรือใช้เชลล์แบบดั้งเดิมและวนรอบfind
ชื่อฟีดลงในนั้น:
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 {} +
ไม่ว่าในกรณีใดคุณไม่ต้องการทำอะไรfor filename in $( grep -rl ... )
ตั้งแต่นั้นเป็นต้นมา
grep
เสร็จสิ้นการทำงานก่อนที่จะเริ่มการวนซ้ำครั้งแรกของลูปซึ่งไม่เหมาะสมและgrep
จะถูกแบ่งออกเป็นคำบนช่องว่างและคำเหล่านี้จะได้รับชื่อไฟล์ที่ทำให้เกิดการวนรอบ (สิ่งนี้จะตัดสิทธิ์ชื่อพา ธ ที่มีช่องว่างและอักขระพิเศษ)ที่เกี่ยวข้อง:
sed
คือมันสามารถทำงานกับไฟล์หลาย ๆ ไฟล์เช่นsed -i 's/old/new/g' /path/to/*.txt
หรือสิ่งที่คล้ายกัน
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
คุณสามารถรับสิ่งนี้ได้โดยทำเช่นนี้:
:%s/OLD_TEXT/NEW_TEXT/gc
โดยเฉพาะการเพิ่มc
ตัวคั่นหลังที่สาม
โปรดทราบว่าตัวเลือก 'c' ใช้งานได้ใน Vim เท่านั้น คุณจะไม่สามารถใช้งานได้sed
ที่บรรทัดคำสั่ง
sed
นี้จะทำงานเฉพาะในกลุ่มไม่บรรทัดคำสั่งปกติ
vim
ดังนั้นคำตอบนี้จึงใช้ได้ แม้ว่าเสียงเรียกเข้าที่ชัดเจนและมีความสามารถที่แตกต่างกัน
คุณสามารถปล่อยให้sed
ทำสิ่งนั้นลงบนไฟล์แล้วบันทึกผลลัพธ์ลงในไฟล์ชั่วคราวซึ่งคุณสามารถแพทช์แบบโต้ตอบได้ในไฟล์ต้นฉบับโดยใช้sdiff
(ดูhttp://www.gnu.org/software/diffutils/manual/diffutils.html # Invoke-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
}
หากคุณป้อนด้วยอาร์กิวเมนต์เดียวsearchandreplace "AAA"
- จะค้นหาเฉพาะสตริงนั้นในไดเรกทอรีปัจจุบัน แต่ถ้าคุณป้อนด้วยอาร์กิวเมนต์สองตัว - searchandreplace AAA BBB
- นอกเหนือจากข้างต้นแล้วยังขอให้คุณแทนที่บรรทัดแรกด้วยบรรทัดที่สองด้วย บรรทัด "สำหรับทุกบรรทัด