Как сохранить лидирующие пробелы при удалении последнего поля в awk? - PullRequest
2 голосов
/ 22 января 2020

Я хочу динамически создать шаблон файла конфигурации, содержащий учетные данные. Копия должна сохранять структуру, но со значениями, замененными пустыми строками (то есть ""). Я использую awk для этой цели.

awk -v dq="\"" '{ if($NF ~ /^[&|\*|\n]/ || $1 == $NF) print $0; else {$NF=""; print $0 dq dq;} }' .config.yaml >> .config_temp.yaml

Проблема в выражении else. Когда я установил $NF="", первые пробелы больше не печатались. Если я не устанавливаю в последнем поле пустую строку, я не наблюдаю такого поведения, но, очевидно, не получаю ожидаемый результат (см. Ниже).

Как я могу установить последнее поле как пустую строку, пока сохраняя первые пробелы?

Я не хочу вручную добавлять количество пробелов, так как намерение варьируется.

config.yaml (вход)


DEVELOPMENT: &development
  <<: *common

  check_access_token: False

  database:
    mongodb:
      database: test
      hostname: localhost
      port: 27017
      username: ""
      password: ""

      collection:
        col_1: test_1
        col_2: test_2
        col_3: test_3
        col_4: test_4

conf_temp.yaml (фактический вывод)


DEVELOPMENT: &development
  <<: *common

check_access_token: ""

  database:
    mongodb:
database: ""
hostname: ""
port: ""
username: ""
password: ""

      collection:
property: ""
ctrl_voc: ""
form: ""
user: ""

Ожидаемый вывод


DEVELOPMENT: &development
  <<: *common

check_access_token: ""

  database:
    mongodb:
      database: ""
      hostname: ""
      port: ""
      username: ""
      password: ""

      collection:
        property: ""
        ctrl_voc: ""
        form: ""
        user: ""

Редактировать (после Sundeep's повтор)

Спасибо за ваш ответ. Это почти работает, как я ожидаю. Однако я не получаю такой же вывод, как вы. Если я звоню

 awk -F'[ ]' -v dq="\"" 'NF>1 && $NF !~ /^[*&]|:$/{$NF = dq dq} 1' .conf.yaml

, я получаю следующий вывод:

DEVELOPMENT: &development
  <<: *common

  check_access_token: ""

  ""                             <--
    ""                           <--
      database: ""
      hostname: ""
      port: ""
      username: ""
      password: ""

      ""                         <--
        property: ""
        ctrl_voc: ""
        form: ""
        user: ""

Отступ соответствует ожидаемому, но клавиши верхних уровней заменяются кавычками (см. Стрелки).

Я получаю тот же вывод, если использую ваше второе предложение с sed.

Ответы [ 2 ]

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

См. Разделитель полей по умолчанию для awk , чтобы понять, что происходит, когда FS имеет значение по умолчанию или установлен в один пробел.

Вы можете избежать этого, используя некоторые другие средства для передачи один пробел, например [ ]

$ awk -F'[ ]' -v dq="\"" 'NF>1 && $NF !~ /^[*&]|:$/{$NF = dq dq} 1' ip.txt
DEVELOPMENT: &development
  <<: *common

  check_access_token: ""

  database:
    mongodb:
      database: ""
      hostname: ""
      port: ""
      username: ""
      password: ""

      collection:
        col_1: ""
        col_2: ""
        col_3: ""
        col_4: ""
  • NF>1, чтобы избежать изменения пустых строк
  • $NF !~ /^[*&]|:$/ проверить, не начинается ли последнее поле с * или & или не заканчивается на :
  • , если оба вышеуказанных условия выполнены, установите для последнего поля значение ""
  • 1 - это идиоматический c способ печати содержимого из $0


Для данного образца вы также можете использовать:

sed '/:$/! s/ [^*&][^ ]*$/ ""/' ip.txt
0 голосов
/ 22 января 2020

Вопрос: Как обновить поле без изменения оригинальных разделителей полей.

В соответствии со стандартом awk POSIX, когда вы обновляете поле с помощью $i = expr это вызывает пересчет значения $ 0, при этом поля разделяются значением OFS.

Для любого разделителя полей, который не равен , решение просто. Изменение поля n выполняется следующим образом:

awk 'BEGIN{FS=OFS="string"}
     {$n="new_value"}
     { ... }' file

Для других разделителей полей это немного более проблематично c:

  • Если FS=" " (значение по умолчанию) ), любой интервал до и после записи игнорируется, используется любая комбинация вкладок и полей
  • Если FS="ere" Расширенное регулярное выражение, вы на самом деле не знаете, каким будет разделитель полей. Если FS="fo*, это может быть что угодно: от f до fooooooo.

В POSIX awk вам необходимо выполнить некоторые неприятные манипуляции:

awk 'BEGIN{FS="ere"}
     # split original record
     { split($0,a,FS) }
     # update field value
     { a[n]="new_value" }
     # rebuild record
     {
       match($0,$1); rec=substr($0,1,RSTART-1); t=substr($0,RSTART+RLENGTH)
       for(i=1;i<NF;i++) {
          match(t,$(i+1)); rec = rec a[i] substr(t,1,RSTART-1)
          t=substr(t,RSTART+RLENGTH)
       }
       $0 = rec a[NF] t
     }
     { ... }' file

В GNU awk, вы можете использовать split немного более обобщенно c, так как он имеет расширение для сохранения оригинальных разделителей:

awk 'BEGIN{FS="ere"}
     # split original record
     { split($0,a,FS,f) }
     # update field value
     { a[n]="new_value" }
     # rebuild record
     { rec=f[0]; for(i=1;i<=NF;i++) rec=rec a[i] f[i]; $0 = rec }
     { ... }' file

Общий комментарий: GNU awk действительно может извлечь выгоду из процедуры, которая изменяет команду split и выполняет combine

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...