Verwenden Sie Befehlszeilenargumente und Variablen mit sed im Bash-Skript

Verwenden Sie Befehlszeilenargumente und Variablen mit sed im Bash-Skript

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 sedSkript zusammenfassen. Dieses sedSkript ä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 columnsdurchlaufen , wodurch die Spaltennamen in durch Zeilenumbrüche getrennt werden:$columnsforcut_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_colsda ich keine Shell-Arrays verwende. Die obige Iterationsmethode $columnsist die universellere und traditionellere Methode. Arrays 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 stdinan 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 sedTricks zum Extrahieren bestimmter Felder aus einer Datei:

printf 'one    two three' |
sed    's|[^ ]*||5'

one     three

Das sieht komisch aus, oder? Hier sedentfernt 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 sedverhä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:

  1. Kopfzeile bearbeiten und einer Variablen zuweisen.
  2. 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;"

verwandte Informationen