Как иметь sh определенный столбец в CSV-файле | linux | - PullRequest
1 голос
/ 18 февраля 2020

У меня есть сценарий

, где я хочу иметь sh некоторые столбцы файла CSV

как это сделать с данными ниже

ID|NAME|CITY|AGE
1|AB1|BBC|12
2|AB2|FGD|17
3|AB3|ASD|18
4|AB4|SDF|19
5|AB5|ASC|22

Столбец имя ИМЯ | AGE должен хэшироваться со случайными значениями

как показано ниже:

ID|NAME|CITY|AGE
1|68b329da9111314099c7d8ad5cb9c940|BBC|77bAD9da9893er34099c7d8ad5cb9c940
2|69b32fga9893e34099c7d8ad5cb9c940|FGD|68bAD9da989yue34099c7d8ad5cb9c940
3|46b329da9893e3403453d8ad5cb9c940|ASD|60bfgD9da9893e34099c7d8ad5cb9c940
4|50Cd29da9893e34099c7d8ad5cb9c940|SDF|67bAD9da98973e34099c7d8ad5cb9c940
5|67bAD9da9893e34099c7d8ad5cb9c940|ASC|67bAD9da11893e34099c7d8ad5cb9c940

Когда я проверял этот код ниже, код дает мне то же значение для столбца 'NAME ' он должен давать рандомизированные значения

awk '{
    tmp="echo " $2 " | openssl md5 | cut -f2 -d\" \""
tmp | getline cksum
close(tmp)
$2=cksum
print
}' < sample.csv 

вывод:

 68b329da9893e34099c7d8ad5cb9c940
 68b329da9893e34099c7d8ad5cb9c940
 68b329da9893e34099c7d8ad5cb9c940
 68b329da9893e34099c7d8ad5cb9c940
 68b329da9893e34099c7d8ad5cb9c940
 68b329da9893e34099c7d8ad5cb9c940

Ответы [ 4 ]

2 голосов
/ 18 февраля 2020

Вы можете использовать это так:

awk 'function hash(s, cmd, hex, line) {
   cmd = "openssl md5 <<< \"" s "\""
   if ( (cmd | getline line) > 0)
      hex = line
   close(cmd)
   return hex
}
BEGIN {
   FS = OFS = "|"
}
NR == 1 {
   print
   next
}
{
   print $1, hash($2), $3, hash($4)
}' file

ID|NAME|CITY|AGE
1|d44aec35a11ff6fa8a800120dbef1cd7|BBC|2737b49252e2a4c0fe4c342e92b13285
2|157aa4a48373eaf0415ea4229b3d4421|FGD|4d095eeac8ed659b1ce69dcef32ed0dc
3|ba3c08d4a65f1baa1d7220a6802b5710|ASD|cf4278314ef8e4b996e1b798d8eb92cf
4|69be622e1c0d417ceb9b8fb0aa9dc574|SDF|3bb50ff8eeb7ad116724b56a820139fa
5|427872b1ac3a22dc154688ddc2050516|ASC|2fc57d6f63a9ee7e2f21a26fa522e3b6
1 голос
/ 18 февраля 2020

Пример использования GNU datama sh для хэширования и некоторого awk для перестановки столбцов, которые он выводит:

$ datamash -t'|' --header-in -f md5 2,4 < input.txt | awk 'BEGIN { FS=OFS="|"; print "ID|NAME|CITY|AGE" } { print $1, $5, $3, $6 }'
ID|NAME|CITY|AGE
1|1109867462b2f0f0470df8386036243c|BBC|c20ad4d76fe97759aa27a0c99bff6710
2|14da3a611e2f8953d76b6fb7866b01d1|FGD|70efdf2ec9b086079795c442636b55fb
3|710a24b9eac0692b1adaabd07726211a|ASD|6f4922f45568161a8cdf4ad2299f6d23
4|c4d15b255ef3c6a89d1fe2e6a26b8eda|SDF|1f0e3dad99908345f7439f8ffabdffc4
5|96b24a28173a75cc3c682e25d3a6bd49|ASC|b6d767d2f8ed5d21a44b0e5886680cb9

Обратите внимание, что хэши MD5 отличаются в этом ответе (На момент написания) те, в других; это потому, что они используют подходы, которые добавляют завершающий символ новой строки к хешируемым строкам, приводя к неверным результатам, если вы хотите точный га sh:

$ echo AB1 | md5sum
d44aec35a11ff6fa8a800120dbef1cd7  -
$ echo -n AB1  | md5sum
1109867462b2f0f0470df8386036243c  -
0 голосов
/ 20 февраля 2020

Вы могли бы рассмотреть возможность использования языка, который поддерживает md5, или, по крайней мере, кэшировать результаты md5 (я предполагаю, что город и возраст имеют ограниченный домен, который меньше, чем количество строк).

Perl имеет встроенную поддержку md5:

perl -M'Digest::MD5 qw(md5_hex)' -F'\|' -le 'if (2..eof) { 
       $F[$_] = md5_hex($F[$_]) for (1,3);
       print join "|",@F 
    } else { print }'

онлайн-демонстрация: https://ideone.com/xg6cxZ (к моему удивлению ideone имеет perl в bash)

  • Digest::MD5 - это основной модуль, любой perl должен иметь его
  • -M'Digest::MD5 qw(md5_hex)' - это загружает функцию md5_hex
  • -l окончания строк
  • -F'\|' - автоматическое разбиение полей на | (это подразумевает -a и -n)
  • 2..eof - оператор диапазона (или триггер, как некоторые хотят его вызвать) - true между строкой 2 и концом файла
  • $F[$_] = md5_hex($F[$_]) - заменить поле $ _ суммой md5
  • for (1,3) - модификатор оператора запускает оператор для 1 и 3, алиасинг $ _ для них
  • print join "|",@F - выведите измененные поля
  • else { print } - это будет содержать заголовок

Примечание о скорости : на моем компьютере это обрабатывает ~ 100 000 строк примерно за 100 мс по сравнению с вариантом awk этого ответа , который делает 5000 строк за ~ 1 минуту 14 секунд (я не был достаточно терпелив, чтобы ждать 100 000 строк)

time perl -M'Digest::MD5 qw(md5_hex)' -F'\|' -le 'if (2..eof) { $F[$_] = md5_hex($F[$_]) for (1,3);print join "|",@F } else { print }' <sample2.txt > out4.txt 

real    0m0.121s
user    0m0.118s
sys 0m0.003s
$ time awk -F'|' -v OFS='|' -i md5.awk '{ print $1,md5($2),$3,md5($4) }' <(head -5000 sample2.txt) >out2.txt

real    1m14.205s
user    0m50.405s
sys 0m35.340s

md5.awk Определяет функцию md5 следующим образом:

$ cat md5.awk 
function md5(str, cmd, l, hex) {
    cmd= "/bin/echo -n "str" | openssl md5 -r"
    if ( ( cmd | getline l) > 0 ) 
        hex = substr(l,0,32)
    close(cmd)
    return hex
}
  • Я использую /bin/echo, потому что есть некоторые варианты оболочки, в которых у echo нет -n
  • I'm используя -n главным образом потому, что я хочу иметь возможность сравнивать результаты с perl результатами
  • substr(l,0,32) - на моей машине openssl md5 не возвращает только сумма, у него также есть имя файла - см .: https://ideone.com/KGMWPe - substr получает только соответствующую часть
  • Я использую отдельный файл, потому что он кажется намного чище, и потому что я довольно легко переключаться между реализациями функций

Как я уже говорил в начале, если вы действительно хотите использовать awk, по крайней мере кэшируйте результат инструмента openssl.

$ cat md5memo.awk 
function md5(str, cmd, l, hex) {
    if (cache[str]) 
        return cache[str]
    cmd= "/bin/echo -n "str" | openssl md5 -r"
    if ( ( cmd | getline l) > 0 ) 
        hex = substr(l,0,32)
    close(cmd)
    cache[str] = hex
    return hex
}

При вышеупомянутом кешировании результаты значительно улучшаются:

$ time awk -F'|' -v OFS='|' -i md5memo.awk '{ print $1,md5($2),$3,md5($4) }' <(head -5000 sample2.txt) >outmemo.txt

real    0m0.192s
user    0m0.141s
sys 0m0.085s
[savuso@localhost hash]$ time awk -F'|' -v OFS='|' -i md5memo.awk '{ print $1,md5($2),$3,md5($4) }' <sample2.txt >outmemof.txt

real    0m0.281s
user    0m0.222s
sys 0m0.088s

однако ваш пробег может варьироваться : sample2.txt имеет 100000 строк, с 5 различными значениями для $ 2 и 40 различными значения за 4 доллара. Реальные данные могут отличаться!

Примечание: я только что понял, что моя реализация awk не обрабатывает заголовки, но вы можете узнать это из других ответов

0 голосов
/ 18 февраля 2020

Вы должны указать | в качестве разделителей поля ввода и вывода. В противном случае $2 - это не то, что вы ожидаете, а пустая строка.

awk -F '|' -v "OFS=|" 'FNR==1 { print; next } {
    tmp="echo " $2 " | openssl md5 | cut -f2 -d\" \""
tmp | getline cksum
close(tmp)
$2=cksum
print
}' sample.csv

печатает

ID|NAME|CITY|AGE
1|d44aec35a11ff6fa8a800120dbef1cd7|BBC|12
2|157aa4a48373eaf0415ea4229b3d4421|FGD|17
3|ba3c08d4a65f1baa1d7220a6802b5710|ASD|18
4|69be622e1c0d417ceb9b8fb0aa9dc574|SDF|19
5|427872b1ac3a22dc154688ddc2050516|ASC|22
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...