Precisa inserir aspas simples no arquivo de texto para usar como consulta SQL usando sed

3

Eu criei uma linha de texto que se parece com isso:

INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,,00:23:32:c2:a9:e8,Auth-Type,:=,Accept);

Eu quero usar o sed para parecer com isso:

INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');

Isso faz muito mais sentido no contexto e eu fiquei um pouco mais longe nas últimas (aparentemente) 17 horas:

#!/bin/bash
ssh [email protected] brmacs >>MACS.txt mv MACS.txt /etc/persistent scp [email protected]:/etc/persistent/MACS.txt MACS.txt
sed -i "1d" MACS.txt
head -c 58 MACS.txt >>shortmacs.txt
tail -c 18 shortmacs.txt >>usermac.txt
sed 's/"//g' usermac.txt >>usermacrdy.txt
sed -i 's/^/INSERT INTO 'radcheck'('id', 'username', 'attribute', 'op', 'value') VALUES (,'',/' usermacrdy.txt
sed "s/$/','Auth-Type',':=','Accept');/" usermacrdy.txt > sqlquery.txt
sed -i "s/,,/\,\'\',\'/" sqlquery.txt
rm -f MACS.txt
rm -f shortmacs.txt
rm -f usermac.txt
rm -f usermacrdy.txt

FUNCIONA !!!

A cabeça e a cauda cortaram o endereço MAC do arquivo de texto original do dispositivo CPE da UBNT e depois passei-o pelo sed para construir a sintaxe SQL em torno do endereço MAC.

Depois de tudo isso, descobri que a parte id da consulta não é necessária para o sucesso, então agora estou no mesmo barco com:

sed -i 's/^/INSERT INTO 'radcheck'('username', 'attribute', 'op', 'value') VALUES (/' usermacrdy.txt
sed "s/$/','Auth-Type',':=','Accept');/" usermacrdy.txt > sqlquery.txt
sed -i "s/\,\'\',\'//" sqlquery.txt
    
por Michael Conrad Halls 03.01.2015 / 06:53

3 respostas

9

Existem quatro maneiras de incluir a cotação simples de que você precisa.

Não é possível escapar de uma string de aspas simples dentro de uma string de aspas simples. No entanto, pode-se terminar a string entre aspas, inserir um aspas simples com escape e, em seguida, iniciar uma nova string entre aspas simples. Assim, para colocar uma aspa simples no meio de 'ab' , use: 'a'\''b' . Ou, usando o comando sed que você precisa:

$ sed -r 's/,([^ ),]+)/,'\'''\''/g; s/,,/,'\'\'',/g' file
INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');

A segunda maneira é usar uma string com aspas duplas, em cujo caso a aspa simples pode ser inserida facilmente:

$ sed -r "s/,([^ ),]+)/,''/g; s/,,/,'',/g" file
INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');

Esse problema com cadeias de caracteres com aspas duplas é que o shell faz o processamento nelas. Aqui, no entanto, não há caracteres ativos no shell, por isso é fácil.

O terceiro método é usar um escape hexadecimal como PM2Ring demonstra.

A quarta maneira, sugerida nos comentários de Jonathan Leffler, é colocar os comandos sed em um arquivo separado:

$ cat script.sed 
s/,([^ ),]+)/,''/g
s/,,/,'',/g
$ sed -rf script.sed file
INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');

Desta forma, tem a grande vantagem de que sed lê os comandos diretamente, sem qualquer interferência do shell. Conseqüentemente, isso evita completamente a necessidade de escapar de caracteres ativos em shell e permite que os comandos sejam inseridos na sintaxe sed pura.

Como a solução sed funciona

O truque é colocar aspas simples em torno das sequências separadas por vírgulas que você deseja, mas não em torno das outras. Com base no único exemplo que você deu, aqui está uma abordagem:

  • s/,([^ ),]+)/,''/g

    Isso procura por um ou mais caracteres não espaciais, sem vírgula e sem parentesco, que seguem uma vírgula. Esses caracteres são colocados entre aspas simples.

  • s/,,/,'',/g

    Isso procura por vírgulas consecutivas e coloca duaspas simples entre elas.

OSX e outras plataformas BSD

Para evitar barras invertidas extras, as expressões sed acima usam expressões regulares estendidas. Com o GNU, elas são chamadas como -r , mas, com o BSD, elas são invocadas com -E . Além disso, alguns não-GNU sed não aceitam vários comandos separados por ponto e vírgula. Assim, no OSX, tente:

sed -E -e "s/,([^ ),]+)/,''/g" -e "s/,,/,'',/g" file

Adendo: combinando um endereço MAC

Nos comentários, temos a seguinte entrada:

$ cat file3
 INSERT INTO radcheck(username, attribute, op, value) VALUES (00:23:32:c2:a9:e8,'Auth-Type',':=','Accept'); 

E queremos colocar aspas simples em torno do endereço MAC que segue os parênteses abertos. Para fazer isso:

$ sed -r "s/\(([[:xdigit:]:]+)/(''/" file3
 INSERT INTO radcheck(username, attribute, op, value) VALUES ('00:23:32:c2:a9:e8','Auth-Type',':=','Accept'); 

Em qualquer localidade, [:xdigit:] corresponderá a qualquer dígito hexadecimal. Assim, ([[:xdigit:]:]+) corresponderá a um endereço MAC (dígito hexadecimal ou dois pontos).

    
por 03.01.2015 / 08:30
2

Quando você trabalha globalmente, precisa considerar como sed é lido. Uma substituição g lobal vai dividir um espaço de padrão em campos individuais delimitados por suas especificações e operar em cada um deles. Os delimitadores são reconhecidos da esquerda para a direita - na ordem em que são lidos - e sed aplicará cada ação assim que possível. Aqui está uma única declaração de s/// ubstitution que pode fazer o que você está perguntando:

sed "s/\(([^)]*)[^,]*\)\{0,1\},\([^,)]*\)/,''/g
" <<\IN
INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,,00:23:32:c2:a9:e8,Auth-Type,:=,Accept);

Quando executado, imprime:

INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');

Veja como:

  • \(([^)]*)[^,]*\)\{0,1\} - O mais importante é a distribuição de * zero + correspondências e o outro tipo. Este bloco inteiro é opcional - sed ficará feliz em combinar com uma string nula em seu lugar, dada a oportunidade - e, na verdade, faz a maior parte do tempo, senão isso não funcionaria. Mas porque \{0,1\} é usado sed não corresponderá mais a uma sequência inteira como essa. E é por isso que a distribuição é importante. Dentro deste bloco temos:
    • ( - pelo menos um parente à esquerda.
    • [^)]* - zero + parentes não-direitos.
    • ) - um parente certo
    • [^,]* - zero + não-vírgulas.
  • sed corresponderá a cada um deles em ordem - e como um bloco, porque eles são \( grouped \) . A correspondência * zero + é muito importante globalmente - se você usar + , pelo menos uma nessa configuração, você reduz drasticamente a eficácia das edições aplicadas. Para apenas inserir o bloco acima, sed delimita sua string assim:

  • INSERT INTO radcheck - Esta string vem antes de qualquer uma das nossas correspondências e não contém nenhuma delas. É ignorado.

  • (id, username, attribute, op, value) VALUES ( - Esta string compõe todo o nosso bloco de correspondência opcional * . O primeiro sed corresponde ao ( e depois à cadeia inteira de [^)]* e depois ao ) e à última - e importante - à cadeia de [^,]* que nos transporta além de outro caso de correspondência %código%. Ele não pode corresponder agora e, portanto, pular o próximo ( para esse grupo de correspondência opcional. ( tentará o próximo.

E enquanto sed aplica as substituições de sed na segunda sequência , ainda aplica as substituições de (.,.,.) , porque ainda corresponde . Primeiro, combinei como o bloco inteiro , salvei-o em (.,.,.)..( e o substitui por - com ele mesmo. Do início da segunda sequência até a próxima ocorrência ( substituirá sed pela string nula toda vez que corresponder a uma - sempre que corresponder a uma vírgula.

Dado muitas sequências como essa em uma linha - e considerando que as vírgulas ocorrem apenas dentro dos delimitadores ( , ) alternará as citações para cada sequência. Aqui está sua amostra várias vezes:

INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');
  • sed - observe que a única correspondência opcional não para toda a regex é a ,\([^,)]*\) comma. O outro bloco ocorre antes dele na expressão e na entrada e assim , permite que ele se aplique - mas a vírgula tem precedência quando todo o resto é igual, e é a única razão pela qual sed não insere sed s em todos os lugares como ele corresponde a todas as cadeias nulas entre cada byte nos outros dois blocos, não corresponde a uma cadeia nula ao aplicar - '' delimita a ele.
  • sed continuará a corresponder sed - e salvará a ele - cada sequência de caracteres que não forem e não ) . Ele substituirá cada vírgula pelos encontros com , . Ele continuará desta forma até encontrar ,'' , altura em que poderá preencher ( e assim o fará.
por 03.01.2015 / 12:47
1

Tente isto:

sed 's/,,/,\x27\x27,/'

\x27 é o código de escape hexadecimal para '

demonstração

$ echo 'INSERT INTO 'radcheck'('id', 'username', 'attribute', 'op', 'value') VALUES (,,00:23:32:c2:a9:e8,Auth-Type,:=,Accept);' |
> sed 's/,,/,\x27\x27,/'
INSERT INTO 'radcheck'('id', 'username', 'attribute', 'op', 'value') VALUES (,'',00:23:32:c2:a9:e8,Auth-Type,:=,Accept);
    
por 03.01.2015 / 08:04