มีทางเลือกอื่นใดสำหรับสวิตช์ -A -B -C grep ของ (เพื่อพิมพ์สองสามบรรทัดก่อนและหลัง)?


10
grep -A 2 -B 3 

พิมพ์ 2 บรรทัดหลังสตริง grep และพิมพ์ 3 บรรทัดก่อนหน้า

grep -C 3

พิมพ์ 3 บรรทัดก่อนหน้าและ 3 บรรทัดหลัง

น่าเสียดายที่grepฉันใช้ไม่รองรับตัวเลือกเหล่านี้ มีคำสั่งหรือสคริปต์ทางเลือกอื่นที่สามารถจำลองได้หรือไม่? ใช้sed/ awk/ perl/ เชลล์สคริปต์หรือไม่


+1 ฉันไม่รู้ว่ามี-Cสวิตช์
Lazer

1
ติดตั้ง grep ของ GNU โดยทั่วไปเมื่อเครื่อง Sun GNU > /usr/localใหม่มาถึงที่นี่เป็นขั้นตอนแรกในการติดตั้งคือสิ่งที่ใครบางคนเรียกว่า โปรแกรม GNU นั้นมีส่วนขยายที่มีประโยชน์มากมายและได้รับการออกแบบมาเพื่อหลีกเลี่ยงข้อ จำกัด โดยพลการ ระบบ propietary หลายแห่งมีที่เก็บแบบไม่เป็นทางการกับ GNU และเครื่องมืออื่น ๆ "พันธมิตร" จะไม่บอกคุณเกี่ยวกับพวกเขาแม้ว่าพวกเขาจะได้รับการจัดการโดยผู้ขาย ...
vonbrand

คำตอบ:


6

วิธีหนึ่งที่น่าเกลียดในระดับปานกลาง

grep -v pattern file >file.tmp; diff -c file.tmp file

หรือแทนที่-cด้วย-C NUMสำหรับNUMบรรทัดของบริบท มันจะสร้างผลผลิตเพิ่มขึ้น (หากการdiffสนับสนุนของคุณ-u/ -U NUMมันจะสะอาดขึ้น)

หากคุณdiffไม่มี-c/ -C/ -uยังมีวิธีการทำอยู่ แต่มันก็น่าเกลียดอยู่ดี ในทางกลับกันระบบที่diffไม่รองรับแม้แต่-cจะไม่มี Perl เช่นกัน


นี่มันเจ๋งสุด ๆ ใช้งานได้อย่างมีเสน่ห์แม้ว่าฉันจะต้องใช้ตัวเลือก - bitw กับมันเพื่อให้มันใช้ได้กับไฟล์ที่สร้างจาก windows
Prashant Bhate

คุณสามารถส่ง stdin เพื่อ diff และข้ามชั่วคราว:grep -v pattern file | diff -c - file
Cascabel

5

แอ๊ต้องใช้เพียง Perl และรวมถึง-A, -Bและ-Cตัวเลือกที่ทำงานเหมือนของ grep มันใช้ไวยากรณ์ regex ของ Perl แทน grep และวิธีการเลือกไฟล์เพื่อค้นหานั้นค่อนข้างแตกต่างกัน คุณอาจต้องการลองใช้-fตัวเลือกเมื่อใช้งาน (ซึ่งพิมพ์ไฟล์ที่จะค้นหาโดยไม่ต้องค้นหาอะไรจริง ๆ )

สามารถติดตั้งเป็นสคริปต์เดียวที่ไม่ต้องใช้โมดูลที่ไม่ใช่คอร์ เพียงแค่วางลงใน~/binไดเรกทอรีของคุณ(หรือที่ใดก็ได้บนเส้นทางของคุณที่คุณมีการเขียนการเข้าถึง) และตรวจสอบให้แน่ใจว่ามันเป็นchmodปฏิบัติการ


กล่องผลิตและน่าเสียดายที่ฉันไม่มีสิทธิ์เพียงพอที่จะติดตั้งอะไรและฉันไม่สามารถเสี่ยงได้ แต่ขอบคุณสำหรับเคล็ดลับนี้ฉันจะติดตั้งและลองใช้แล็ปท็อปที่บ้านของฉัน
Prashant Bhate

@ Prashant คุณไม่จำเป็นต้องรูทเพื่อติดตั้งackเพื่อการใช้งานของคุณเอง
cjm

ใช่ แต่ฉันก็ยังใช้ไม่ได้ที่นั่นถึงแม้ว่ามันจะแน่ใจว่าสคริปต์นี้จะอยู่ตลอดไปใน ~ / bin :) ของฉัน
Prashant Bhate

@ Prashant: ทำไมคุณไม่สามารถใช้มันได้? มันเป็นเพียงสคริปต์ Perl
intuited

1
มันเป็นกล่องผลิตจำเป็นต้องได้รับอนุญาตพิเศษการอนุมัติ bla bla bla ... เพื่อทำสิ่งใด ๆ และมีอะไรผิดพลาดมีบนมาบนหัวของฉัน) และมันไม่คุ้มค่า :)
Prashant Bhate

5

สคริปต์ Perl ง่าย ๆ นี้เลียนแบบgrep -Aในระดับหนึ่ง

#!/usr/bin/perl

$pattern=shift; #patthern to search
$lines=shift; # number of lines to print

$n = 0;
while (<>) {
  $n = $lines if /$pattern/; # reset counting
  if ($n) { print; $n-- } # print if within
  $n = 0 if eof; # don't leak across file boundaries
}

โปรดทราบว่าคุณสามารถเพิ่มคำสั่งการใช้งานเพื่อให้สคริปต์อ่านและใช้งานได้;)

USAGE:    $./grep-A.pl <pattern> <numLines> <filename> 

ดีฉันต้องเปิดใช้ Perl รุ่นใด
Prashant Bhate

ฉันใช้ v5.10.1 ฉันเดาว่า perl 5 เป็นเรื่องธรรมดาในทุกวันนี้
วีเจย์อนันต์

ยาของมัน 5.8.8 และใช้งานได้ดี แต่ฉันต้องการสคริปต์ที่ทำในสิ่งที่ -B ทำ
Prashant Bhate

ดี. ฉันจะเปลี่ยนลำดับของการขัดแย้งแม้ว่า; ดูธรรมชาติมากขึ้นกว่าgrep-A 3 foo grep-A foo 3:-)
musiphil

3

คุณสามารถติดตั้งGNU grepหรือAck (เขียนด้วย Perl ทำความเข้าใจกับตัวเลือก grep ของ GNU และอีกมากมาย)

หากคุณต้องการยึดติดกับเครื่องมือมาตรฐานบวกกับการเขียนสคริปต์เล็กน้อยนี่คือสคริปต์awkที่เลียนแบบพฤติกรรมของ grep -Aและ-Bตัวเลือกของ GNU ทดสอบขั้นต่ำ

#!/bin/sh
# grep-ac: a grep-like awk script
# Arguments: pattern = awk regexp to search for
#            before = number of lines to print before a match
#            after = number of lines to print after a match
{ "exec" "awk" "-f" "$0" "$@"; }
# The array h contains the history of lines that haven't been printed
# but are eligible for being "before" lines.
# The variable until contains the number of the last "after" line to print.
match($0, pattern) {   # the current line matches
    for (i in h) {
        print h[i];    # print each remaining before line
        delete h[i];   # delete each line as it's printed
    }
    until=NR+after;    # record the last after line to print
}
{
    if (NR<=until) print $0;    # from a match to its last after line: print
    else h[NR]=$0;              # after that: save in history
    delete h[NR-before];        # remove line too old to be a before line
}
END {exit !until}               # exit status: 0 if there was a match, else 1

เรียกใช้เป็นgrep-ac -vpattern=PATTERN -vbefore=NBEFORE -vafter=NAFTERตำแหน่งที่PATTERNรูปแบบการค้นหา ( นิพจน์ปกติที่ขยายด้วยการเพิ่ม awkเล็กน้อย) NBEFOREและNAFTERเป็นจำนวนบรรทัดที่จะพิมพ์ก่อนและหลังการจับคู่ตามลำดับ (ค่าเริ่มต้นเป็น 0) ตัวอย่าง:

<input_file grep-ac -vbefore=2 -vpattern='foo *bar'

วิธีแก้ปัญหาใด ๆ ที่เก็บข้อมูลในอาเรย์นั้นเป็นปัญหา ... ตามที่ฉันได้กล่าวไปแล้วขนาดไฟล์มีขนาดใหญ่มากและอาจไหลเกิน นอกจากนี้ awk บนระบบนี้ไม่อนุญาตให้มีขนาดไฟล์มากกว่า 3000 ไบต์
Prashant Bhate

2
@Prashant: ฉันไม่เข้าใจการคัดค้านของคุณ สคริปต์นี้จะลบบรรทัดเมื่อไม่มีสิทธิ์เป็นบรรทัดก่อนหน้า มันไม่ได้ใช้หน่วยความจำมากเกินความจำเป็นอย่างแท้จริงเนื่องจากข้อกำหนดนั้นยกเว้นว่า awk อาจมีค่าใช้จ่ายสูงกว่าโปรแกรมวัตถุประสงค์พิเศษ (แต่น้อยกว่า Perl ซึ่งคุณพิจารณาด้วย) ขนาดทั้งหมดของไฟล์ไม่เกี่ยวข้องอย่างสมบูรณ์
Gilles 'หยุดความชั่วร้าย'

2
{ "exec" "awk" "-f" "$0" "$@"; }: วิธีที่ดีมากในการหลีกเลี่ยงข้อ จำกัด ในการแยกวิเคราะห์แบบ Shebang
dubiousjim

2

ปรากฎว่ามันค่อนข้างยากที่จะเลียนแบบ -B เนื่องจากปัญหาที่ครอบตัดเมื่อคุณมีเส้นที่ตรงกันซึ่งกันและกันโดยตรง การทำเช่นนี้ค่อนข้างไม่อนุญาตให้ใช้ไฟล์สแกนแบบ Single-pass-through

ฉันรู้สิ่งนี้ในขณะที่เล่นรอบกับการประมาณดังต่อไปนี้:

perl -pe 'if(/search_term/) {print foreach @A; print ">"; $B=4}; shift @A if push(@A, $_)>7; $_ = "" unless ($B-- > 0);' target_file

สิ่งนี้จะทำงานได้อย่างถูกต้องตามที่ grep -A7 -B3 ทำกับคำเตือนที่อธิบายไว้ในย่อหน้าแรก

ทางเลือกอื่น (เช่นไฟล์เดี่ยว) สำหรับปัญหานี้คือการใช้ Perl เพื่อป้อนสตริงคำสั่ง:

sed -n `perl -pe '$_=(/search_term/?sprintf("%d,%dp;", $.-3,$.+4):"")' file` file

ค่อนข้างสวย oneliner แต่ไฟล์นี้มีขนาดใหญ่มากดังนั้นการกดบรรทัดลงในอาร์เรย์ในกรณีนี้เป็นความคิดที่ไม่ดีใช่ไหม?
Prashant Bhate

shift @A if push(@A,$_)>7;บิตเพียง แต่ช่วยให้อาร์เรย์ของขนาดสูงสุด 7 รอบ (นั่นคือพารามิเตอร์ A ของคุณ) ตัวเลือกที่สองเก็บไฟล์ขนาดเล็กอย่างไม่น่าเชื่อ (เพียงเรียกใช้ perl โดยไม่มีเลเยอร์ด้านนอกเพื่อดูสิ่งที่สร้างขึ้นที่นั่น) แต่มันจะอ่านไฟล์สองครั้ง
user455

0

การใช้sedคุณสามารถรับหมายเลขบรรทัดของบรรทัดการจับคู่ลดและเพิ่มหมายเลขบรรทัดที่กำหนดในwhileลูปแล้วใช้sed -n "n1,n2p"เพื่อพิมพ์บรรทัดของบริบท ( n1) และการต่อท้าย ( n2) นำหน้า (คล้ายกับsedทางเลือกที่แนะนำโดย user455) กระบวนการอ่านจำนวนมากอาจนำไปสู่ประสิทธิภาพในการทำงาน

edสามารถอ้างอิงบรรทัดก่อนหน้าและถัดไปของบรรทัดที่ตรงกันได้โดยตรง แต่ล้มเหลวหากไม่มีช่วงของช่วงที่ระบุ ตัวอย่างเช่นสายการจับคู่คือหมายเลขบรรทัด 2 แต่ควรพิมพ์ 5 บรรทัดก่อนการจับคู่ การใช้edจึงจำเป็นต้องเพิ่มจำนวนบรรทัด (ว่าง) ที่เหมาะสมในตอนต้นและตอนท้าย (สำหรับไฟล์ขนาดใหญ่edอาจไม่ใช่เครื่องมือที่ถูกต้องโปรดดู: bfs - เครื่องสแกนไฟล์ขนาดใหญ่ )

# sample code to match lines with number 5 plus previous & following line
# (using Bash)
printf '%s\n' {1..20} > num.txt

# sed
sed -n '/5/=' num.txt | while read num; do
   n1=$((num - 1))
   n2=$((num + 1))
   [[ $n1 -lt 1 ]] && n1=1
   sed -n "${n1},${n2}p" num.txt
   echo --
done | sed -e '${/^--$/d;}'

# ed
cat <<-'EOF' | ed -s num.txt | sed -e $'N;N;a\\\n--' | sed -e '${/^--$/d;}'
H
0i
beginning: added line one
.
$a
end: added line one
.
,g/5/km\
'm-1,'m+1p
q
EOF
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.