Use o argumento de linha de comando e a variável com sed dentro de script de bash

0

Eu tenho um script bash que leva um monte de argumentos de linha de comando. O único que importa neste contexto é o primeiro, $ 1, que é um arquivo de texto.

O cabeçalho é muito longo, abaixo estão alguns exemplos de alguns campos.

COL0___LINE_NUMBER
COL1_AFF_ID
COL2_FULL_NAME
COL3_ADDRESS
BDID
BEST_STATE
COL48_LATITUDE   
COL49_LONGITUDE

Eu preciso alterar a linha de cabeçalho, o que eu posso fazer usando o código abaixo. Isso faz o que eu quero, no entanto, quaisquer mudanças estilísticas, etc., que preservem a variável como na saída abaixo, são bem-vindas, considerando que esta é minha primeira vez que o script 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: As abas para novas linhas são formatadas como tal, puramente como uma tentativa de estética quando o cabeçalho das colunas é ecoado. Isso é tanto para a legibilidade para mim quanto para os usuários do script para quem a vertica criar a instrução da tabela é ecoada.

De qualquer forma, agora quero tornar a variável de colunas a linha de cabeçalho do meu arquivo de texto para que eu possa trabalhar com a nova versão dentro do script. Então, eu gostaria do arquivo de texto original completo sem ser a linha de cabeçalho original, e com a que eu criei para que o seguinte, por exemplo, esteja se referindo à versão editada do meu arquivo,

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;"
    
por Audrey 06.11.2015 / 21:46

3 respostas

2

Podemos combinar todos os comandos do pipeline original columns= shell em um script sed . Este script sed modifica apenas a primeira linha da entrada e, em seguida, sai. O que segue é exatamente a mesma coisa que o columns= na pergunta 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"
)

# . . .

Eu prefiro o formato de várias linhas também para legibilidade. Mesmo que a declaração original estivesse em uma linha, era muito menos eficiente e, na minha opinião, mais difícil de ler. yomd

Agora você tem os cabeçalhos do arquivo de entrada (arg 1), armazenados na variável columns separados por novas linhas. Você pode iterar sobre as strings em $columns com um for loop, isso separará os nomes das colunas em cut_cols por novas linhas:

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

Dependendo das suas preferências, isso faz o mesmo:

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

Eu não testei o loop de sua matriz para cut_cols porque não uso matrizes de shell. O método acima de iterar sobre $columns é o método mais universal e tradicional. Array s é uma extensão, não disponível em todos os shell.

Depois de ter atribuído a cut_cols , você pode iterar o mesmo que $columns .

Para enviar um novo cabeçalho com os dados do arquivo original, imprima o novo cabeçalho e imprima todos, exceto a primeira linha do arquivo original. Faça isso em um grupo de comandos (entre { e } ) para que você possa redirecionar a saída de ambos os comandos como se fossem um único programa.

O texto a seguir produz o arquivo de texto original completo sem a linha de cabeçalho original e com o que você criou, e o envia ao stdin de 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;"
    
por 06.11.2015 / 22:34
1

Eu realmente não entendo muito dessa questão (especialmente a causa de editar apenas a linha do cabeçalho da coluna em um arquivo - o que acontece com todas as linhas que ele usou para identificar depois?) , mas esta parte faz 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.

Isso eu entendo. Aqui estão alguns truques de sed para extrair campos específicos de um arquivo:

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

Isso parece estranho, certo? Aqui sed remove a quinta seqüência possível de caracteres não espaciais, que calcula qualquer sequência de comprimento de caracteres não espaciais como um único campo - para incluir uma seqüência de comprimento zero. E então um é o primeiro campo, a próxima é a cadeia nula entre o espaço seguinte e o espaço que o segue, e o mesmo para os campos 3 e 4, e o quinto campo é 4 espaços. Muito gnarly, eu sei.

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

Não incluo uma correspondência definitiva para pelo menos um caractere não-espacial por campo e, portanto, sed se comporta mais como alguns outros programas podem. A coisa mais útil sobre expressões regulares, no entanto, e especialmente quando aplicada a edições, é que você pode adaptar muito especificamente o comportamento de sua saída, e manipular cadeias nulas é apenas uma parte disso.

    
por 09.11.2015 / 09:47
0

Ok, então eu percebi isso. A questão, que confundiu alguns, foi como eu pego minha linha de cabeçalho, edito algumas excentricidades em nomes de campos e preencho de volta no arquivo.

O que acabei fazendo:

  1. Editar linha de cabeçalho e atribuir a variável.
  2. Mantenha a linha de cabeçalho e o arquivo de texto restante separados o tempo todo.

Esta solução é em grande parte devido à natureza do script como uma ferramenta loader para uma tabela Vertica. Contanto que os mesmos campos sejam cortados da linha de cabeçalho e do arquivo, não importa se eles são sempre um arquivo novamente. Eu queria principalmente reunir o cabeçalho editado com seu conteúdo original para que eu pudesse salvar um arquivo de texto com a linha de cabeçalho correta no meu diretório e para que eu não precisasse cortar a linha de cabeçalho e o conteúdo separadamente. No entanto, acabei cortando-os separadamente assim,

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

Minha decisão de manter uma variável para colunas vem do uso desse script como um carregador. Criar uma tabela no Vertica requer uma instrução que identifique cada campo e seu tipo de dados. Eu faço isso executando a variável de colunas (linha de cabeçalho) por meio de algumas instruções if que preenchem uma variável com campos e tipos de dados em uma cadeia a ser usada na sintaxe de uma instrução de criação.

Depois, carreguei o member_temp.txt na tabela criada anteriormente. Não importa que não exista uma linha de cabeçalho, porque eu apenas a desnudaria de qualquer maneira, já que não quero armazená-la na minha tabela.

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;"
    
por 10.11.2015 / 21:41