एक लाइन की शुरुआत में एक निश्चित स्ट्रिंग को पकड़ना


20

grep "^$1"काम करता है की तरह है, लेकिन मैं कैसे बचते "$1"हैं grep विशेष रूप से किसी भी वर्ण की व्याख्या नहीं करता है?

या कोई बेहतर तरीका है?

संपादित करें: मैं '^$1'एक गतिशील रूप से सम्मिलित निश्चित स्ट्रिंग के लिए खोज नहीं करना चाहता हूं, जिसे केवल एक पंक्ति की शुरुआत में मिलान किया जाना चाहिए। यही मेरा मतलब है $1


क्या आपने दोहरे उद्धरण चिह्नों के बजाय एकल उद्धरणों का उपयोग करने की कोशिश की, जैसे grep '^$1'? या आपका मतलब यह नहीं था कि आप $1शेल द्वारा विस्तारित होने से रोकना चाहते हैं ?
मनिले

@ मैनील मैं '^ $ 1' की खोज नहीं करना चाहता, लेकिन गतिशील रूप से सम्मिलित निश्चित स्ट्रिंग के लिए जिसे केवल एक पंक्ति की शुरुआत में मिलान किया जाना चाहिए। यही मेरा मतलब $ 1 से है।
PSkocik

3
आप इसे grepभी कर सकते हैं, लेकिन आपको अपनी स्ट्रिंग में किसी भी विशेष चरित्र से बचना होगा जैसेprintf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile
डॉन_क्रांति

@don_crissti कि अन्य उत्तरों में से कुछ से बेहतर है। देखभाल यह एक बनाने के लिए?
रोएमा

@roaima - मुझे पता है, लेकिन यहाँ पहले से ही उत्तरों का एक गुच्छा है और यह (var के अंदर विशेष वर्णों से बचना) कुछ मैं (और यहाँ अन्य उपयोगकर्ताओं के एक जोड़े) काफी समय से घर पर हथौड़ा मार रहा है ... आप हमेशा जोड़ सकते हैं यदि आप चाहें तो यह आपके उत्तर के लिए है और मैं यहाँ टिप्पणी को हटा दूंगा (लापता ब्रेस को जोड़ना न भूलें)।
don_crissti

जवाबों:


7

मैं इसका उपयोग करने का तरीका नहीं सोच सकता grep; ^स्वयं एक नियमित अभिव्यक्ति का हिस्सा है इसलिए इसका उपयोग करने के लिए नियमित अभिव्यक्ति की आवश्यकता होती है। यह में सबस्ट्रिंग मिलान का उपयोग कर तुच्छ है 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 वास्तव में, मैंने इसे संभालने के लिए एक संस्करण जोड़ा है।
स्टीफन किट

अभी भी इस तरह के जटिल स्ट्रिंग्स के लिए असफल हो जाएगा जैसे \\\/\/\/\\\\/कि \\///\\/कार्यक्रम में देखा गया है। जहां तक ​​मुझे पता है कि जाग में बैकस्लैश को ठीक से भागने का कोई तरीका नहीं है, जब तक कि आप नहीं जानते कि पहले से कितने उपयोग किए जाएंगे।
123

1
@ 123 धन्यवाद, मैंने बचने की प्रक्रिया से बचने के लिए पर्यावरण के माध्यम से जाने की आपकी चाल को अनुकूलित किया है।
स्टीफन किट

मुझे अब भी यह समाधान सबसे अच्छा लगता है। कुशल (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मिलान लाइनों का पूरा पाठ प्राप्त करने के लिए, लेकिन उस बिंदु पर यह सिर्फ अजगर या रूबी की तरह एक आधुनिक स्क्रिप्टिंग भाषा के लिए तक पहुँचने के लिए आसान है।

(उपरोक्त उदाहरण पॉज़िक्स ग्रेप और कट को मानते हैं। वे मान लेते हैं कि खोज करने के लिए फ़ाइल मानक इनपुट से आती है, लेकिन इसके बदले फ़ाइल नाम लेने के लिए आसानी से अनुकूलित किया जा सकता है।)

संपादित करें: आपको यह भी सुनिश्चित करना चाहिए कि पैटर्न ( $1) शून्य-लंबाई वाला स्ट्रिंग नहीं है। अन्यथा cutकहने में विफल रहता है values may not include zero। इसके अलावा, यदि बैश का उपयोग करते हैं, तो set -o pipefailत्रुटि-निकास को पकड़ने के लिए उपयोग करें cut


10

पर्ल का उपयोग करने का एक तरीका जो बैकस्लैश का सम्मान करेगा

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

यह कमांड के लिए एनवायरनमेंट वेरिएबल v को सेट करता है, फिर प्रिंट करता है यदि वेरिएबल का इंडेक्स 0 है यानी लाइन की शुरुआत।

तुम भी जाग में समान कर सकते हैं

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

यदि आपका $1शुद्ध ASCII है और आपके 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 मान है। चरित्र का शाब्दिक रूप से मिलान किया जाता है, भले ही यह एक विशेष रीगेक्स चरित्र हो।

odइसका उपयोग हेक्स मानों की सूची में अपेक्षित लाइन प्रारंभ को परिवर्तित करने के लिए किया जाता है, जो तब एक साथ प्रहार करते हैं, प्रत्येक प्रिंटफ \xद्वारा उपसर्ग किया जाता है। ^आवश्यक regex बनाने के लिए इस स्ट्रिंग को फिर से तैयार किया गया है।


यदि आपका $1यूनिकोड है, तो यह काफी कठिन हो जाता है, क्योंकि आउटपुट के रूप में हेक्स बाइट्स के लिए वर्णों का 1: 1 पत्राचार नहीं है 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..

"का हवाला देते हुए अक्षरों से परे" perlre प्रलेखन की धारा बताते हैं:

मेटाकाचर्स को उद्धृत करते हुए

पर्ल में Backslashed अक्षरों से परे, जैसे अक्षरांकीय हैं \b, \w, \n। कुछ अन्य नियमित अभिव्यक्ति भाषाओं के विपरीत, कोई बैकस्लेस्ड प्रतीक नहीं हैं जो अल्फ़ान्यूमेरिक नहीं हैं। तो ऐसा लगता है कि कुछ भी है कि \\, \(, \), \[, \], \{, या \}हमेशा शाब्दिक वर्ण, नहीं एक metacharacter रूप में व्याख्या की है। यह एक बार एक सामान्य मुहावरे में इस्तेमाल किया गया था, जिसे आप एक पैटर्न के लिए उपयोग करना चाहते हैं जो स्ट्रिंग में नियमित अभिव्यक्ति मेटाचैकर्स के विशेष अर्थों को निष्क्रिय या उद्धृत करने के लिए करते हैं। बस सभी गैर- "शब्द" वर्णों को उद्धृत करें:

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

(यदि use localeसेट किया जाता है, तो यह वर्तमान लोकल पर निर्भर करता है।) आज सभी मेटाचैकरर्स के विशेष अर्थों को निष्क्रिय करने के लिए quotemetaफ़ंक्शन या \Qमेटाक्वॉटिंग एस्केप अनुक्रम का उपयोग करना अधिक आम है :

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

खबरदार है कि अगर आप शाब्दिक बैकस्लैश (जो इंटरपोल किए गए वेरिएबल्स के अंदर नहीं हैं) के बीच में \Qऔर \E, डबल-क्वॉलिटेड बैकलैश इंटरपोलेशन के कारण भ्रमित परिणाम हो सकते हैं। यदि आपको शाब्दिक बैकस्लैश का उपयोग करने की आवश्यकता है \Q...\E, तो पेरलोप में "पार्सिंग कोटेड कंस्ट्रक्शन के गोर विवरण" से परामर्श करें

quotemetaऔर \Qपूरी तरह से quotemeta में वर्णित हैं ।



2

यदि कोई ऐसा चरित्र है जिसका आप उपयोग नहीं करते हैं, तो आप इसका उपयोग लाइन की शुरुआत को चिह्नित करने के लिए कर सकते हैं। उदाहरण के लिए, $'\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

यदि आपको मिलान वाली रेखा की आवश्यकता नहीं है, तो आप अनुगामी को छोड़ सकते हैं 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.