Функция awk для изменения нескольких столбцов с помощью регулярного выражения в CSV - PullRequest
0 голосов
/ 24 января 2020

Цель:

Мне нужно изменить URL-адреса, чтобы в них оставалось только число (широта / долгота / id): в .csv-файле у меня есть один "определенный заголовок в заголовке". Это мне нужно найти. В столбце этого найденного Титела мне нужно будет удалить начало и конец URL-адресов, поэтому в нем останется только то, что было частью URL-адреса. Это мне нужно будет сделать на разных структурированных CSV с несколькими столбцами с разными титрами и разными шаблонами URL. Есть ли способ написать функцию в bash с помощью awk?

Я пробовал - и она не работает, потому что в ней отсутствует много недостающих знаний:

#!/bin/bash
CSVFILE=$(find ./aufzubereiten -type f ! -name ".DS_Store") #only one file in this folder.
FILENAME=$(basename "$CSVFILE")

function modify_col() {
    COL= how to find the right column in the csv?
    awk -F',' OFS="," -v pat='"$PAT"' '{sub(/pat/,X,$${COL})} 1' "$CSVFILE" > "$CSVFILE".tmp1 && mv "$CSVFILE".tmp1 "$CSVFILE"
}

COLTITEL="certain Titel in Header"
PAT='/Text1234Text[0-9]{5,8}Text1.html'
PATNEW=''
modify_col

COLTITEL="certain Titel2 in Header"
PAT='/Text2234Text[0-9]{5,8}Text2.html'
PATNEW=''
modify_col

COLTITEL="certain Titel3 in Header"
PAT='/Text3234Text[0-9]{5,8}Text3.html'
PATNEW=''
modify_col

Пример файла:

header1, header2, certain Titel in Header, certain Titel2 in Header, certain Titel3 in Header
,,/Text2234Text7846641Text.html,/Text2234Text8974341Text2.html,/Text2234Text823241Text3.html
,,/Text2234Text7846642Text.html,/Text2234Text8974342Text2.html,/Text2234Text823242Text3.html
,,/Text2234Text7846643Text.html,/Text2234Text8974343Text2.html,/Text2234Text823243Text3.html

Результат должен быть:

header1, header2, certain Titel in Header, certain Titel2 in Header, certain Titel3 in Header
,,7846641,8974341,823241
,,7846642,8974342,823242
,,7846643,8974343,823243

Спасибо за ваши идеи:)

Ответы [ 4 ]

1 голос
/ 24 января 2020

Предположение:

  • данные выглядят точно как образец в вопросе, ie, литерал Text отображается в 3-х местах в каждом файле html имя

Пример данных:

$ cat text.dat
header1, header2, certain Titel in Header, certain Titel2 in Header, certain Titel3 in Header
,,/Text2234Text7846641Text.html,/Text2234Text8974341Text2.html,/Text2234Text823241Text3.html
,,/Text2234Text7846642Text.html,/Text2234Text8974342Text2.html,/Text2234Text823242Text3.html
,,/Text2234Text7846643Text.html,/Text2234Text8974343Text2.html,/Text2234Text823243Text3.html

Один awk Решение:

$ awk -F"Text" '
BEGIN  { OFS="," }
FNR==1 { print ; next }
       { print ",,"$3,$6,$9 }
' text.dat

Где:

  • -F"Text" - используйте Text в качестве нашего разделителя поля ввода
  • OFS="," - установите разделитель поля вывода
  • FNR==1 {print ; next} - для строки № 1 (строка заголовка) напечатайте всю строку и перейдите к следующая строка в файле
  • print ",,"$3,$6,$9 - выведите 2 запятые, а затем поля 3, 6 и 9 (разделенные OFS=",")

Результат:

header1, header2, certain Titel in Header, certain Titel2 in Header, certain Titel3 in Header
,,7846641,8974341,823241
,,7846642,8974342,823242
,,7846643,8974343,823243
1 голос
/ 24 января 2020

Вот обобщенное решение для поиска чисел с пятью или более цифрами и удаления всего остального.

awk -F , 'BEGIN { OFS=FS }
  FNR>1{
    for(i=1;i<=NF;++i) {
        gsub(/(^|[^0-9])[0-9]{1,4}([^0-9]|$)/, "", $i);
        gsub(/[^0-9]+/, "", $i);
    }
  } 1' filename

Если у вас только одно имя файла, вероятно, нет смысла использовать find. Если вы не знаете имя файла, но в текущем каталоге есть только один файл, * расширится до этого имени.

Это немного хрупко, потому что это не поможет вещь, если два числа внутри поля разделены одним нечисловым символом. Это не сложно исправить, но я ленив, и ваши требования немного расплывчаты.

1 голос
/ 24 января 2020

Не могли бы вы попробовать следующие, написанные и протестированные с показанными образцами.

awk '
BEGIN{
  FS=OFS=","
}
FNR==1{
  print
  next
}
{
  for(i=1;i<=NF;i++){
    sub(/^\/Text[0-9]+Text/,"",$i)
    sub(/Text.*/,"",$i)
  }
}
1
'  Input_file

Объяснение: Добавление подробного уровня объяснения приведенного выше кода.

awk '
BEGIN{                                 ##Starting BEGIN section of code here.
  FS=OFS=","                           ##Setting FS and OFS to comma here.
}
FNR==1{                                ##Checking condition if FNR==1 then do following.
  print                                ##Printing the current line here.
  next                                 ##next will skip all further statements from here.
}
{
  for(i=1;i<=NF;i++){                  ##Starting a for loop to traverse into all fields here.
    sub(/^\/Text[0-9]+Text/,"",$i)     ##Substituting from starting Text digits Text with NULL in current field.
    sub(/Text.*/,"",$i)                ##Substituting everything from Text to till last of field value with NULL in current field.
  }
}
1                                      ##1 will print edited/non-edited line here.
'  Input_file                          ##Mentioning Input_file name here.
0 голосов
/ 24 января 2020

Я знаю, что OP спросил, есть ли способ сделать это с помощью awk, но из представленного контекста я понимаю, что любое решение, которое будет выполняться в скрипте bash, решит проблему OP.

Для таких случай, на мой взгляд, sed является более элегантным решением:

sed 's/[^,]\+[^0-9]\([0-9][0-9]\+\)[^,]\+/\1/g' data.csv

Он выводит любое число с 2 или более цифрами, которое ближе к концу поля. Расширенная версия sed может помочь лучше ее визуализировать:

sed -E 's/[^,]+[^0-9]([0-9][0-9]+)[^,]+/\1/g' data.csv

output:

rvb@ubuntu:~$ sed -E 's/[^,]+[^0-9]([0-9][0-9]+)[^,]+/\1/g' data.csv
header1, header2, certain Titel in Header, certain Titel2 in Header, certain Titel3 in Header
,,7846641,8974341,823241
,,7846642,8974342,823242
,,7846643,8974343,823243
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...