Eu tenho dois arquivos, um arquivo contém uma lista de strings.
+stringa +Dog +Cat
+cat +Tux +elephant
e o segundo arquivo (csv) contém algo como:
"123456 Abc","+Stringx +123","something"
"23456 dEf","+cat +Tux +elephant","Other something"
"34524 xyz","+stringa +Dog +Cat","third something"
o resultado deve ser:
"123456 Abc","+Stringx +123","something"
"23456 dEf","+cat +tux +elephant","Other something"
"34524 xyz","+stringa +dog +cat","third something"
Como posso alterar as strings que correspondem à minha lista de padrões para minúsculas?
Meu arquivo de valores separados por vírgula tem cerca de 30 colunas e cerca de 1.500 linhas.
Responder1
Com GNU sed
, assume que você não possui nenhum metacaractere na lista de strings, +
não é um metacaractere com BRE padrão
$ # create substitute command for each line
$ sed 's/.*/s|"&"|\\L\&|gi/' f1
s|"+stringa +Dog +Cat"|\L&|gi
s|"+cat +Tux +elephant"|\L&|gi
$ # pass those commands as sed script
$ sed -f <(sed 's/.*/s|"&"|\\L\&|gi/' f1) ip.csv
"123456 Abc","+Stringx +123","something"
"23456 dEf","+cat +tux +elephant","Other something"
"34524 xyz","+stringa +dog +cat","third something"
$ # or save them in a file and use
$ sed 's/.*/s|"&"|\\L\&|gi/' f1 > f2
$ sed -f f2 ip.csv
\L
para converter string em minúsculasg
para substituir todas as ocorrências em uma linha,i
para correspondência sem distinção entre maiúsculas e minúsculas
Se você não temGNU sed
$ # \Q to quote metacharacters
$ # but will have issues if you have \ or $ or @
$ sed 's/.*/s|\\Q"&"|\\L$\&|gi;/' f1
s|\Q"+stringa +Dog +Cat"|\L$&|gi;
s|\Q"+cat +Tux +elephant"|\L$&|gi;
$ perl -p <(sed 's/.*/s|\\Q"&"|\\L$\&|gi;/' f1) ip.csv
"123456 Abc","+Stringx +123","something"
"23456 dEf","+cat +tux +elephant","Other something"
"34524 xyz","+stringa +dog +cat","third something"
Conforme observado por Stéphane Chazelas, isso pode levar a vulnerabilidades de injeção de código se o conteúdo f1
não estiver sob controle
Responder2
Com perl
, supondo que você queira cadapalavrano primeiro arquivo a ser colocado em letras minúsculas:
perl -pe '
BEGIN {local $/ = undef; $regex = join "|", map qr{\Q$_\E}i, split " ", <>}
s/$regex/\L$&/g' file1.words file2.csv
local $/ = undef
torna o separador de registro para o bloco BEGIN indefinido, de modo que aquela invocação <>
dele, sorve todo o primeiro arquivo ( file1.words
). Dividimos isso em espaço em branco ( split " "
é especial in perl
da mesma forma que awk -F " "
está em awk
) e juntamos as palavras resultantes com |
depois de tercitado em regexe os tornou insensíveis a maiúsculas e minúsculas.
Portanto, temos um regexp enorme que é algo parecido (?i:word1)|(?i:word2)|...
com o que aplicamos em cada linha do segundo arquivo no restante do código.
Se for cada string em cadalinhado primeiro arquivo, então isso pode ser simplificado para:
perl -pe '
BEGIN {chomp (@strings = <STDIN>); $regex = join "|", map qr{\Q$_\E}i, @strings}
s/$regex/\L$&/g' < file1.strings file2.csv
Lá, abrimos o primeiro arquivo no stdin em vez de passá-lo como argumento. <STDIN>
retorna uma lista de suas linhas das quais removemos os delimitadores com chomp
e juntamos |
como acima.
Se você não quiser que seja limitado a caracteres ASCII, adicione a -Mopen=locale
opção.
Responder3
AWK
solução (para sua entrada atual):
Supondo que o segundo campo seja de interesse principal e os valores no arquivo de pesquisa estejam entre aspas.
awk 'NR==FNR{ $0="\042"$0"\042"; a[$0]; next }
$2 in a{ $2=tolower($2) }1' patterns FS=',' OFS=',' file.csv
$0="\042"$0"\042"
- embrulhe umpadrãolinha com aspas duplas enquanto itera pelas linhas dopatterns
arquivoa[$0]
- capturando umpadrãolinha em arraya
$2 in a{ $2=tolower($2) }
- se o valor do segundo campo da linha dofile.csv
arquivo estiver na lista de padrões (ou seja, arraya
) - converta todos os caracteres nele para letras minúsculas$2=tolower($2)
A saída:
"123456 Abc","+Stringx +123","something"
"23456 dEf","+cat +tux +elephant","Other something"
"34524 xyz","+stringa +dog +cat","third something"