Для начала, конвейер сторожевого пса из вашего вопроса, вероятно, не убьет process1
, если он снова не пытается писать на мертвую трубу. Поэтому ваш сторожевой пёс должен каким-то образом явно убить процесс1.
В дополнение к этому вот очень простой watchdog.sh
сценарий оболочки. Вы можете проверить его в интерактивном режиме в консоли. Просто введите ./watchdog.sh
. Он будет дублировать все, что вы вводите, и остановится, если вы ничего не введете в течение 5 секунд.
#!/bin/bash
# first arg is timeout (default: 5)
T="${1:-5}"
PID=$$
exec tee >(bash -c '
while true ; do
bytes=$(timeout '$T' cat | wc -c)
if ! [ "$bytes" -gt 0 ] ;then
break
fi
done
## add something like "killall process1", for now we just kill this tee command
kill -9 '$PID)
Заметьте, что скрипт на самом деле будет иметь таймаут между T и 2*T (иначе это было бы намного сложнее). Каким-то образом можно добавить способ убить process1
, как я уже говорил.
Ниже пример для тестирования.
process1.sh:
#!/bin/bash
echo "here we are ..."
sleep 2
echo "still alive ..."
sleep 20
echo "too late ..."
И запустите его следующим образом (включая уродливый способ убить process1.sh по таймауту):
(./process1.sh & echo $! >/tmp/pid; wait) |(./watchdog.sh 5; kill `cat /tmp/pid`)
Самый простой способ, который я могу придумать, это сохранить первую строку как переменную, а затем распечатать остальные по мере необходимости. Это, однако, требует сохранения всего входного файла в памяти:
#!/bin/gawk -f
{
if(NR==1){
header[1]=$1;
header[2]=$2;
for(i=3;i<=NF;i++){
header[$i]=i;
}
}
else{
data[$1][NR]=$2"\t"$(header[$1]);
}
}
END{
OFS="\t";
for(i in data){
print header[1],header[2],i > i".txt"
for(k in data[i]){
print i,data[i][k] >> i".txt"
}
}
}
Сохраните этот скрипт как foo.awk
, сделайте его исполняемым ( chmod a+x foo.awk
) и запустите это в вашем файле:
foo.awk file
Вы можете использовать awk
:
awk 'NR>1{if ($1!=p){N="file"++C; print "Name\tID\t"$1 >N};
print $1,$2,$(C+2)>N}{p=$1}' infile.txt
Полноерешение awk:
awk 'NR==1{ len=split($0,a_pos); for(i=1;i<=len;i++) a_keys[a_pos[i]]=i }
NR>1{ if(!r[$1]++) { fn="file"++c; print "Name\tID\t"$1 > fn }
print $1,$2,$(a_keys[$1]) > fn
}' OFS='\t' file
len=split($0,a_pos)
-разбивает 1-ю строку на массив "ключей" (массив a_pos
индексируется целыми числами)
for(i=1;i<=len;i++) a_keys[a_pos[i]]=i
-преобразование a_pos
в массив a_keys
, который будет проиндексирован строковыми ключами (для дальнейшей обработки)
fn="file"++c
-создание имени файла
Просмотр результатов:
for f in file[0-9]*; do (echo "$f"; cat "$f"; echo); done
Выход (file1
, file2
, file3
и file4
последовательно):
file1
Name ID ABC12
ABC12 123456 XX
ABC12 123456 XX
file2
Name ID ABCD12
ABCD12 123456 YY
ABCD12 123456 YY
file3
Name ID ABCD123
ABCD123 123456 ZZ
ABCD123 123456 ZZ
file4
Name ID ABCD1234
ABCD1234 123456 JJ
ABCD1234 123456 JJ
Использование:./split_matrix.awk input.txt
#!/usr/bin/awk -f
BEGIN {
cnt = 1;
}
NR == 1 {
for(i = 3; i <= NF; i++) {
headers[$i] = i;
}
}
NR > 1 {
if( ! file_names[$1]) {
file_names[$1] = cnt++;
printf "%s %s %s\n", "Name", "ID", $1 > "file_"file_names[$1];
}
printf "%s %s %s\n", $1, $2, $headers[$1] >> "file_"file_names[$1];
}
вход
Name ID ABC12 ABCD12 ABCD123 ABCD1234
ABC12 123456 XX YY ZZ JJ
ABC12 123456 XX YY ZZ JJ
ABCD12 123456 XX YY ZZ JJ
ABCD12 123456 XX YY ZZ JJ
ABCD123 123456 XX YY ZZ JJ
ABCD123 123456 XX YY ZZ JJ
ABCD1234 123456 XX YY ZZ JJ
ABCD1234 123456 XX YY ZZ JJ
вывод(используйте tail -n +1 -- file*
для печати имени файла и содержимого файла. Я нашел этот трюк здесь)
==> file_1 <==
Name ID ABC12
ABC12 123456 XX
ABC12 123456 XX
==> file_2 <==
Name ID ABCD12
ABCD12 123456 YY
ABCD12 123456 YY
==> file_3 <==
Name ID ABCD123
ABCD123 123456 ZZ
ABCD123 123456 ZZ
==> file_4 <==
Name ID ABCD1234
ABCD1234 123456 JJ
ABCD1234 123456 JJ