Ich muss ein Bash-Skript schreiben, das Symbole wie „<“ und „>“ von stdin zählt.
Zum Beispiel:
$ ./myscript.sh <example.html
> - 20
< - 21
Found mismatching brackets!
Ich tat dies:
x=`grep -o '>' example.html | wc -l`
y=`grep -o '<' example.html | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
Ist das ein guter Weg? Ich weiß nicht, wie ich den Dateinamen „example.html“ von stdin bekomme.
Antwort1
Der springende Punkt stdin
ist, dass es alles sein kann, es kann zum Beispiel eine Pipe sein, ein Netzwerk-Socket, eine normale Datei, ein Gerät, es kann mitten in einer normalen Datei sein, wenn Ihr Skript gestartet wird... Wenn Sie die Daten nicht in einem Durchgang verarbeiten können, beschränken Sie sich aufsuchbarDateien, also normale Dateien und einige Gerätedateien, oder Sie müssen die Informationen irgendwie speichern (in einer temporären Datei oder einem temporären Speicher ...). Es ist hier jedoch möglich, alle Informationen auf einmal abzurufen.
Sie könnten beispielsweise Folgendes tun:
$ grep -o '[<>]' < a.html | sort | uniq -c
82 <
82 >
POSIX:
fold -w 1 a.html | grep '[<>]' | sort | uniq -c
So erkennen Sie eine Nichtübereinstimmung:
if fold -w 1 a.html | awk '{n[$0]++}
END{exit(n["<"]!=n[">"])}'
then
echo match
else
echo mismatch
fi
Um nun die Frage im Betreff zu beantworten: Unter Linux können Sie einen „Namen“ für stdin wie folgt finden:
readlink -f /dev/stdin
Beispiel:
$ readlink -f /dev/stdin < a
/home/chazelas/a
$ : | readlink -f /dev/stdin
/proc/20238/fd/pipe:[758683]
(20238 oben ist die PID von readlink
, daher wird dieser Pfad nach dem Beenden nicht mehr von großem Nutzen sein readlink
, und das wäre er auch nicht, das pipe:[758683]
ist nur informativ, es kann nichtgeöffnet).
Und allgemeiner, wenn lsof
verfügbar:
lsof -ad0 -p "$$" -Fn 2> /dev/null | sed -n 'n;s/^n//p'
( $$
Da es sich allerdings um die PID des Prozesses handelt, der die Shell ausgeführt hat, würde es in Sub-Shells, deren Standardeingabe umgeleitet wurde, nicht funktionieren.)
Jetzt können Sie die Datei nicht unbedingt erneut zum Lesen öffnen und selbst wenn Sie dies tun, erhalten Sie beim Lesen aus der Datei möglicherweise nicht wieder dieselben Daten (denken Sie beispielsweise an Pipes).
$ seq 3 > a
$ { cat; cat /dev/stdin; } < a
1
2
3
1
2
3
$ cat a | { cat; cat /dev/stdin; }
1
2
3
Unter Linux wird beim Öffnen von /dev/stdin
stdin eine reguläre Datei die Datei erneut vom Anfang an gelesen, während auf anderen Systemen das Öffnen von /dev/stdin eher einem entspricht dup(0)
, d. h. die Datei wird nicht an den Anfang zurückgespult (im ersten Beispiel oben würde die Ausgabe 1\n2\n3\n
einmal statt zweimal erfolgen).
Antwort2
Sie müssen den Dateiinhalt irgendwie speichern. Sie können eine Variable verwenden.
content=`cat`
x=`echo "$content" | grep -o '>' | wc -l`
y=`echo "$content" | grep -o '<' | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
Oder eine temporäre Datei (erforderlich, wenn example.html
sie Null-Bytes enthält).
tmp=`mktemp`
trap "rm $tmp" EXIT
x=`grep -o '>' "$tmp" | wc -l`
y=`grep -o '<' "$tmp" | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
Wenn das Lesen des Dateiinhalts von stdin nicht erforderlich ist, können Sie den Dateinamen als Argument an das Skript übergeben.
x=`grep -o '>' "$1" | wc -l`
y=`grep -o '<' "$1" | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
Rufen Sie das Skript folgendermaßen auf:
$ ./myscript.sh example.html
Antwort3
Eine Möglichkeit für Ihre Aufgabe ist:
#!/bin/bash
if [[ -n $1 ]]; then
if [[ ! -f $1 ]] || [[ ! -r $1 ]]; then
echo >&2 "File \`$1' not found or not readable."
exit 1
fi
exec "$0" < "$1"
fi
declare -A hary
while read c; do
(( ++hary[$c] ))
done < <(grep -o '[<>]')
echo "> ${hary[>]}"
echo "< ${hary[<]}"
Wenn Sie dieses Skript aufrufenAnzahlnichtübereinstimmend(Sie können einen kürzeren Namen wählen), Sie können es mit oder ohne Dateinamen verwenden. Einige Möglichkeiten:
$ countmismatched example.html
$ countmismatched < example.html
$ cat example.html | countmismatched
Die Ausgabe sieht ungefähr so aus:
> 41
< 42
Wenn Sie Nichtübereinstimmungen erkennen müssen, fügen Sie am Ende des Skripts Folgendes hinzu:
if (( hary[<]} != hary[>] )); then
echo "Mismatched brackets"
else
echo "It's all good"
fi
oder etwas deutlicher:
((difference=hary[<]-hary[>]))
if (( difference>0 )); then
echo "Mismatched brackets: you have $difference more <'s than >'s"
elif (( difference<0 )); then
echo "Mismatched brackets: you have $((-difference)) more >'s than <'s"
else
echo "It's all good"
fi