ทำการร้องขอเพื่อยืนยันก่อนการเปลี่ยนแต่ละครั้งหรือไม่


37

มีวิธีที่จะทำให้ sed ขอให้ฉันยืนยันก่อนที่จะเปลี่ยนแต่ละครั้งหรือไม่ บางสิ่งที่คล้ายกับ 'c' เมื่อใช้แทนที่ส่วนภายในเป็นกลุ่ม

sed ทำเช่นนี้ได้ทั้งหมดหรือไม่?


3
มันจะเป็นไปได้ในทางเทคนิค แต่เป็นการฝึกทางปัญญามากกว่าความพยายามที่เป็นประโยชน์ ดูวิธีเปลี่ยนข้อความในลำดับชั้นของโฟลเดอร์ขนาดใหญ่ได้อย่างไร ซึ่งมีโซลูชั่นที่เป็นกลุ่มและ Perl
Gilles 'หยุดความชั่วร้าย'

ฉันกำลังใช้เสียงเรียกเข้า (args และ argdo) เมื่อใดก็ตามที่ฉันต้อง 'ยืนยัน' แต่สงสัยว่าถ้ามีวิธีที่ 'ง่าย'
Yuvi

1
มันขัดกับวัตถุประสงค์พื้นฐานของ sed - เพื่อทำการแก้ไขอัตโนมัติบนสตรีม
teppic

@teppic ไม่ใช่เพราะคุณยังคงต้องไปหาอินสแตนซ์ในไฟล์ข้อความซึ่งคุณสามารถทำได้ ฉันคิดว่าคำถามเหมาะสมแล้วในกรณีที่คุณเพิ่มไฟล์ผิดในรายการและต้องการดูไฟล์ที่คุณแก้ไข
Kolob Canyon

คำตอบ:


37

การทำด้วย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 ... )ตั้งแต่นั้นเป็นต้นมา

  1. มันจะต้องให้grepเสร็จสิ้นการทำงานก่อนที่จะเริ่มการวนซ้ำครั้งแรกของลูปซึ่งไม่เหมาะสมและ
  2. ชื่อพา ธ ที่ส่งคืนโดยgrepจะถูกแบ่งออกเป็นคำบนช่องว่างและคำเหล่านี้จะได้รับชื่อไฟล์ที่ทำให้เกิดการวนรอบ (สิ่งนี้จะตัดสิทธิ์ชื่อพา ธ ที่มีช่องว่างและอักขระพิเศษ)

ที่เกี่ยวข้อง:


2
ข้อดีsedคือมันสามารถทำงานกับไฟล์หลาย ๆ ไฟล์เช่นsed -i 's/old/new/g' /path/to/*.txtหรือสิ่งที่คล้ายกัน
user1717828

10
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
tcpaiva

@tecepe โปรดแปลงเป็นคำตอบขอบคุณ
Nishant

1
@Nishant มันจะเป็นคำตอบที่บอบบางเพราะมันจะตัดสิทธิ์ไฟล์ใด ๆ ที่มีช่องว่างในชื่อหรือพา ธ ของมัน
Kusalananda

7

คุณสามารถรับสิ่งนี้ได้โดยทำเช่นนี้:

:%s/OLD_TEXT/NEW_TEXT/gc

โดยเฉพาะการเพิ่มcตัวคั่นหลังที่สาม

โปรดทราบว่าตัวเลือก 'c' ใช้งานได้ใน Vim เท่านั้น คุณจะไม่สามารถใช้งานได้sedที่บรรทัดคำสั่ง


4
ชี้แจง: sedนี้จะทำงานเฉพาะในกลุ่มไม่บรรทัดคำสั่งปกติ
เบรนแดน

OP ถูกแท็กด้วยvimดังนั้นคำตอบนี้จึงใช้ได้ แม้ว่าเสียงเรียกเข้าที่ชัดเจนและมีความสามารถที่แตกต่างกัน
ILMostro_7

4

คุณสามารถปล่อยให้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

-1
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- นอกเหนือจากข้างต้นแล้วยังขอให้คุณแทนที่บรรทัดแรกด้วยบรรทัดที่สองด้วย บรรทัด "สำหรับทุกบรรทัด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.