. Вы можете использовать split
для извлечения двух частей поля 10 в массив (здесь он называется arr10
]) следующим образом:
split($10, arr10, ":")
Затем вы можете построить индекс из комбинации первого элемента этого массива и всего элемента 14. Используя этот индекс, вы можете построить два новых массива, например sum_of_11
и old_15
:
sum_of_11[arr10[1]"\t"$14] += $11 # sum of all rows that have this index
old_15[arr10[1]"\t"$14] = $15 # just the value in the single most recent row
Объединение (и установка OFS = "\ t"
):
awk '{ split($10, arr10, ":");
sum_of_11[arr10[1]"\t"$14] += $11;
old_15[arr10[1]"\t"$14] = $15
} END {
OFS = "\t";
for (i in sum_of_11) {
print i, sum_of_11[i], old_15[i], old_15[i] - sum_of_11[i]
}
}' file
Результат:
chromosome_1_Contig0.3916 gi|733214878|ref|NM_001303082.1| 477 708 231
chromosome_1_unplaced_Contig0.12366 gi|526117831|ref|NM_001281196.1| 637 1025 388
chromosome_1_unplaced_Contig0.3951 gi|526117967|ref|NM_001281232.1| 107 1518 1411
chromosome_1_unplaced_Contig0.12504 gi|526117831|ref|NM_001281196.1| 314 1025 711
chromosome_1_Contig0.1980 gi|952977790|ref|NM_001317128.1| 849 849 0
Позвольте мне начать с того, что вы можете просто встроить все свои данные в scannew
, поскольку вы все равно подождите
, если вы не собираетесь сканировать снова в какой-то другой момент вашего скрипта. Это действительно вызов wc
, который, по вашему мнению, может занять слишком много времени, и если это произойдет, вы можете просто прекратить его.Это простой способ настроить это с помощью ловушки
, которая позволяет вам захватывать сигналы, отправленные процессу, и устанавливать для него свой собственный обработчик:
#! /usr/bin/env bash
# print a line just before we run our subshell, so we know when that happens
printf "Lets do something foolish...\n"
# trap SIGINT since it will be sent to the entire process group and we only
# want the subshell killed
trap "" SIGINT
# run something that takes ages to complete
BAD_IDEA=$( trap "exit 1" SIGINT; ls -laR / )
# remove the trap because we might want to actually terminate the script
# after this point
trap - SIGINT
# if the script gets here, we know only `ls` got killed
printf "Got here! Only 'ls' got killed.\n"
exit 0
Однако, если вы хотите сохранить свой способ действий , поскольку scannew
является функцией, выполняемой как фоновое задание, это требует немного больше работы.
Поскольку вам нужен ввод данных пользователем, правильный способ сделать это - использовать read
, но нам все равно нужно, чтобы скрипт продолжал работать, если scannew
завершится, а не просто ждать, пока пользователь ввод навсегда. read
делает это немного сложным, потому что bash
ожидает завершения текущей команды, прежде чем разрешить trap
работать с сигналами. Единственное известное мне решение без рефакторинга всего скрипта - это поместить read
в цикл while true
и дать ему тайм-аут в 1 секунду, используя читать -t 1
. Таким образом, для завершения процесса всегда потребуется как минимум секунда, но это может быть приемлемо в таких обстоятельствах, как ваше, когда вы, по сути, хотите запустить демон опроса, который отображает список USB-устройств.
#! /usr/bin/env bash
function slow_background_work {
# condition can be anything of course
# for testing purposes, we're just checking if the variable has anything in it
while [[ -z $BAD_IDEA ]]
do
BAD_IDEA=$( ls -laR / 2>&1 | wc )
done
# `$$` normally gives us our own PID
# but in a subshell, it is inherited and thus
# gives the parent's PID
printf "\nI'm done!\n"
kill -s SIGUSR1 -- $$
return 0
}
# trap SIGUSR1, which we're expecting from the background job
# once it's done with the work we gave it
trap "break" SIGUSR1
slow_background_work &
while true
do
# rewinding the line with printf instead of the prompt string because
# read doesn't understand backslash escapes in the prompt string
printf "\r"
# must check return value instead of the variable
# because a return value of 0 always means there was
# input of _some_ sort, including <enter> and <space>
# otherwise, it's really tricky to test the empty variable
# since read apparently defines it even if it doesn't get input
read -st1 -n1 -p "prompt: " useless_variable && {
printf "Keypress! Quick, kill the background job w/ fire!\n"
# make sure we don't die as we kill our only child
trap "" SIGINT
kill -s SIGINT -- "$!"
trap - SIGINT
break
}
done
trap - SIGUSR1
printf "Welcome to the start of the rest of your script.\n"
exit 0
Конечно, если вам действительно нужен демон, который отслеживает изменения количества USB-устройств или что-то в этом роде, вам следует изучить systemd
, который может предоставить что-то более элегантное.
(в некоторой степени) общее решение для запуска данной команды и ее уничтожения, если пользователь вводит данные, в противном случае выполняется выход. Суть состоит в том, чтобы выполнить чтение в несколько сыром виде. режим терминала ищет "любой" ключ и обрабатывает случай run-program-exits через сигнал SIGCHLD
, который должен быть отправлен при выходе дочернего процесса (при условии, что дочерний процесс не выполняет никаких забавных действий). Треска e и docs (и возможные тесты) .
#ifdef __linux__
#define _POSIX_SOURCE
#include <sys/types.h>
#endif
#include <err.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <termios.h>
#include <unistd.h>
int Flag_UserOkay; // -U
struct termios Original_Termios;
pid_t Child_Pid;
void child_signal(int unused);
void emit_help(void);
void reset_term(void);
int main(int argc, char *argv[])
{
int ch, status;
char anykey;
struct termios terminfo;
while ((ch = getopt(argc, argv, "h?U")) != -1) {
switch (ch) {
case 'U':
Flag_UserOkay = 1;
break;
case 'h':
case '?':
default:
emit_help();
/* NOTREACHED */
}
}
argc -= optind;
argv += optind;
if (argc == 0)
emit_help();
if (!isatty(STDIN_FILENO))
errx(1, "must have tty to read from");
if (tcgetattr(STDIN_FILENO, &terminfo) < 0)
err(EX_OSERR, "could not tcgetattr() on stdin");
Original_Termios = terminfo;
// cfmakeraw(3) is a tad too raw and influences output from child;
// per termios(5) use "Case B" for quick "any" key reads with
// canonical mode (line-based processing) and echo turned off.
terminfo.c_cc[VMIN] = 1;
terminfo.c_cc[VTIME] = 0;
terminfo.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminfo);
atexit(reset_term);
signal(SIGCHLD, child_signal);
Child_Pid = fork();
if (Child_Pid == 0) { // child
close(STDIN_FILENO);
signal(SIGCHLD, SIG_DFL);
status = execvp(*argv, argv);
warn("could not exec '%s' (%d)", *argv, status);
_exit(EX_OSERR);
} else if (Child_Pid > 0) { // parent
if ((status = read(STDIN_FILENO, &anykey, 1)) < 0)
err(EX_IOERR, "read() failed??");
kill(Child_Pid, SIGTERM);
} else {
err(EX_OSERR, "could not fork");
}
exit(Flag_UserOkay ? 0 : 1);
}
void child_signal(int unused)
{
// might try to pass along the exit status of the child, but that's
// extra work and complication...
exit(0);
}
void emit_help(void)
{
fprintf(stderr, "Usage: waitornot [-U] command [args ..]\n");
exit(EX_USAGE);
}
void reset_term(void)
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Original_Termios);
}