grepping สตริงคงที่ที่จุดเริ่มต้นของบรรทัด


20

grep "^$1"การเรียงลำดับของงาน แต่ฉันจะหลบหนี"$1"เพื่อ grep ไม่ตีความตัวละครใด ๆ ในนั้นเป็นพิเศษได้อย่างไร

หรือมีวิธีที่ดีกว่า

แก้ไข: ฉันไม่ต้องการค้นหา'^$1'แต่สำหรับสตริงคงที่ที่แทรกแบบไดนามิกซึ่งควรจับคู่ก็ต่อเมื่อเป็นจุดเริ่มต้นของบรรทัด $1นั่นคือสิ่งที่ฉันหมายถึงโดย


คุณพยายามใช้เครื่องหมายคำพูดเดี่ยวแทนที่จะเป็นเครื่องหมายคำพูดคู่grep '^$1'หรือไม่? หรือคุณไม่ได้หมายความว่าคุณต้องการป้องกัน$1การขยายตัวของเชลล์?
mnille

@mnille ฉันไม่ต้องการค้นหา '^ $ 1' แต่สำหรับสตริงคงที่ที่แทรกแบบไดนามิกซึ่งควรจับคู่หากเป็นจุดเริ่มต้นของบรรทัด นั่นคือสิ่งที่ฉันหมายถึงโดย $ 1
PSkocik

3
คุณสามารถทำได้ด้วยgrepเช่นกัน แต่คุณจะต้องหลบหนีอักขระพิเศษใด ๆ ในสายอักขระของคุณก่อนเช่นprintf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile
don_crissti

@don_crissti ดีกว่าคำตอบอื่น ๆ สนใจที่จะทำให้มันเป็นหนึ่ง
roaima

@roaima - ฉันรู้ แต่มีคำตอบมากมายอยู่ที่นี่แล้วและนี่ (สิ่งที่หลบหนีเข้าไปใน chars พิเศษภายใน vars) เป็นสิ่งที่ฉัน (และผู้ใช้อื่นสองสามคนที่นี่) ได้รับการตอกกลับบ้านมาระยะหนึ่ง ... คุณสามารถเพิ่มได้เสมอ คำตอบของคุณหากคุณต้องการและฉันจะลบความคิดเห็นที่นี่ (อย่าลืมเพิ่มวงเล็บปีกกาชั้นนำที่หายไป)
don_crissti

คำตอบ:


7

ฉันไม่สามารถคิดวิธีการทำสิ่งนี้โดยใช้grep; ^ตัวเองเป็นส่วนหนึ่งของการแสดงออกปกติดังนั้นการใช้มันต้องมีการตีความนิพจน์ปกติ มันเป็นเรื่องเล็ก ๆ น้อย ๆ โดยใช้ substring ตรงกันในawk, perlหรืออะไรก็ตาม:

awk -v search="$1" 'substr($0, 1, length(search)) == search { print }'

ในการจัดการกับสตริงการค้นหาที่มี\คุณสามารถใช้กลอุบายเช่นเดียวกับในคำตอบของ 123 :

search="$1" awk 'substr($0, 1, length(ENVIRON["search"])) == ENVIRON["search"] { print }'

สิ่งนี้จะใช้ไม่ได้กับสตริงเช่น\/
123

@ 123 แน่นอนฉันได้เพิ่มตัวแปรเพื่อจัดการกับมัน
Stephen Kitt

จะยังคงล้มเหลวสำหรับสตริงที่ซับซ้อนเช่น\\\/\/\/\\\\/ที่เห็น\\///\\/ในโปรแกรม เท่าที่ฉันรู้ว่าไม่มีวิธีใดที่จะหลบหนีแบ็กสแลชใน awk ได้อย่างถูกต้องเว้นแต่คุณจะรู้ว่าจะต้องใช้จำนวนเท่าไหร่ก่อน
123

1
@ 123 ขอบคุณฉันได้ปรับอุบายของคุณให้ผ่านสภาพแวดล้อมเพื่อหลีกเลี่ยงการหลบหนี
Stephen Kitt

ฉันยังคงชอบวิธีนี้ดีที่สุด มีประสิทธิภาพ (awk + ไม่เสียเวลาดูรอบ ๆ ) เริ่มต้นอย่างรวดเร็ว (awk + ไม่มีกระบวนการเพิ่มเติมที่จำเป็นในการตั้งค่าสถานะ) ใช้เครื่องมือมาตรฐานและค่อนข้างรัดกุม คำตอบอื่น ๆ ทั้งหมดขาดอย่างน้อยบางส่วนของเหล่านี้ (ประสิทธิภาพเป็นจุดแข็งที่นี่เนื่องจาก grep เป็นที่รู้จักกันในเรื่องความเร็วที่ไม่มีใครเทียบได้)
PSkocik

14

หากคุณต้องการตรวจสอบว่าพบการแข่งขันหรือไม่ให้ตัดบรรทัดอินพุตทั้งหมดตามความยาวของคำนำหน้า ( $1) จากนั้นใช้ grep รูปแบบคงที่:

if cut -c 1-"${#1}" | grep -qF "$1"; then
    echo "found"
else
    echo "not found"
fi

นอกจากนี้ยังง่ายต่อการนับจำนวนเส้นที่ตรงกัน:

cut -c 1-"${#1}" | grep -cF "$1"

หรือหมายเลขบรรทัดของการจับคู่ทั้งหมด (หมายเลขบรรทัดเริ่มต้นที่ 1):

cut -c 1-"${#1}" | grep -nF "$1" | cut -d : -f 1

คุณสามารถป้อนหมายเลขบรรทัดheadและtailเพื่อรับข้อความเต็มของบรรทัดที่ตรงกัน แต่ ณ จุดนั้นมันง่ายกว่าที่จะเข้าถึงภาษาสคริปต์ที่ทันสมัยเช่น Python หรือ Ruby

(ตัวอย่างข้างต้นสมมติว่ามี grep และตัดของ Posix พวกเขาถือว่าไฟล์ที่ค้นหามาจากอินพุตมาตรฐาน แต่สามารถปรับให้ใช้ชื่อไฟล์แทนได้อย่างง่ายดาย)

แก้ไข: คุณควรตรวจสอบให้แน่ใจว่ารูปแบบ ( $1) ไม่ใช่สตริงที่มีความยาวเป็นศูนย์ มิฉะนั้นล้มเหลวบอกว่าcut values may not include zeroนอกจากนี้ถ้าใช้ทุบตีใช้เพื่อจับข้อผิดพลาดออกโดยset -o pipefailcut


10

วิธีการใช้ Perl ซึ่งจะเคารพแบ็กสแลช

v="$1" perl -ne 'print if index($_, $ENV{"v"} )==0' file

ชุดนี้ตัวแปรสภาพแวดล้อม v สำหรับคำสั่งแล้วพิมพ์ว่าดัชนีของตัวแปรคือ 0 คือจุดเริ่มต้นของบรรทัด

คุณยังสามารถทำสิ่งเดียวกันได้ใน awk

v="$1" awk 'index($0, ENVIRON["v"])==1' file

7

นี่คือตัวเลือกการทุบตีทั้งหมดไม่ใช่ที่ฉันแนะนำให้ใช้ทุบตีเพื่อการประมวลผลข้อความ แต่ใช้งานได้

#!/usr/bin/env bash
# searches for $1 at the beginning of the line of its input

len=${#1}
while IFS= read -r line
do
  [[ "${line:0:len}" = "$1" ]] && printf "%s\n" "$line"
done

สคริปต์คำนวณความยาวlenของพารามิเตอร์อินพุต $ 1 จากนั้นใช้การขยายพารามิเตอร์ในแต่ละบรรทัดเพื่อดูว่าlenอักขระตัวแรกตรงกับ $ 1 หรือไม่ หากเป็นเช่นนั้นจะพิมพ์บรรทัด


4

หาก$1ASCII ของคุณบริสุทธิ์และคุณgrepมี-Pตัวเลือก (เพื่อเปิดใช้งาน PCRE) คุณสามารถทำได้ดังนี้:

#!/bin/bash

line_start="$1"
line_start_raw=$(printf '%s' "$line_start" | od -v -t x1 -An)
line_start_hex=$(printf '\\x%s' $line_start_raw)
grep -P "^$line_start_hex"

แนวคิดในที่นี้คือgrep -Pอนุญาตให้นิพจน์ทั่วไปที่มี\xXXเพื่อระบุอักขระตัวอักษรโดยที่XXค่า ASCII ฐานสิบหกของอักขระนั้น อักขระนั้นถูกจับคู่อย่างแท้จริงแม้ว่าจะเป็นอักขระพิเศษ regex ก็ตาม

odถูกใช้เพื่อแปลงบรรทัดที่คาดหวังเริ่มต้นไปยังรายการค่าเลขฐานสิบหกซึ่งต่อมาถูกรวมเข้าด้วยกันซึ่งแต่ละส่วนนำหน้าด้วย\xprintf ^จะถูกต่อเติมสตริงนี้เพื่อสร้าง regex ที่ต้องการ


หากคุณ$1เป็น Unicode แล้วนี้จะค่อนข้างเป็นบิตยากเพราะมีไม่ได้เป็น 1: 1 จดหมายของตัวละครเพื่อ hex odไบต์เป็นผลผลิตจาก


3

เป็นตัวกรอง:

perl -ne 'BEGIN {$pat = shift} print if /^\Q$pat/' search-pattern

ทำงานกับหนึ่งไฟล์ขึ้นไป:

perl -ne 'BEGIN {$pat = shift} print if /^\Q$pat/' search-pattern file..

ส่วน"การอ้างถึงตัวอักษร" ของเอกสารอธิบายที่เกี่ยวข้อง:

การอ้างถึงตัวอักษร

metacharacters backslashed ใน Perl เป็นตัวอักษรและตัวเลขเช่น\b, ,\w \nไม่เหมือนกับภาษานิพจน์ทั่วไปอื่น ๆ ไม่มีสัญลักษณ์แบ็กสแลชที่ไม่ใช่ตัวอักษรและตัวเลข ดังนั้นสิ่งที่มีลักษณะเช่น\\, \(, \), \[, \], \{หรือ\}ถูกตีความเสมอเป็นตัวอักษรตัวอักษรไม่ metacharacter ครั้งนี้ใช้ในสำนวนทั่วไปเพื่อปิดใช้งานหรืออ้างอิงความหมายพิเศษของ metacharacters นิพจน์ปกติในสตริงที่คุณต้องการใช้สำหรับรูปแบบ เพียงพูดอักขระที่ไม่ใช่ "คำ" ทั้งหมด:

    $pattern =~ s/(\W)/\\$1/g;

(หากuse localeมีการตั้งค่าสิ่งนี้จะขึ้นอยู่กับสถานที่ปัจจุบัน) วันนี้มันเป็นเรื่องปกติมากที่จะใช้quotemetaฟังก์ชั่นหรือ\Q ลำดับการหลบหนีของเมตาเพื่อยุติการปิดใช้งานความหมายพิเศษของอักขระเมตาทั้งหมด:

    /$unquoted\Q$quoted\E$unquoted/

ระวังว่าถ้าคุณใส่เครื่องหมายแบ็กสแลชตามตัวอักษร (ซึ่งไม่อยู่ในตัวแปรที่ถูกสอดแทรกระหว่าง) ระหว่าง\Qและ\Eการแก้ไขแบ็กสแลชสองครั้งอาจทำให้เกิดผลลัพธ์ที่สับสน หากคุณจำเป็นต้องใช้เครื่องหมายอักษรภายใน\Q...\Eปรึกษา“รายละเอียดของเลือดแยกโครงสร้างยก” ใน perlop

quotemetaและ\Qจะมีการอธิบายอย่างเต็มที่ในquotemeta


3

หาก grep ของคุณมีตัวเลือก -P ซึ่งหมายถึงPCREคุณสามารถทำได้ดังนี้:

grep -P "^\Q$1\E"

อ้างถึงคำถามนี้และดูPCRE docสำหรับรายละเอียดหากคุณต้องการ


2

หากมีอักขระ aa ที่คุณไม่ได้ใช้คุณสามารถใช้อักขระนั้นเพื่อทำเครื่องหมายจุดเริ่มต้นของบรรทัด ตัวอย่างเช่น$'\a'(ASCII 007) มันน่าเกลียด แต่มันจะทำงาน:

{ echo 'this is a line to match'; echo 'but this is not'; } >file.txt

stuffing=$'\a'    # Guaranteed never to appear in your source text
required='this'   # What we want to match that beginning of a line

match=$(sed "s/^/$stuffing/" file.txt | grep -F "$stuffing$required" | sed "s/^$stuffing//")

if [[ -n "$match" ]]
then
    echo "Yay. We have a match: $match"
fi

หากคุณไม่จำเป็นต้องจับคู่สาย (s) แล้วคุณสามารถวางต่อท้ายและการใช้งานsed grep -qFแต่มันง่ายกว่าด้วยawk(หรือperl) ...


0

เมื่อคุณต้องการค้นหาไฟล์ที่ไม่มีการวนซ้ำคุณสามารถใช้:
ตัดไฟล์ด้วยความยาวของสตริงการค้นหา

  cut -c1-${#1} < file

ค้นหาสตริงคงที่และส่งคืนหมายเลขบรรทัด

  grep -Fn "$1" <(cut -c1-${#1} < file)

ใช้หมายเลขบรรทัดสำหรับสิ่งที่ชอบ sed -n '3p;11p' file

  sed -n "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed 's/:.*/p;/' | tr -d '\n')" file

เมื่อคุณต้องการลบบรรทัดเหล่านี้ให้ใช้

  sed "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed 's/:.*/d;/' | tr -d '\n')" file
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.