bash文字列で始まる行を見つける


10

たくさんのファイルがあり、特定の文字列で始まる連続した行が含まれているファイルを見つけたい。

たとえば、次のファイルの場合:

Aaaaaaaaaaaa
Baaaaaaaaaaa
Cxxxxxxxxx
Cyyyyyyyyy
Czzzzzzzzz
Abbbbbbbbbbb
Bbbbbbbbbbbb
Caaaaaa
Accccccccccc
Bccccccccccc
Cdddddd
Ceeeeee

「C」で始まる行が複数あるので、このファイルをコマンドで見つけたいと思います。
たとえば、次のファイルの場合:

Aaaaaaaaaaaa
Baaaaaaaaaaa
Cxxxxxxxxx
Abbbbbbbbbbb
Bbbbbbbbbbbb
Caaaaaa
Accccccccccc
Bccccccccccc
Cdddddd

「C」で始まる行は常に1つあります。このファイルは必要ありません。a grepやa の使用を考えましたsedが、正確な方法がわかりません。多分正規表現^C.*$^Cかそのようなものを使用しています。何か案が ?


C2番目の例では、2行あります。
cuonglm 2014年

5
この質問は不明確です。で始まる連続した複数の行があるファイルを探していますCか?
Graeme

はい、これは私が欲しいものです。誤解してすみません。
ジェレミー

2
@terdon、それは-Pを使用した複数行の検索が2.5.4まで機能し、その後は機能しなくなったようですが、理由を説明する変更ログには何も見つかりません。
ステファンChazelas

1
@Graeme回答を元に戻す場合があります。Stephaneのコメントを参照してくださいgrep。古いバージョンでも動作するようです。
terdon

回答:


5

pcregrep

pcregrep -rMl '^C.*\nC' .

POSIXly:

find . -type f -exec awk '
  FNR==1 {last=0; printed=0; next}
  printed {next}
  /^C/ {if (last) {print FILENAME; printed=1; nextfile} else last=1; next}
  {last=0}' {} +

(ただし、をawkサポートしていない実装ですべてのファイルを完全に読み取ることを意味しますnextfile)。


grep2.5.4までのバージョンのGNUでは:

grep -rlP '^C.*\nC' .

動作するように見えますが、それは偶然であり、動作することが保証されていません。

このcommitによって)2.6で修正される前は、GNU grepは使用しているpcre検索機能がによって現在処理されているバッファ全体と一致することを見落としておりgrep、あらゆる種類の驚くべき動作を引き起こしていました。例えば:

grep -P 'a\s*b'

以下を含むファイルに一致します:

bla
bla

これは一致します:

printf '1\n2\n' | grep -P '1\n2'

しかしこれは:

(printf '1\n'; sleep 1; printf '2\n') | grep -P '1\n2'

または:

(yes | head -c 32766; printf '1\n2\n') > file; grep -P '1\n2' file

そうではありません(これは、1\n2\nによって処理される2つのバッファ間で発生するためgrep)。

ただし、その動作は文書化されていました。

15-どうすれば行を越えて照合できますか?

基本的に行ベースであるため、標準のgrepはこれを実行できません。したがって、 '[:space:]'文字クラスを使用するだけでは、期待した方法で改行が一致しません。ただし、grepがPerlパターンを有効にしてコンパイルされている場合、Perlの「s」修飾子(「。」を改行に一致させる)を使用できます。

     printf 'foo\nbar\n' | grep -P '(?s)foo.*?bar'

それが2.6で修正された後、ドキュメントは修正されませんでした(以前にそこで報告しました)。


nextfileの代わりに使用exitしない理由はあります-exec \;か?
terdon

@terdon、つまり、awkファイルごとに1 つ実行することになります。awkサポートしておらずnextfile、ファイルの大部分があり、ファイルの先頭に向かって一致する行がある場合にのみ、これを行う必要があります。
ステファンChazelas

行終端子をNULに設定してファイル全体を単一の文字列のように見せることで複数行の一致を容易にするこのgrepテクニック(GNU grepの最近のバージョンではそうでしょう)はどうですか?制限があるかどうか知っていますか?
iruvar 2014年

1
@ 1_CR、NUL文字がそこになく、行にNUL文字が含まれていないと仮定すると、メモリ全体にファイル全体がロードされます。また、古いバージョンのGNU grep(OPが持っている)はで使用できないことに注意-zしてください-P。何もありません\Nせずに-P、あなたがそれを書く必要があるだろう、$'[\01-\011\013-\0377]'唯一のCロケールでの作業と思われる(参照thread.gmane.org/gmane.comp.gnu.grep.bugs/5187
ステファンChazelas

@StephaneChazelas、非常に役立つ詳細、ありがとう
iruvar 14年

2

awk

awk '{if (p ~ /^C/ && $1 ~ /^C/) print; p=$1}' afile.txt

これは、で始まる連続した行がある場合、ファイルの内容を出力しますC。式(p ~ /^C/ && $1 ~ /^C/)はファイル内の連続する行を調べ、両方の最初の文字が一致する場合にtrueと評価されますC。その場合、行が印刷されます。

このようなパターンを持つすべてのファイルを見つけるには、find次のコマンドを使用して上記のawkを実行できます。

find /your/path -type f -exec awk '{if (p ~ /^C/ && $1 ~ /^C/) {print FILENAME; exit;} p=$1}' {} \;

このコマンドでは、find+ execは各ファイルを通過し、各ファイルで同様のawkフィルタリングを実行しFILENAME、awk式がtrueと評価された場合にその名前を出力します。FILENAME複数の一致がある1つのファイルの複数回の印刷を回避するために、exitステートメントが使用されます(@terdonに感謝)。


私の質問は、私が始まる複数の連続したラインを持つファイルの名前を知りたい、明確では十分ではなかったC
ジェレミー

@Jérémie回答を更新しました。
mkc 14年

これがどのように機能するかについての説明を追加していただけませんか?また、の必要はなくflagexit代わりに。そうすれば、一致が見つかった後にファイルを処理し続ける必要はありません。
terdon

2

GNUのさらに別のオプションsed

単一ファイルの場合:

sed -n -- '/^C/{n;/^C/q 1}' "$file" || printf '%s\n' "$file"

(ただし、読み取れないファイルも報告されます)。

の場合find

find . -type f ! -exec sed -n '/^C/{n;/^C/q 1}' {} \; -print

読み取れないファイルが印刷される問題は、次のように書くことで回避できます。

find . -type f -size +2c -exec sed -n '$q1;/^C/{n;/^C/q}' {} \; -print

詳細を教えていただけますsed -n '$q1;/^C/{n;/^C/q}'か?
ジェレミー

説明してくれる人はいますか?
ジェレミー

@Jérémie- $q1パターンが見つからない場合、sedを強制的にエラーで終了します。また、ファイルに問題がある場合(読み取り不能または壊れている場合)はエラーで終了します。したがって、パターンが見つかり印刷に渡される場合にのみ、終了ステータス0で終了します。との部分/^C/{n;/^C/qはかなりシンプルです。Cで始まる文字列が見つかった場合は次の行を読み取り、Cで始まった場合は終了ステータス0で終了します。
2014年

1

ファイルがメモリに読み込まれるほど小さいと仮定します。

perl -000ne 'print "$ARGV\n" if /^C[^\n]*\nC/sm' *

説明:

  • - 000\n\nレコードセパレータとして設定されます。これにより、段落モードがオンになり、段落(連続する改行で区切られた)が単一行として扱われます。
  • -ne:引数として指定されたスクリプトを-e入力ファイルの各行に適用します。
  • $ARGV :現在処理中のファイルです
  • /^C[^\n]*\nC/C行の先頭で一致します(smこれが機能する理由については、以下の修飾子の説明を参照してください)。その後に0個以上の非改行文字、改行、次に別のCが続きます。つまり、で始まる連続する行を見つけますC。* //sm:これらの一致修飾子は次のとおりです([ここに記載されています])。

    • m:文字列を複数行として扱います。つまり、 "^"と "$"を、文字列の左端と右端のみの行の最初または最後と一致するようから、文字列内の任意の場所と一致するように変更します。

    • s:文字列を1行として扱います。つまり、「。」を変更します。通常は一致しない改行を含め、すべての文字に一致します。

次のような醜いこともできます:

for f in *; do perl -pe 's/\n/%%/' "$f" | grep -q 'C[^%]*%%C' && echo "$f"; done

ここで、perlコードは改行をで置き換えます。入力ファイルに(もちろん、大きい場合%%がないと想定すると、はで始まる連続する行に一致します。%%grepC


1

解決:

( set -- *files ; for f ; do (
set -- $(printf %c\  `cat <$f`)
while [ $# -ge 1 ] ;do [ -z "${1#"$2"}" ] && {
    echo "$f"; break ; } || shift
done ) ; done )

デモ:

まず、テストベースを作成します。

abc="a b c d e f g h i j k l m n o p q r s t u v w x y z" 
for l in $abc ; do { i=$((i+1)) h= c= ;
    [ $((i%3)) -eq 0 ] && c="$l" h="${abc%"$l"*}"
    line="$(printf '%s ' $h $c ${abc#"$h"})"
    printf "%s$(printf %s $line)\n" $line >|/tmp/file${i}
} ; done

上記は/tmpnamedに26個のファイルを作成しますfile1-26各ファイルには、文字で始まりa-z、残りのアルファベットが続く27行または28行があります。3番目のファイルごとに、最初の文字が重複する2つの連続した行が含まれています。

サンプル:

cat /tmp/file12
...
aabcdefghijkllmnopqrstuvwxyz
babcdefghijkllmnopqrstuvwxyz
cabcdefghijkllmnopqrstuvwxyz
...
kabcdefghijkllmnopqrstuvwxyz
labcdefghijkllmnopqrstuvwxyz
labcdefghijkllmnopqrstuvwxyz
mabcdefghijkllmnopqrstuvwxyz
...

そして私が変わるとき:

set -- *files

に:

set -- /tmp/file[0-9]*

わかった...

出力:

/tmp/file12
/tmp/file15
/tmp/file18
/tmp/file21
/tmp/file24
/tmp/file3
/tmp/file6
/tmp/file9

つまり、簡単に言うと、ソリューションは次のように機能します。

setsすべてのファイルに対するサブシェルの位置、およびそれぞれの

set、それがループして、各ファイルの各行の最初の文字に、ネストされたサブシェルのpositionalsを。

[ tests ]$1否定を$2示す場合は一致、そうであれば

echoesファイル名は、breaksの電流ループの繰り返し

それ以外shiftの場合は、次の1文字の位置に移動して再試行します


0

このスクリプトはgrep、とcutを使用して一致する行の行番号を取得し、連続する2つの番号をチェックします。ファイルは、スクリプトの最初の引数として渡された有効なファイル名と見なされます。

#!/bin/bash

checkfile () {
 echo checking $1
 grep -n -E "^C.*$" $1 | cut -d: -f1 | while read linenum
     do
        : $[ ++PRV ] 
        if [ $linenum == $PRV ]; then return 1; fi
        PRV=$linenum
     done
     return 0
}

PRV="-1"
checkfile $1
if [ $? == 0 ]; then
   echo Consecutive matching lines found in file $1
fi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.