xargs: загуба на изход при пренасочване на stdout към файл в паралелен режим

Използвам GNU xargs (версия 4.2.2) в паралелен режим и изглежда, че надеждно губя изход при пренасочване към файл. При пренасочване към тръба изглежда, че работи правилно.

Следните команди на обвивката демонстрират минимален, пълен и проверим пример за проблема. Генерирам 2550 числа, като използвам xargs, за да го разделя на редове от по 100 аргумента, всеки общо 26 реда, където 26-ият ред съдържа само 50 аргумента.

# generate numbers 1 to 2550 where each number is on its own line
$ seq 1 2550 > /tmp/nums
$ wc -l /tmp/nums
2550 /tmp/nums

# piping to wc is accurate: 26 lines, 2550 args
$ xargs -P20 -n 100 </tmp/nums | wc
     26    2550   11643

# redirecting to a file is clearly inaccurate: 22 lines, 2150 args
$ xargs -P20 -n 100 </tmp/nums >/tmp/out; wc /tmp/out
     22  2150 10043 /tmp/out

Вярвам, че проблемът не е свързан с основната обвивка, тъй като обвивката ще извърши пренасочването преди изпълнението на командите и ще изчака xargs да завършат. В този случай предполагам, че xargs завършва преди прочистване на буфера. Въпреки това, ако моята хипотеза е вярна, не знам защо този проблем не се проявява при писане в канал.

Редактиране:

Появява се при използване на >> (създаване/добавяне към файл) в обвивката, проблемът не изглежда да се проявява:

# appending to file
$ >/tmp/out
$ xargs -P20 -n 100 </tmp/nums >>/tmp/out; wc /tmp/out
     26    2550   11643

# creating and appending to file
$ rm /tmp/out
$ xargs -P20 -n 100 </tmp/nums >>/tmp/out; wc /tmp/out
     26    2550   11643

person snap    schedule 08.09.2015    source източник
comment
Получавам точен резултат и в двата случая. Shell> wc -l /tmp/nums 2550 /tmp/nums Shell> xargs -P20 -n 100 </tmp/nums | wc 26 2550 11643 Shell> xargs -P20 -n 100 </tmp/nums >/tmp/out; wc /tmp/out 26 2550 11643 /tmp/out Shell>   -  person Sriharsha Kalluru    schedule 08.09.2015
comment
Получавате ли надеждно правилния резултат, ако изпразните изходния файл и след това използвате >> вместо > пренасочване? Ако е така, има някакво обяснение.   -  person Jonathan Leffler    schedule 08.09.2015
comment
@JonathanLeffler: Изглежда, че си прав. С >> проблемът не се проявява. Опитах се да създам файла преди време и да пренасоча, като използвам и използвам „›“ (съкращавам съществуващия файл) и проблемът изглежда се появява отново.   -  person snap    schedule 08.09.2015
comment
Когато използвате пренасочването >, какви числа се появяват в началото на /tmp/out? Дали са числа като 1, 2, 3 или са числа като 2001, 2002, 2003? Имам някои проблеми с намирането на правдоподобен механизъм за проблема. Поведението на канала и добавянето е достатъчно лесно за обяснение. Но поведението с > трябва да е по същество същото и оставам да се чудя как нещата се развалят. Имате ли налични truss или strace? Ако е така, може да е поучително да погледнете какво прави процесът xargs (но не — поне на първо място — какво правят неговите деца). […продължение…]   -  person Jonathan Leffler    schedule 08.09.2015
comment
[… продължение…] Има ли полезна информация в xargs.log, след като стартирате strace -o xargs.log xargs -P 20 -n 100 </tmp/nums > /tmp/out? Мисля за нещо като lseek() на файлов дескриптор 1, но не съм сигурен колко правдоподобно е това. Един от проблемите може да е, че всъщност е дете, което причинява пакостите; в такъв случай ще трябва да използвате опцията „следване на деца“ (-f), за да видите какво причинява проблема. Но резултатът би бил много по-обемист. Получавам „правилния“ изход както на Mac OS X 10.10.5, така и на Ubuntu 14.04 LTS (работещ във VM под Mac OS X).   -  person Jonathan Leffler    schedule 08.09.2015
comment
Благодаря за предложението да използвате strace. Сега анализирам изхода. Проблемът се случва в Ubuntu 14.04 LTS (също във виртуална машина), но забелязах, че е по-очевиден на някои системи в сравнение с други. Намирам, че проблемът възниква бързо в груб цикъл while: seq 1 2550 > /tmp/nums; while true; do xargs -P20 -n 100 </tmp/nums >/tmp/out; wc /tmp/out; done | grep -v ' 26 '. Опитах това на OSX 10.10.4 и не успях да изявя и този проблем. [1/2]   -  person snap    schedule 08.09.2015
comment
Току-що обаче открих подобен проблем. Определено може да се дължи на xargs родителски процес, който се отделя от децата, излизайки рано, докато дете (което наследява и пише в stdout) не изчиства буфера. Ще прегледам изхода на strace, за да видя дали мога да определя къде се случва това. [2/2]   -  person snap    schedule 08.09.2015
comment
Бих искал да чета повече въпроси като вашите!   -  person wap26    schedule 08.09.2015


Отговори (2)


Вашият проблем се дължи на смесването на изхода от различни процеси. Показано е тук:

parallel perl -e '\$a=\"1{}\"x10000000\;print\ \$a,\"\\n\"' '>' {} ::: a b c d e f
ls -l a b c d e f
parallel -kP4 -n1 grep 1 > out.par ::: a b c d e f
echo a b c d e f | xargs -P4 -n1 grep 1 > out.xargs-unbuf
echo a b c d e f | xargs -P4 -n1 grep --line-buffered 1 > out.xargs-linebuf
echo a b c d e f | xargs -n1 grep 1 > out.xargs-serial
ls -l out*
md5sum out*

Решението е да се буферира изходът от всяко задание - или в паметта, или в tmp файлове (както прави GNU Parallel).

person Ole Tange    schedule 08.09.2015
comment
Съгласен съм, че няма абсолютно никакъв контрол върху смесването на изхода на stdout (освен ако write()s от дъщерния процес са ограничени по размер, атомни и приложението позволява смесване на изхода), но това не обяснява загубата input, което се случва както в моя, така и във вашия пример. Всъщност преминах към паралелно поради групирането на изхода. - person snap; 09.09.2015
comment
Това се дължи на множество файлови дескриптори, отворени за един и същ файл: Ако те пишат един след друг, няма проблем. Ако пишат едновременно, те ще пишат на едни и същи позиции във файла. Това също обяснява защо не виждате проблема, ако пренасочите към канал вместо към файл: Няма позиция на файла в канал. Това също обяснява защо ›› не причинява поведението. - person Ole Tange; 09.09.2015

Знам, че този въпрос е за xargs, но ако продължавате да имате проблеми с него, тогава може би GNU Parallel може да е от полза. Вашето xargs извикване ще се преведе на:

$ < /tmp/nums parallel -j20 -N100 echo > /tmp/out; wc /tmp/out
26  2550 11643 /tmp/out
person Jeroen Janssens    schedule 08.09.2015