Utilice el argumento de línea de comando y la variable con sed dentro del script Bash

Utilice el argumento de línea de comando y la variable con sed dentro del script Bash

Tengo un script bash que toma varios argumentos de línea de comando. El único que importa en este contexto es el primero, $1, que es un archivo de texto.

El encabezado es muy largo; a continuación se muestran ejemplos de algunos de los campos.

COL0___LINE_NUMBER
COL1_AFF_ID
COL2_FULL_NAME
COL3_ADDRESS
BDID
BEST_STATE
COL48_LATITUDE   
COL49_LONGITUDE

Necesito modificar la fila del encabezado, lo cual puedo hacer usando el siguiente código. Esto logra lo que quiero, sin embargo, cualquier cambio de estilo, etc. que preserve la variable como en el resultado a continuación es bienvenido considerando que esta es la primera vez que hago un script en bash.

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:]"`

Nota: Las pestañas de las nuevas líneas tienen el formato como tal simplemente como un intento de estética cuando se repite el encabezado de las columnas. Esto es tanto para facilitar la lectura para mí como para los usuarios del script para quienes se repite la declaración de creación de tabla vertical.

De todos modos, ahora quiero que las columnas sean variables en la fila del encabezado de mi archivo de texto para poder trabajar con la nueva versión dentro del script. Entonces, me gustaría el archivo de texto original completo.sines la fila de encabezado original, y con la que creé para que lo siguiente, por ejemplo, se refiera a la versión editada de mi archivo,

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;"

Respuesta1

Podemos combinar todos los comandos del columns=proceso de shell original en un solo sedscript. Este sedscript modifica solo la primera línea de la entrada y luego sale. Lo siguiente haceexactamentelo mismo que columns=en la pregunta original:

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"
)

# . . .

Prefiero el formato de varias líneas también para facilitar la lectura. Aunque la declaración original estaba en una sola línea, era mucho menos eficiente y, en mi opinión, más difícil de leer. yomd

Ahora tiene los encabezados del archivo de entrada (arg 1), almacenados en la variable columnsseparados por nuevas líneas. Puede iterar sobre las cadenas $columnscon un forbucle, esto separará los nombres de las columnas cut_colsmediante nuevas líneas:

cut_cols="$(
    for col in $columns
    do
        case $col in
        (*__LINE_NUMBER*|*CONFIDENCE*|*DROP_BDID*|*LINE_NUMBER*|*ZIP9*|*ZIP9MATCH*)
                echo "$col"
                ;;
        esac
    done
)"

Dependiendo de tus preferencias, esto hace lo mismo:

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')

No probé su bucle de matriz cut_colsporque no uso matrices de shell. El método anterior de iteración $columnses el método más universal y tradicional. Arrays son una extensión, no están disponibles en todos los shells.

Después de haber asignado a cut_cols, puede iterar sobre él de la misma manera que $columns.

Para enviar un nuevo encabezado con los datos del archivo original, imprima el nuevo encabezado y luego imprima todo menos la primera línea del archivo original. Haga esto en un grupo de comandos (entre {y }) para que pueda redirigir la salida de ambos comandos juntos como si fueran un solo programa.

Lo siguiente produce el archivo de texto original completo sin su fila de encabezado original y con la que usted creó, y lo envía stdina 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;"

Respuesta2

Realmente no entiendo mucho de esta pregunta.(especialmente la causa para editar solo la fila del encabezado de la columna en un archivo: ¿qué sucede con todas las filas que utilizó para identificar después?), pero esta parte tiene sentido:

        #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.

Eso lo entiendo. Aquí hay algunos sedtrucos para extraer campos específicos de un archivo:

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

one     three

Eso parece raro, ¿verdad? Aquí sedse quita el 5to.posiblesecuencia de caracteres sin espacios, que funciona para contar cualquier secuencia de longitud de caracteres sin espacios como un solo campo, para incluir una secuencia de longitud cero. Y entoncesunoes el primer campo, el siguiente es la cadena nula entre el espacio siguiente y el espacio que le sigue, y lo mismo ocurre con los campos 3 y cuatro, y el quinto campo tiene 4 espacios. Bastante retorcido, lo sé.

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

one     three

Ahí incluyo undefinidocoincide con al menos un carácter sin espacio por campo, por lo que sedse comporta más como lo harían otros programas. Sin embargo, lo útil de las expresiones regulares, y especialmente cuando se aplican a ediciones, es que puedes personalizar muy específicamente el comportamiento de tu salida, y el manejo de cadenas nulas es solo una parte de eso.

Respuesta3

Ok, entonces me di cuenta de esto. La pregunta, que confundió a algunos, era cómo tomo mi fila de encabezado, edito algunas excentricidades en los nombres de los campos y las antepongo nuevamente al archivo.

Lo que terminé haciendo:

  1. Edite la fila del encabezado y asígnela a la variable.
  2. Mantenga la fila del encabezado y el archivo de texto restante separados todo el tiempo.

Esta solución se debe en gran medida a la naturaleza del script como herramienta de carga para una tabla Vertica. Siempre que se eliminen los mismos campos de la fila del encabezado y del archivo, no importa si alguna vez vuelven a ser un solo archivo. Principalmente quería reunir el encabezado editado con su contenido original para poder guardar un archivo de texto con la fila del encabezado correcta en mi directorio y para no tener que cortar la fila del encabezado y el contenido por separado. Sin embargo, terminé cortándolos por separado así,

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

Mi decisión de mantener una variable para las columnas proviene del uso de este script como cargador. Crear una tabla en Vertica requiere una declaración que identifique cada campo y su tipo de datos. Hago esto ejecutando la variable de columnas (fila de encabezado) a través de algunas declaraciones if que completan una variable con campos y tipos de datos en una cadena que se usará en la sintaxis de una declaración de creación.

Luego cargué member_temp.txt en la tabla creada anteriormente. No importa que no haya una fila de encabezado porque de todos modos la quitaría, ya que no quiero que se almacene en mi tabla.

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;"

información relacionada