2016-06-08 2 views
0

Я написал программу, которая ждет данных на стандартном входе, записывает ее в временное местоположение и перемещает ее в указанное пользователем местоположение по окончании ввода.В трубе, как программа вниз по течению получает код выхода программы вверх по течению после окончания ввода?

К этому, я хочу, чтобы включить трубопроводы как этот

# Does not work; truncates myfile 
cat myfile | some_filter > myfile 
# shall enable pseudo-inplace modification 
cat myfile | some_filter | my_program myfile 

Но моя программа в настоящее время пишет в файл независимо от того, вверх по течению была успешной труба. Я хочу избежать потери данных, если восходящий поток обнаружил ошибку и, следовательно, прервал выполнение программы, если одна из программ, расположенных выше по потоку, вытолкнула ошибку.

На данный момент я написал программу в bash, но не ограничился ею.

Как я могу проверить это в своей программе?

EDIT: Чтобы быть более конкретным: я хочу, чтобы мой пользователь имел дело с созданием временных файлов, проверял успех промежуточных программ и подобных им как можно больше. Я хочу, чтобы пользователь мог вызвать какую-либо программу (например, добавить столбец в текстовый файл, сортировать, фильтровать и т. Д.) В каком-либо файле и записать результат обратно в тот же файл, но только если промежуточные прогаммы вернули успех.

+1

Вы можете попробовать использовать mktemp для создания временного файла, записать в файл temp, проверить, завершился ли процесс успешно, а затем записать из файла temp в ваш файл, удалить файл temp. – dood

+2

Чтобы объяснить, что @dood сказал: 'tmpfile = $ (mktemp); process1 | process2> $ tmpfile && mv $ tmpfile myfile' – anishsane

+1

Также, 'cat myfile | some_filter' является [UUOC] (http://porkmail.org/era/unix/award.html#cat). Вместо этого используйте 'some_filter anishsane

ответ

1

Перенаправление, например > file, обрабатывается оболочкой, которая открывает файл для записи перед вызовом команды. Обойти это - как это было предложено @dood в комментариях, используя временный файл:

tmp=$(mktemp) 
a file | b | c > "$tmp"; && mv "$tmp" file 

Теперь файл, созданный mktemp используется для перенаправления. А затем после перемещения файла на номер file.

Это, однако, будет проверять только последнюю команду в трубе и двигаться, если это было успешно.

В есть переменная с именем PIPESTATUS, который содержит все статусы выхода для трубы, например:

% ls | cat | cat 
% echo "${PIPESTATUS[@]}" 
0 0 0 

И неудача:

% ls | cat | false 
% echo "${PIPESTATUS[@]}" 
0 141 1 

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

% ls | cat | cat 
% [[ "${PIPESTATUS[@]}" =~ ^0(0)*$ ]] && echo "good" 
good 

И с Ваш пример:

tmp=$(mktemp) 
some_filter <myfile> "$tmp" 
[[ "${PIPESTATUS[@]}" =~ ^0(0)*$ ]] && mv "$tmp" "myfile" 

Регулярное выражение ^0(0)*$ соответствует 0, 0 0, 0 0 0 ... Так что в основном соответствует, если все команды в трубе вышли успешно.

+0

Это именно то, что я хочу инкапсулировать в программу: Мой пользователь не должен создавать временный файл, считывая состояние канала и гарантируя, что временный файл будет удален после этого. – akraf

1

Весь смысл выполнения команд в конвейере заключается в том, что они выполняются одновременно. Когда вы запустите cmd1 | cmd2 | cmd3, cmd3, скорее всего, начнет выполнение до того, как закончится cmd1. Итак, если вы не можете вызвать временную аномалию стиля Star Trek, невозможно определить статус выхода cmd1 перед запуском cmd3. Если вам нужно подождать, пока cmd1 не закончится до начала cmd3, вы не сможете разместить их в одном конвейере.Ну, это не совсем так, вы можете делать такие хаки;

mkfifo /tmp/foo; { cmd1; echo > /tmp/foo; } | cmd2 | { cat /tmp/foo; cmd3; }

но зачем вы хотите? (Обратите внимание, что даже в этом случае команды в финальной части трубопровода действительно выполнить до того cmd1 отделки, но и cat блоки таким образом, что выполнение cmd3 задерживается.)

отметить также, что независимо от того, что вы пытаетесь, существует потенциальный тупик. Если трубы заполняются, cmd1 и cmd2 будут блокироваться бесконечно на записи, а cmd3 никогда не запустится.

+0

Спасибо за это объяснение. Но возможно ли иметь следующий курс времени: cmd1, cmd2 и cmd3 все начинают, cmd3 считывает весь вывод из cmd1 | cmd2 до конца передачи. Это означает, что cmd2 закрыл свой вывод, поэтому cmd3 должен иметь возможность получить статус выхода cmd2 в этой точке? – akraf

+0

Уверенный: 'cmd1 | cmd2>/tmp/foo && cmd3

Смежные вопросы