схоплюючи фіксовану струну на початку рядка


20

grep "^$1"подібні твори, але як я втечу, "$1"щоб греп не спеціально інтерпретував жодних персонажів у ньому?

Або є кращий спосіб?

Редагувати: я не хочу шукати, '^$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 - Я знаю, але тут вже є маса відповідей, і це (втеча від спеціальних характеристик всередині вар) - це те, що я (та ще декілька інших користувачів тут) забиваю додому довгий час ... Ви завжди можете додати це на вашу відповідь, якщо ви хочете, і я видалю тут коментар (не забудьте додати відсутніх провідної дужки).
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 + відсутність додаткових процесів, необхідних для встановлення стану) використовує стандартні інструменти, і досить стислий. В усіх інших відповідях відсутні хоча б деякі з них. (Ефективність тут є важливим моментом, оскільки греп відомий незрівнянною швидкістю.)
PSkocik

14

Якщо вам потрібно лише перевірити, чи знайдено збіг, виріжте всі вхідні рядки до довжини потрібного префікса ( $1) та використовуйте греп фіксованого шаблону:

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.

(Наведені вище приклади передбачають, що Posix збирається і вирізається. Вони припускають, що файл для пошуку надходить зі стандартного вводу, але його можна легко адаптувати, щоб замість нього взяти ім’я файлу.)

Редагувати: Ви також повинні переконатися, що шаблон ( $1) не є рядком нульової довжини. Інакше cutне вдається сказати values may not include zero. Також, якщо ви використовуєте Bash, використовуйте set -o pipefailдля лову виходів помилок на cut.


10

Спосіб за допомогою perl, який поважатиме зворотні риси

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

Це встановлює змінну середовища v для команди, а потім друкує, якщо індекс змінної дорівнює 0, тобто початок рядка.

Ви також можете зробити ідентичні у див

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

7

Ось варіант all-bash, що я не рекомендую bash для обробки тексту, але він працює.

#!/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використовується для перетворення очікуваного початку рядка до списку шістнадцяткових значень, які потім з'єднуються разом, з кожним префіксом \xprintf. ^Тоді попередньо передбачено цей рядок для створення необхідного регулярного вираження.


Якщо ваш $1Unicode, то це стає дещо складніше, тому що не існує відповідності символів 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 пояснюється:

Цитуючи метахарактеристики

Метасимволи в керуючих послідовностей Perl є буквено - цифровими, такими , як \b, \w, \n. На відміну від деяких інших мов регулярного вираження, немає зворотних косих символів, які не буквено-цифрові. Тому все , що виглядає як \\, \(, \), \[, \], \{, або \}завжди інтерпретується як буквений символ, а НЕ метасимволу. Це колись було використано в загальній ідіомі для відключення або цитування спеціальних значень метахарактерів регулярних виразів у рядку, який потрібно використовувати для шаблону. Просто процитуйте всі символи, які не мають слова:

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

(Якщо use localeвстановлено, то це залежить від поточного локалу.) Сьогодні частіше використовувати quotemetaфункцію або \Q послідовність введення метаквітування для відключення спеціальних значень усіх метахарактерів, як це:

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

Пам’ятайте, що якщо ви поставите буквальні зворотні косої риски (ті, що не знаходяться всередині інтерпольованих змінних) між \Qта \E, подвійне зменшення зворотної косої риски може призвести до заплутаних результатів. Якщо вам потрібно використовувати буквальні зворотні риси \Q...\E, зверніться до “Gory деталей розбору цитованих конструкцій” у перлопі .

quotemeta і \Q повністю описані в quometa .


3

Якщо ваша греп має опцію -P, це означає PCRE , ви можете зробити це:

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

Зверніться до цього питання , і див. Документ PCRE, щоб отримати детальну інформацію, якщо ви хочете.


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

Якщо вам не потрібні відповідні рядки, тоді ви можете відмовитись від трейлінгу 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 та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.