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


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.