поиск фиксированной строки в начале строки


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 - я знаю, но здесь уже есть куча ответов, и это (без специальных символов внутри vars) - это то, что я (и несколько других пользователей здесь) довольно долго стучал домой ... Вы всегда можете добавить если хотите, ответьте на ваш ответ, и я удалю здесь комментарий (не забудьте добавить пропущенную ведущую скобку).
— 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 на самом деле, я добавил вариант, чтобы справиться с этим.
— Стивен Китт

Будет по-прежнему не работать для сложных строк, таких как \\\/\/\/\\\\/это видно \\///\\/из программы. Насколько я знаю, в awk нет способа правильно избежать обратных слешей, если только вы не знаете, сколько из них будет использовано заранее.
— 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получить полный текст соответствующих строк, но в этот момент проще всего достичь современного языка сценариев, такого как Python или Ruby.

(Приведенные выше примеры предполагают использование Posix grep и cut. Они предполагают, что файл для поиска поступает из стандартного ввода, но вместо этого может быть легко адаптирован для получения имени файла.)

Редактировать: Вы также должны убедиться, что pattern ( $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, т.е. началу строки.

Вы также можете сделать то же самое в awk

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

7

Вот вариант полностью 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 escape-последовательность мета-цитирования, чтобы отключить специальные значения всех метасимволов, например:

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

Имейте в виду, что если вы поместите буквенные обратные слэши (те, которые не входят в интерполированные переменные) между \Qи \E, двойная квотированная обратная косая черта может привести к путанным результатам. Если вам нужно использовать обратную косую черту внутри \Q...\E, проконсультируйтесь с «Gory details of синтаксический анализ цитируемых конструкций» в perlop .

quotemetaи \Qполностью описаны в цитате .



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 и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.