
Ich schreibe ein Skript, das die ersten 3 Buchstaben jeder Zeile (mithilfe von Cut) in einer Datei mit Zeichenfolgen in einem Array vergleicht. Ich habe mich bereits umgesehen, aber die Lösungen, die ich gefunden habe, funktionierten auf meinem System nicht.
Im Moment sieht es so aus:
weekdays=([Mon]=1 [Tue]=1 [Wed]=1 [Thu]=1 [Fri]=1 [Sat]=1 [Sun]=1)
input="/Foo/Bar.log"
while read -r line
do
cutline="$(echo ${line} | cut -c 1-3"
if [[ ${weekdays["$cutline"]} ]]
then
echo "Match"
else
echo "No Match"
fi
done < ${input}
Die Linie wird ordnungsgemäß abgeschnitten, aber während des Tests wird ein falsches Positiv zurückgegeben, da unabhängig von den ersten drei Buchstaben eine „Übereinstimmung“ zurückgegeben wird.
Als ich das Skript mit -x überprüfte, zeigte es mir anstelle des eigentlichen Tests, dass es stattdessen verwendet wurde
[[ -n 1 ]]
Und als ich es mit dem [ ]
Ausdruck getestet habe, zeigte es ein1
Wird jedes einzelne Zeichen im Array geprüft und nicht nur die ganzen Wörter, oder stimmt etwas anderes damit nicht?
Wenn kein Problem besteht, gibt es eine andere Möglichkeit, die ersten drei Buchstaben einer Zeile mit dem gesamten Inhalt eines Arrays zu vergleichen, bevor mit dem nächsten fortgefahren wird?
Als Randbemerkung: Ich verwende tatsächlich Bash 4, also sollten assoziative Arrays funktionieren
Antwort1
Der grundlegende Fehler besteht darin, dass Sie tatsächlich kein assoziatives Array deklarieren:
$ weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)
$ echo ${weekdays[@]}
1
$ echo ${weekdays[0]}
1
$ echo ${weekdays[2]}
$
Ich bin mir nicht ganz sicher, wie Bash damit umgeht und warum es einfach ein nimmt 1
, aber ich bin sicher, dass dies kein assoziatives Array ist. Wie in man bash
(Hervorhebung von mir) erklärt:
Ein indiziertes Array wird automatisch erstellt, wenn eine beliebige Variable mit der Syntax Name[Index]=Wert zugewiesen wird. Der Index wird als arithmetischer Ausdruck behandelt, der eine Zahl ergeben muss. Um ein indiziertes Array explizit zu deklarieren, verwenden Sie „declare -a name“ (siehe unten IN DIE SHELL EINGEBAUTES BEFEHL). „declare -a name[Index]“ wird ebenfalls akzeptiert; der Index wird ignoriert.
Assoziative Arrays werden mit „declare -A name“ erstellt.
Versuchen Sie stattdessen Folgendes und es wird wie erwartet funktionieren:
declare -A weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)
Allerdings ist Ihr Skript etwas komplexer als nötig. Hier ist eine einfachere Version mit demselben Ansatz:
#!/bin/bash
declare -A weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)
input="/Foo/Bar.log"
cut -c 1-3 "$input" | while read -r line; do
if [[ ${weekdays["$line"]} ]]
then
echo "Match : $cutline : ${weekdays[$line]}"
else
echo "No Match"
fi
done
Obwohl ich es wahrscheinlich so machen würde:
#!/bin/bash
cut -c 1-3 "$1" | while read -r line; do
case $line in
"Mon"|"Tue"|"Wed"|"Thu"|"Fri"|"Sat"|"Sun")
echo yes;;
*)
echo no;;
esac
done
Führen Sie dann das Skript mit dem Zieldateinamen als Argument aus:
script.sh /Foo/Bar.log"
Antwort2
Ich würde einen Aufruf eines Textverarbeitungstools verwenden, um Text zu verarbeiten, nicht mehrere Tools für jede Eingabezeile:
awk -v 'weekday=(Mon|Tue|Wed|Thu|Fri|Sat|Sun)' '
{print ($0 ~ "^" weekday ? "" : "No ") "Match"}' < "$input"
Sie würden eine Schleife verwenden, wenn Sie für jede Eingabezeile eine bestimmte Anwendung ausführen müssten. Wenn es sich jedoch nur um Textverarbeitung handelt, z. B. um die Ausgabe der Zeile in eine Datei, awk
können Sie es folgendermaßen machen:
awk -v 'weekday=Mon|Tue|Wed|Thu|Fri|Sat|Sun' '
(day = substr($0, 1, 3)) ~ weekday {
print substr($0, 4) > day ".txt"
} < "$input"