行の先頭で固定文字列をgrepする


20

grep "^$1"動作しますが、"$1"grepがその中の文字を特別に解釈しないようにするにはどうすればエスケープできますか?

または、より良い方法がありますか?

編集:'^$1'動的に挿入された固定文字列 を検索するのではなく、行の先頭にある場合にのみ一致する必要があります。それが私が意味したもの$1です。


たとえば、二重引用符の代わりに単一引用符を使用しようとしましたgrep '^$1'か?それとも$1、シェルによって展開されるのを防ぎたいという意味ではありませんか?
ニール

@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^それ自体は正規表現の一部であるため、それを使用するには、正規表現を解釈する必要があります。これは、サブストリングマッチングで使用して些細だawkperlまたは何を:

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が知られているため、ここでの
長所

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を想定しています。検索するファイルは標準入力からのものであると想定していますが、代わりにファイル名を取得するように簡単に調整できます。)

編集:パターン($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、その文字の16進ASCII値です。文字は、特別な正規表現文字であっても、文字通り一致します。

odは、期待される行の開始を16進値のリストに変換するために使用され、その後、それぞれが\xprintfで始まる16進数値のリストに変換されます。 ^次に、この文字列を先頭に追加して、必要な正規表現を作成します。


$1がUnicodeの場合、これはかなり困難になります。これは、による出力としての文字と16進バイトの1:1対応がないためodです。


3

フィルターとして:

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

1つ以上のファイルで実行します。

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でリテラルのバックスラッシュを使用する必要がある場合は、perlopの「引用構造の解析の詳細」を参照してください

quotemetaおよびquotemetaで\Q完全に説明されています


3

grepにPCRPを意味する-Pオプションがある場合、これを行うことができます。

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

この質問を参照し、必要に応じて詳細についてはPCREのドキュメントを参照してください。


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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.