
Ich habe ein Bash-Skript, das eine Reihe von Befehlszeilenargumenten akzeptiert. Das einzige, das in diesem Zusammenhang wichtig ist, ist das erste, $1, das eine Textdatei ist.
Die Kopfzeile ist sehr lang. Nachfolgend finden Sie Beispiele für einige der Felder.
COL0___LINE_NUMBER
COL1_AFF_ID
COL2_FULL_NAME
COL3_ADDRESS
BDID
BEST_STATE
COL48_LATITUDE
COL49_LONGITUDE
Ich muss die Kopfzeile ändern, was ich mit dem folgenden Code tun kann. Dies erreicht mein Ziel, aber alle stilistischen Änderungen usw., die die Variable wie in der folgenden Ausgabe beibehalten, sind willkommen, da dies mein erstes Bash-Skripting ist.
columns=`cat $1 | head -1 |sed 's/-/_/g' | sed 's/ /_/g' |
sed 's/COL[0-9]\+_BDID/DROP_BDID/g' | sed 's/COL[0-9]\+_//g' |
tr '\t' '\n' | tr "[:lower:]" "[:upper:]"`
Hinweis: Die Tabulatoren für Zeilenumbrüche sind als solche formatiert, nur um die Ästhetik zu verbessern, wenn die Spaltenüberschrift wiedergegeben wird. Dies dient sowohl der Lesbarkeit für mich als auch für die Benutzer des Skripts, für die die Anweisung „vertica create table“ wiedergegeben wird.
Wie dem auch sei, ich möchte nun die Spaltenvariable zur Kopfzeile meiner Textdatei machen, damit ich mit der neuen Version im Skript arbeiten kann. Ich hätte also gerne die komplette Originaltextdateiohnedie ursprüngliche Kopfzeile und die von mir erstellte, so dass sich beispielsweise die folgenden Zeilen auf die bearbeitete Version meiner Datei beziehen,
col_arr=($columns)
cut_cols = ""
for i in ${!col_arr[@]}; do
if [[ "${col_arr[$i]}" =~ ^(__LINE_NUMBER|CONFIDENCE|DROP_BDID|LINE_NUMBER|ZIP9|ZIP9|ZIP9MATCH)$ ]]; then
echo "$i"
#haven't written yet, but this will add to cut_cols so that
#I can remove the above listed columns in the text file
#based on their index.
fi
done
/opt/vertica/bin/vsql -U ${4} -w ${5} -h ${database} \
-c "copy $schema.$table from STDIN delimiter E'\t' direct no escape;"
Antwort1
Wir können alle Befehle der ursprünglichen columns=
Shell-Pipeline in einem sed
Skript zusammenfassen. Dieses sed
Skript ändert nur die erste Zeile der Eingabe und wird dann beendet. Folgendes funktioniertgenaudas gleiche wie columns=
in der ursprünglichen Frage:
columns=$(
sed '
1 { # execute block on line 1
s/-/_/g
s/ /_/g
s/COL[0-9]\+_BDID/DROP_BDID/g
s/COL[0-9]\+_//g
s/\t/\n/g
y/abcdefghijklmnopqrstuv/ABCDEFGHIJKLMNOPQRSTUV/
q # quit after line 1
}
' "$1"
)
# . . .
Ich bevorzuge das mehrzeilige Format auch aus Gründen der Lesbarkeit. Obwohl die ursprüngliche Anweisung nur eine Zeile umfasste, war sie viel weniger effizient und meiner Meinung nach schwieriger zu lesen. yomd
Jetzt haben Sie die Header aus der Eingabedatei (Argument 1), die in der Variablen durch Zeilenumbrüche getrennt gespeichert sind . Sie können die Zeichenfolgen in mit einer Schleife columns
durchlaufen , wodurch die Spaltennamen in durch Zeilenumbrüche getrennt werden:$columns
for
cut_cols
cut_cols="$(
for col in $columns
do
case $col in
(*__LINE_NUMBER*|*CONFIDENCE*|*DROP_BDID*|*LINE_NUMBER*|*ZIP9*|*ZIP9MATCH*)
echo "$col"
;;
esac
done
)"
Abhängig von Ihren Präferenzen bewirkt dies dasselbe:
cut_cols=
for col in $columns
do
case $col in
(*__LINE_NUMBER*|*CONFIDENCE*|*DROP_BDID*|*LINE_NUMBER*|*ZIP9*|*ZIP9MATCH*)
cut_cols="$cut_cols $col"
;;
esac
done
cut_cols=$(echo "$cut_cols" | sed 's/^ *//; s/ /\n/g')
Ich habe Ihre Array-Schleife nicht getestet, cut_cols
da ich keine Shell-Arrays verwende. Die obige Iterationsmethode $columns
ist die universellere und traditionellere Methode. Array
s sind eine Erweiterung, die nicht in jeder Shell verfügbar ist.
Nachdem Sie es zugewiesen haben cut_cols
, können Sie es auf die gleiche Weise wie durchlaufen $columns
.
Um einen neuen Header mit den Originaldateidaten zu senden, drucken Sie den neuen Header und dann die gesamte Originaldatei außer der ersten Zeile. Tun Sie dies in einer Befehlsgruppe (zwischen {
und }
), damit Sie die Ausgabe beider Befehle gemeinsam umleiten können, als wären sie ein Programm.
Folgendes erstellt die vollständige Originaltextdatei ohne die ursprüngliche Kopfzeile und mit der von Ihnen erstellten und sendet sie stdin
an vsql
:
# . . .
{ # start command group
echo "$columns" | tr '\n' '\t'; # print with tabs instead of newlines
echo # add newline record separator
sed 1d "$1" # print all but 1st line of "$1"
} | # pipe as one file to vsql
/opt/vertica/bin/vsql -U ${4} -w ${5} -h ${database} \
-c "copy $schema.$table from STDIN delimiter E'\t' direct no escape;"
Antwort2
Ich verstehe diese Frage wirklich nicht.(insbesondere der Grund für das Bearbeiten nur der Spaltenkopfzeile in einer Datei – was passiert anschließend mit allen Zeilen, die zur Identifizierung verwendet wurden?), aber dieser Teil macht Sinn:
#haven't written yet, but this will add to cut_cols so that
#I can remove the above listed columns in the text file
#based on their index.
Das verstehe ich. Hier sind ein paar sed
Tricks zum Extrahieren bestimmter Felder aus einer Datei:
printf 'one two three' |
sed 's|[^ ]*||5'
one three
Das sieht komisch aus, oder? Hier sed
entfernt man die 5.möglichFolge von Zeichen ohne Leerzeichen, was bedeutet, dass jede beliebige Folge von Zeichen ohne Leerzeichen als ein einzelnes Feld gezählt wird - einschließlich einer Folge mit der Länge Null. Und soeinsist das erste Feld, „next“ ist die Nullzeichenfolge zwischen dem folgenden Leerzeichen und dem darauf folgenden Leerzeichen, und das Gleiche gilt für die Felder 3 und 4, und das fünfte Feld hat 4 Leerzeichen. Ziemlich knifflig, ich weiß.
printf 'one two three' |
sed 's|[^ ][^ ]*||2'
one three
Dort füge ich einbestimmtÜbereinstimmung für mindestens ein Nicht-Leerzeichen pro Feld und sed
verhält sich daher eher wie einige andere Programme. Das Praktische an regulären Ausdrücken, insbesondere bei der Anwendung auf Bearbeitungen, ist jedoch, dass Sie das Verhalten Ihrer Ausgabe sehr spezifisch anpassen können, und die Verarbeitung von Nullzeichenfolgen ist nur ein Teil davon.
Antwort3
Ok, also habe ich das herausgefunden. Die Frage, die einige verwirrte, war, wie ich meine Kopfzeile nehme, einige Eigenheiten in den Feldnamen bearbeite und sie der Datei wieder voranstelle.
Was ich letztendlich gemacht habe:
- Kopfzeile bearbeiten und einer Variablen zuweisen.
- Halten Sie die Kopfzeile und die restliche Textdatei die ganze Zeit getrennt.
Diese Lösung beruht größtenteils auf der Natur des Skripts als Ladetool für eine Vertica-Tabelle. Solange dieselben Felder aus der Kopfzeile und der Datei ausgeschnitten werden, spielt es keine Rolle, ob sie jemals wieder eine Datei sind. Ich wollte hauptsächlich die bearbeitete Kopfzeile mit ihrem ursprünglichen Inhalt wieder vereinen, damit ich eine Textdatei mit der richtigen Kopfzeile in meinem Verzeichnis speichern konnte und die Kopfzeile und den Inhalt nicht separat ausschneiden musste. Am Ende habe ich sie jedoch separat ausgeschnitten, und zwar so:
col_arr=($columns)
cut_cols=""
for i in ${!col_arr[@]}; do
if ! [[ "${col_arr[$i]}" =~ ^(__LINE_NUMBER|CONFIDENCE|DROP_BDID|LINE_NUMBER|ZIP9|ZIP9|ZIP9MATCH)$ ]]; then
ind=$(($i+1))
cut_cols="$cut_cols,$ind"
fi
done
cut_cols=$(echo $cut_cols | sed s/^,//g)
columns=$(echo "$columns" | cut -f "$cut_cols")
cut -f ${cut_cols} ${1}>member_temp.txt
sed -i 1d member_temp.txt
Meine Entscheidung, eine Variable für Spalten beizubehalten, beruht auf der Verwendung dieses Skripts als Ladeprogramm. Zum Erstellen einer Tabelle in Vertica ist eine Anweisung erforderlich, die jedes Feld und seinen Datentyp identifiziert. Dazu führe ich die Spaltenvariable (Kopfzeile) durch einige if-Anweisungen, die eine Variable mit Feldern und Datentypen in einer Zeichenfolge füllen, die in der Syntax für eine Create-Anweisung verwendet werden soll.
Ich habe dann einfach member_temp.txt in die zuvor erstellte Tabelle geladen. Dass keine Kopfzeile vorhanden ist, spielt keine Rolle, da ich sie sowieso entfernen würde, da ich sie nicht in meiner Tabelle speichern möchte.
cat member_temp.txt | /opt/vertica/bin/vsql -U ${4} -w ${5} -h ${database} \
-c "copy $schema.$table from STDIN delimiter E'\t' direct no escape;"