Заменить все 0, если все строки равны 0, нет данных и 1

Я основываюсь на ответе Атти, который мне понравился как идея.

Вы можете сделать это с помощью встроенной команды bash printf, которая будет принимать секунды с начала эпохи в качестве аргумента. Не нужно разветвляться для запуска date.

Вы должны установить часовой пояс на UTC для printf, потому что он форматирует время в вашем местном часовом поясе, и вы получите неправильный ответ, если вы не в формате UTC.

$ seconds=123456789
$ TZ=UTC printf "%d days %(%H hours %M minutes %S seconds)T\n" $((seconds/86400)) $seconds
1428 days 21 hours 33 minutes 09 seconds

По моему местному времени (, которое сейчас NZDT -+1300 ), ответ будет неверным, если я не установлю часовой пояс

$ seconds=123456789
$ printf "%d days %(%H hours %M minutes %S seconds)T\n" $((seconds/86400)) $seconds
1428 days 09 hours 33 minutes 09 seconds

С настройкой часового пояса и без нее

$ seconds=$(( 3600 * 25))
$ printf "%d days %(%H hours %M minutes %S seconds)T\n" $((seconds/86400)) $seconds
1 days 13 hours 00 minutes 00 seconds

$ TZ=UTC printf "%d days %(%H hours %M minutes %S seconds)T\n" $((seconds/86400)) $seconds
1 days 01 hours 00 minutes 00 seconds
2
07.07.2020, 17:47
3 ответа

01.awk:

BEGIN{FS=OFS="\t"}
skip=0
{
    for(i=4;i<=NF;i++){
        if($i !~ /NA/ && $i!=0 && $i!=1){
            skip=1
            break
        }
    }
}
!skip{gsub(/0\.0+/,"0.001")}
1

Цикл for ищет не -NA, не -ноль и не -одно поле в строке, начиная с четвертого столбца. Если найдено, skipустанавливается равным 1, поэтому строка

!skip{gsub(/0\.0+/,"0.001")}

не выполняется и 0.00не становится 0.001.

Выполните скрипт с помощью

awk -f 01.awk inputfile

PS:#!/bin/bash -ueпо началу ваша попытка не имеет особого смысла, так как вы написали awk-скрипт, а не bash-скрипт.

2
18.03.2021, 23:21

Вы можете попробовать следующее (, которое я разбил на несколько строк, используя «продолжение строки» с обратной косой чертой в конце -строки -для удобства чтения):

awk -F'\t' -v OFS='\t' '{delete a; nzero=0;\
      for (i=4;i<=NF;i++){\
        if ($i==0) a[++nzero]=i;\
        if ($i!=0 && $i!=1 && $i!="NA") {print; next;}\
      }\
      for (i=1;i<=nzero;i++) {$a[i]=0.001;}; print;}' input.txt
  • Это проанализирует все строки, чтобы увидеть, какие из полей (, начиная с 4-го поля, )имеют значение, численно равное нулю, и сохранит номера полей в массиве aи количество таких полей в nzero.
  • В то же время он проверяет, соответствует ли какое-либо проверяемое поле не 0, 1 или "NA". Если это так, строка печатается «как есть», а выполнение переходит к следующей строке.
  • Если «недопустимые» поля не найдены, массив aанализируется, и все номера полей, хранящиеся в нем, заменяются на 0.001.

Обратите внимание, что синтаксис delete aдля очистки массива требует GNU Awk. Для других реализаций используйте вместо этого split("",a).

То же, что и скрипт Awk (назовем егоreplace.awk):

#!/bin/awk -f
BEGIN{FS=OFS="\t"}

{
  delete a;
  nzero=0;

  for (i=4;i<=NF;i++)
  {
    if ($i==0) a[++nzero]=i;
    if ($i!=0 && $i!=1 && $i!="NA")
    {
      print;
      next;
    }
  }
  for (i=1;i<=nzero;i++) $a[i]=0.001;
  print;
}

Использовать как

awk -f replace.awk input.txt
2
18.03.2021, 23:21

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

$ awk '/^([\t]*[^\t]+){3}([\t]+([01][.]00|NA))+$/ && gsub(/0\.00/, "0.001") || 1' file

Регулярное выражение декодировано:

^([\t]*[^\t]+){3}обход первых трех полей (поля разделены табуляцией )текущей записи.

([\t]+([01][.]00|NA))— это форма «хорошего» поля, начиная с четвертого.

поставить +после поля «хорошо» и довести до конца записи, то есть все поля после третьего являются «хорошими». И это означает, что это строка, которую мы хотим изменить.

gsubзаменит «0,00» на «0,001»

===============================

Ниже приведен код awk для генерации регулярного выражения на лету и внесения изменений.

$ gudFld="[01][.]00|NA" \
    awk '
       function enc(arg) {
         return "(" arg ")"
       }
       BEGIN {
         sp = "\t"
         s = "["  sp "]"
         S = "[^" sp "]"
         f_ = s"*" S"+"; f = enc(f_)
         f3 = f"{3}"
         e = enc( ENVIRON["gudFld"])
         g_ = s"+" e; g = enc(g_)
         pat = "^" f3 g"+" "$"
       }
       $0 ~ pat && gsub(/0.00/, "&1") || 1
' file

И если мы хотим быть параноиками и учитывать возможность появления шаблона 0.00в первых трех полях, мы можем заменить последнюю строку следующей. Здесь мы запускаем gsub только в поле 4 и далее.

$0 ~ pat {
   match($0, f3)
   f123 = substr($0, 1, RLENGTH)
   f4_end = substr($0, RLENGTH+1)
   gsub(/0.00/, "&1", f4_end)
   $0 = f123 f4_end
}1
1
18.03.2021, 23:21

Теги

Похожие вопросы