/ / Wie setze ich Regex-Match-Gruppen in separate Ausgabespalten und behandle korrekt fehlende / leere Werte? - bash, grep, Textverarbeitung

Wie setze ich Regex-Match-Gruppen in separate Ausgabespalten, die korrekt mit fehlenden / leeren Werten umgehen? - bash, grep, Textverarbeitung

wenn ich die folgende Datei habe:

This file has two lines
This file has three lines
This file has four
This file has five lines

Ich würde gerne grep file und lines so dass ich folgende Ausgabe habe:

file lines
file lines
file
file lines

Wenn beide Übereinstimmungen pro Zeile gefunden werden, drucken Sie die Übereinstimmungen in derselben Zeile. Wenn nur eine gefunden wurde, drucken Sie sie, lassen Sie einen Platzhalter (null / leer / was auch immer) und gehen Sie dann zur nächsten Zeile.

Ich habe das versucht mit:

grep -oP "(file)|(lines)" example.txt | paste -d " " - -

aber ich bekomme:

file lines
file lines
file file
lines

wo denn lines wurde nicht in der dritten Zeile gefunden, es findet file von der nächsten Zeile und legt es auf der gleichen Ausgangsleitung.

Ich zwinge grundsätzlich paste um Schlitze in der Ausgabe zu füllen, unabhängig davon, was in jeder Zeile gefunden wird.

Wie kann ich das ändern?

Antworten:

2 für die Antwort № 1

Ich nehme an, dass file und lines sind eigentlich reguläre Ausdrücke mit eigenen Match-Gruppen. Folgendes ermöglicht die Verwendung von ERE an Ort und Stelle:

#!/usr/bin/env bash

# replace these with any ERE-compliant regex of your choice
file_re="(file)"    # for instance: file_re="file=([^[:space:]]+)([[:space]]|$)"
lines_re="(lines)"

while IFS= read -r line; do
# default to a blank placeholder if no matches exist
file= lines=

# compare against each regex; if one matches, assign the group contents to a variable
[[ $line =~ $file_re ]] && file=${BASH_REMATCH[1]}
[[ $line =~ $lines_re ]] && lines=${BASH_REMATCH[1]}

# print a line of output if *either* regex matched.
[[ $file || $lines ]] && printf "%st%sn" "$file" "$lines"

done <"${1:-example.txt}" # with input from $1 if given, or example.txt otherwise

Sehen BashFAQ # 1 ("Wie kann ich eine Datei (Datenstrom, Variable) Zeile für Zeile (und / oder Feld für Feld) lesen?") für eine Beschreibung der hier verwendeten Technik.


Mit Ihrer gegebenen Eingabe ist die Ausgabe:

file    lines
file    lines
file
file    lines

0 für die Antwort № 2

sed ist für s/old/new/ und Grep ist für g/re/p. Für andere Textmanipulationen sollten Sie awk verwenden.

Mit GNU awk für den 3. arg passen ():

$ awk "{f=match($0,/file/,a); f+=match($0,/lines/,b)} f{print a[0], b[0]}" file
file lines
file lines
file
file lines

Mit anderen Awks verwenden Sie substr (), um die übereinstimmenden Zeichenfolgen zu erfassen:

$ awk "{f=match($0,/file/); a=substr($0,RSTART,RLENGTH); f+=match($0,/lines/); b=substr($0,RSTART,RLENGTH)} f{print a, b}" file
file lines
file lines
file
file lines