So formatieren Sie eine Bash-Schleife mit sed, awk, grep und wc

So formatieren Sie eine Bash-Schleife mit sed, awk, grep und wc

Ich habe also eine Textdatei, aus der ich bestimmte Zeilen extrahieren und zählen muss, wie oft eine Zahl in einer bestimmten Spalte vorkommt. Ich habe ungefähr 100 dieser Dateien. Ich kann das in kleinen Schritten erledigen, möchte es aber mit Bash/KSH erledigen:

foreach i *h3
sed '4p;55p;77q;d' $i >> output.txt
end 

^^^^dadurch werden nur die Zeilen extrahiert, die ich aus jeder h3-Datei brauche

awk '{print $6}' output.txt | grep 'P2' | wc -l

^^^dadurch wird einfach Spalte 6 aus output.txt extrahiert und gezählt, wie oft P2 in Spalte 6 vorkommt

Gibt es eine Möglichkeit, all dies in einem Bash/Ksh-Skript zu kombinieren?

Antwort1

Wenn ich das richtig verstanden habe:

  • Sie möchten zählen, wie oft „P2“ irgendwo im 6. Feld der Zeilen 4, 55 und 77 einiger Dateien (mit dem Namen *h3) vorkommt?

Sie könnten dies mit 1 awk tun:

awk '
( FNR==4 || FNR==55 || FNR==77 ) {
    if ( $6 ~ "P2" ) { occurence++ } 
}
END {
    printf "There was: %d P2 ", occurence
    printf " among the 6th field on lines 4,55 or 77 of the *h3 files\n"
}' *h3

Hinweis: Ändern Sie es $6 ~ "P2"in , $6 == "P2"wenn Sie eine genaue Übereinstimmung wünschen (anstelle eines grep, wie Sie es in Ihrem eigenen Beispiel verwendet haben, sodass es auch mit Folgendem somethingP2otherthingund Varianten davon übereinstimmt).

FNR = Anzahl der Datensätze der Datei = Anzahl der Zeilen in der aktuellen Datei (d. h. beginnt in der ersten Zeile jeder Datei wieder bei 1) (Aktuelle Datei, deren Name auch durch die interne Variable „FILENAME“ ermittelt werden kann)

(NR = würde hier nicht funktionieren, da es sich um die (Gesamt-)Anzahl der seit Beginn gelesenen Datensätze handelt (nicht seit Beginn der aktuellen Datei))

Antwort2

Sicher. Hier ist eine Möglichkeit

p2_count=0
for f in *h3; do
    for ((n=1; n<=77; n++)); do
        IFS= read -r line
        if [[ $n == 4|55|77 ]]; then
            echo "$line"
            set -f
            set -- $line
            set +f
            if [[ $6 == *P2* ]]; then
                ((p2_count++))
            fi
        fi
    done < "$f"
done > output.txt
echo "saw P2 in 6th column $p2_count times"

Antwort3

Oder mit einemSchlagEinzeiler:

for i in *h3; do sed '4p;55p;77q;d' $i | awk '{print $6}' | grep 'P2'; done | wc -l

Oder kürzer mit grep -c:

for i in *h3; do sed '4p;55p;77q;d' $i | awk '{print $6}'; done | grep -c 'P2'

Antwort4

Normalerweise, wenn eine Frage lautet: "Wie verarbeite ich eine Reihe von Textdateien mitspezifische(s) Werkzeug(e)in einer Bash-Schleife?", lautet die Antwort teilweise: "Verwenden Sie keine Bash-Schleife, verwenden Sie (einige oder alle) der Tools selbst". Manchmal lautet ein Teil der Antwort sogar: "Verwenden Sie diese Tools nicht, verwenden Sie stattdessen dies".

Was Sie wollen, können Sie allein erledigen awk, eine Shell-Schleife ist nicht erforderlich. Oder sedoder grepoder wc:

awk 'BEGIN {OFS="\t"}
     FNR ~ /^(4|10|17)$/ && $6 ~ /P2/ {count++}
     ENDFILE { print FILENAME, count; count=0 }' *h3

Notiz:ENDFILE ist spezifisch für GNU awk. Es funktioniert nicht mit anderen Versionen von awk.

Und diese Version druckt auch eine Gesamtsumme aller Dateien:

awk 'BEGIN {OFS="\t"}
     FNR ~ /^(4|10|17)$/ && $6 ~ /P2/ {count++; total++}
     ENDFILE { print FILENAME, count; count=0 }
     END { print "---", total,"total" }' *h3

Der END{}Block druckt die Gesamtsumme und unternimmt außerdem einen groben Versuch, die tatsächliche Gesamtsumme von allen Dateien zu unterscheiden, die zufällig den Dateinamen „total“ haben. Dies geschieht, indem ---im ersten Feld, dann die Gesamtsumme und dann die Zeichenfolge totalim dritten Feld gedruckt wird. Dies ist bei weitem nicht perfekt, reicht aber in vielen Fällen aus. Es ist besser, als beispielsweise wcden Versuch überhaupt nicht zu unternehmen.

verwandte Informationen