Я основываюсь на ответе Атти, который мне понравился как идея.
Вы можете сделать это с помощью встроенной команды 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
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-скрипт.
Вы можете попробовать следующее (, которое я разбил на несколько строк, используя «продолжение строки» с обратной косой чертой в конце -строки -для удобства чтения):
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
a
и количество таких полей в nzero
. 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
Вот еще один 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