Some typo in Chapter 11.2 in time to ticks conversion formulas
Should be
VR = VAT * HSF (but not VSF!);
(VFL - VR) = VBT * HSF (but not VSF!);
Definition notes {{{
dot clock tick = horizontal tick
dot = raster dot
All four terms used here interchangeable. Though dot clock tick
generally refers to time needed to process (draw?) one dot, here it's
also used to measure horizontal scan line length: we take scan line
length as number of dot clock ticks passed until monitor have finished
to draw it.
scan line tick = vertical tick
scan line
These three terms here also used interchangeable. Scan line tick is
time needed to process (draw?) one horizontal scan line, but it's also
can be used to measure vertical screen length.
}}}
Frames and frequencies (in ticks) {{{
DCF = RR * HFL * VFL, (RR == VSF)
DCF = HFL * HSF
HSF = VFL * VSF
where {{{
DCF (driving clock frequency / dot clock frequency)
Number of dots (pixels) per second video adapter can emit. DCF
should match to monitor's video bandwidth - higher DCF will have
no effect.
RR (refresh rate)
Number of times per second entire screen is repainted. (RR is the
same as VSF);
HSF (horizontal sync frequency)
Number of times per second the monitor can write a horizontal scan
line (HFL dot clock ticks in length).
VSF (vertical sync frequency)
Number of times per second the monitor can write a full screen
(VFL scan line ticks in length). (The same as RR);
HFL (horizontal frame length)
Number of dot clock ticks needed for monitor to scan one
horizontal line, including the inactive left and right borders;
VFL (vertical frame length)
Number of scan line ticks needed for monitor to scan entire
screen, including the inactive top and bottom borders;
}}}
On the one hand, HFL is simply number of dots in one horizontal scan line
and VFL is simply number of scan lines in the entire screen. But we know
time required to scan one raster dot (duration of one dot clock tick)
(1 / DCF),
and time required to scan one horizontal line (duration of one scan line
tick)
(1 / HSF)
so we also can represent HFL and VFL in seconds (see below).
}}}
Sync pulses (in ticks) {{{
HFL = HR + HGT1 + HSP + HGT2
VFL = VR + HGT2 + VSP + VGT2
HB = HGT1 + HSP + HGT2
VB = VGT1 + VSP + VGT2
where {{{
HR (horizontal resolution)
Number of visible horizontal ticks (dot clock ticks). Or simply
number of visible raster dots;
VR (vertical resolution)
Number of visible vertical ticks (scan line ticks). Or simply
number of visible horizontal scan lines;
HGT1 (horizontal guard time 1)
Number of dot clock ticks to wait before sending HSP;
HSP (horizontal sync pulse)
Length in dot clock ticks of horizontal sync pulse;
HGT2 (horizontal guard time 2)
Number of dot clock ticks between end of HSP and end of horizontal
frame;
VGT1 (vertical guard time 1)
Number of vertical ticks (scan lines) to wait before sending VSP;
VSP (vertical sync pulse)
Length in vertical ticks (scan lines) of vertical sync pulse;
VGT2 (vertical guard time 2)
Number of vertical ticks (scan lines) between end of VSP and end
of vertical frame;
HB (horizontal blanking)
Length of horizontal blanking interval (see below) in dot clock
ticks.
VB (vertical blanking)
Length of vertical blanking interval (see below) in scan line
ticks.
}}}
HGT1 (VGT1) also called Front Porch and HGT2 (VGT2) - Back Porch.
Horizontal (vertical) blanking interval - is interval, when beam turned
off. It consists from three phases: Front porch, Sync pulse, Back porch.
- Front porch - the beam is black (== blank) and still moving right
(past the end of the visible part of scan line, but still at the
same scan line);
- Sync pulse - the beam is blacker than black and rapidly moving from
right physical screen edge to the left to start scan next line;
- Back porch - the beam is black (== blank) and moving right again
(already at the next (now current) scan line, but before the start
of its visible part);
Hence, actually what we call horizontal frame locates on two scan lines,
but if we map all its parts onto one line, they will not overlap. Here is
illustration:
time
------------------------------------------------------------------>
| Scan line N | Between | Scan line (N + 1)
| | scanlines |
| | |
>> HR >> Front >> sync >> Back >> HR >> ..
>> >> porch >> pulse >> porch >> >> ..
>> (visible) >> (black) >> (blacker) >> (black) >> (visible) >> ..
| |
| Horizontal frame N |
and here is another one illustration:
| HR (visible) || Front |
| || porch |
| || (black) |
R =====>=====>======>==___>___>___R
R /R - sync pulse,
R ..<.........<.........<.........<........ R - move beam to the left
R/ R - (blacker than black)
R___>___>___=====> R
| Back |
| porch |
| (black) |
where
= Beam turned on, visible ticks.
_ Beam turned off, black.
. Beam turned off, blacker than black.
R Physical screen borders.
< Beam moving to the right.
> Beam moving to the left.
}}}
Frames and syncs in seconds {{{
HR = HAt * DCF
HB = HBt * DCF
VR = VAt * HSF
VB = VBt * HSF
HFL = HFt * DCF
VFL = VFt * HSF
where {{{
HAt (horizontal active time)
Time (in seconds) needed by monitor to scan HR raster dots. Or,
in other words, time required for HR dot clock ticks.
VAt (vertical active time)
Time (in seconds) needed by monitor to scan VR scan lines. Or, in
other words, time required for VR scan line ticks.
HBt (horizontal blank time)
Time (in seconds) needed for HB dot clock ticks.
VBt (vertical blank time)
Time (in seconds) needed for VB scan line ticks.
HFt (horizontal frame time)
Time (in seconds) needed for HFL dot clock ticks.
VFt (vertical frame time)
Time (in seconds) needed for VFL scan line ticks.
}}}
}}}
Common notes {{{
HFL is overall number of raster dots in one horizontal frame, or, in other
words, in the one horizontal scan line. But the screen physical dimensions
are constant, though HFL (as well as HR) may vary. So, physical length (in
milimeters) of one raster dot depends from overall number of raster dots in
horizontal frame, i.e. depends from HFL. If we increase HFL, then physical
length of one raster dot will decrease, and, hence, with constant HR,
physical length of visible area will decrease and black margins will
increase. And vice versa: if we decrease HFL, then physical length of one
raster dot will increase, and, hence, with constant HR, physical length of
visible part of horizontal frame will increase and black margins will
decrease.
Duration of Front porch controls the size of right black margin. Duration
of Back porch controls the size of left black margin.
Hence, to move image to the right (left), we should move sync signal
relative to HB interval to the left (right), effectively changing front
and back porches. To squeeze (stretch) image in both (right/left or
up/down) directions (with constant HR or VR) we should change HFL (VFL)
and move sync signal in the same relative position in the HB interval, as
it was before, effectively changing raster dot physical length, and hence
area occupied by visible part of horizontal (vertical) frame. To change
all four visible area's dimensions (with constant RR), we should change
DCF, effectively changing (HFL * VFL) product.
}}}
DISCLAIMER. English language used here only for compatibility (ASCII only), so any suggestions about my bad grammar (and not only it) will be greatly appreciated.
понедельник, 31 января 2011 г.
Notes for XFree86 Video Timings howto
четверг, 13 января 2011 г.
Отчет о Новогоднем путешествии "Новогодний Quest 3-8 января 2011 года". Экипаж №40
(отформатированная версия оригинала с форума "Магистрали" )
3 января. Первый день путешествия Москва - Покров - Владимир
Мы совсем новички - пилот сел за руль три месяца назад. В таких поездках никогда не участвовали.
В 8-00 получили задание. Посмотрели карты, бегло пробежали контрольные точки, определили маршрут, около 13 часов выехали. Хотелось поскорее куда-нибудь доехать и отгадать загадку. Первым на нашем пути был Покров. В придорожном магазине нам указали, где стоит памятный крест и шоколадная фея. Радостно взяв свои первые КП, мы бросились к первому встречному мужчине с вопросом об островном монастыре. Мужчина оказался иностранцем, а женщина с ним - его жена - журналисткой. Они давно хотели съездить на остров, но никак не получается. Так в чем же дело? Поехали с нами! По пути наши новые знакомые рассказали нам историю монастыря. Журналистка в свое время сняла о нем фильм. В монастыре, поговорив с сестрами и объяснив кто мы, к нам пригласили матушку, которая открыла нам двери уже запертой церкви. В церкви нас попросили помочь перенести к алтарю целый ящик свечей. Мы, конечно, все сделали, и нам пообещали за это прощение грехов. Если бы все было так просто! Потом мы привезли наших знакомых домой, пообещали выслать им фотографии и поехали во Владимир, поскольку там была забронирована гостиница ( "Золотое кольцо"), которая на ближайшее время становилась нашей базой. С третьего раза заехали на парковку (все куда-то не туда тянуло), поселились в двухместном стандарте (горячая вода + завтрак + интернет, только распечатать несколько листов нам нигде в городе так и не удалось).
4 января. Второй день путешествия Владимир - Лакинск - Петушки - Покров - Новоселово - Владимир
Был посвящен городам на трассе Москва - Владимир. Лакинск особых трудностей не вызвал. Первые встречные мужики подробно рассказали, где найти памятный камень Лакину и как он выглядит. Спортсменов сами заметили. Поразились и порадовались их энергии и напору. Уличные КП в Петушках нашли быстро, посетили неохваченные в первый день музеи и собрали музейные КП. Пожалели, что нет времени отведать горячего шоколода. Решили обязательно приехать потом.
А вот памятник Гагарину поискали. Сначала проскочили поворот на мемориал, потом заехали слишком далеко к Андреевской церкви. Там встретили других участников автопробега. Служитель музея рассказал, где стоит стела, и после фотографирования мы уехали. Тихое торжественное место.
5 января. Третий день путешествия Владимир - Суздаль - Гаврилов Посад - Суздаль
Утром собрали все КП по Владимиру. Трижды пытались попасть с улицы Горького на Большую московскую, чтобы выехать в начало Октябрьского проспекта к памятнику воинам-интернационалистам (КП-58), но безуспешно. Один раз упилили по Судогодскому шоссе в поселок Коммунар, но потом все-таки пробрались куда надо по другим улицам. Дворника (КП-59) искали недолго - примерно знали, где он. А вот из Владимира в Суздаль выехали не той дорогой: выскочили на дорогу на Юрьев-Польский. Возвращаться не хотелось - надоело кружить. Решили продолжить движение, и в Старом дворе повернули по указателю направо. Дорога шла через небольшие деревни Малининский, Тарбаево, Туртино. Лед. Ветер свистит. Метель переметает. На горизонте земля соединяется с небом. Мы одни. А дорога вьется вверх-вниз и вдали уходит в небо. Вот так - дорога в небо... Замечтались-загляделись, и на вершине подъема на льду снесло нас на обочину в сугроб. Прочно так сели, по самое брюхо. Никто не проедет, одни мы, расчитывать не на что. Достали лопату - раскапывать! И вдруг - чудо: встречный джип! Останавливается. Спрашивает нас про трос, и через 20 минут после совместных усилий мы снова на дороге и в строю! Ура, мы спасены из снежного плена. Спасибо-спасибо.
Благополучно проезжаем Суздаль, мчимся в Гаврилов Посад, по пути замечая какая красота проносится мимо. Быстро находим там все точки и возвращаемся на ночевку в Суздаль (забронирована гостиница "Золотой ручей", напротив Кремля, все хорошо, но дороговато). Вечером погуляли по городу.
6 января. Четвертый день путешествия Суздаль - Юрьев-Польский - Городище - Подолец - Юрьев-Польский
Утро было посвящено памятникам в Суздале. Звонница в Спасо-Ефимьевском монастыре (КП-48) трудностей не вызвала. Удалось даже послушать звоны. Церковь Александра Невского нам указали жители (очень понравилась), а вот церковь Николая Чудотворца (КП-51) в Музее деревянного зодчества не нашли. Пришлось поехать в Кремль и на Кремлевской улице потолкаться с лошадьми. Это нас позабавило, мы быстренько сняли церковь и направились дальше.
Путь наш лежал в Юрьев-Польский. Поехали дорогой через Крапивье - Гавриловское - Цибеево - Обращиха. Эта дорога оказалась более оживленной, чем через Старый двор, и тоже очень красивой. В Юрьев-Польском сняли нужные точки быстро и поехали по деревням. Там тоже нашли все без проблем. Очень понравился Подолец, полюбовались и поехали назад. В пути поснимали закаты и дорогу. Канун Рождества. В гостинице нас ждала бутылка шампанского - подарок от администрации в честь праздника.
7 января. Пятый день путешествия Юрьев-Польский - Кольчугино - Киржач - Александров - Сергиев Посад - Александров
В этот день мы объехали наибольшее количество точек. В Кольчугино искали долго главную площадь с бюстом Кольчугина (КП-17), проверили три из четырех въездов в город в поисках сталевара, а вот Ильича нашли быстро - знали, где он спрятался. В Завалино произвела впечатление переправа через мостик из труб, а в Карабаново - проезд через арки под ж.д.
Очень понравился Махрищский монастырь, Киржач. Вечером прибыли в Александров. Музей уже был закрыт. Чтобы сэкономить время на следующий день, решили съездить в Сергиев Посад. Около Лавры быстро собрали все точки, скит нашли тоже легко, а вот скульптуру Николая Чудотворца пришлось поискать: спросить не у кого, сами найти не можем, темно, собаки воют, страшно. Огорченные, даже уезжать собрались, но вдруг по пустынной улице идет человек, мы - к нему. Рассказал нам все, оказалось - священник, нас расспросил - кто такие, зачем пожаловали. Мы ему про свой долгий путь поведали. Одобрил, благословил. К 12 часам ночи мы вернулись в гостиницу в Александров.
8 января. Шестой день путешествия Александров - Лукьянцево - Сергиев Посад-Москва
С утра - в Лукьянцево. Там на ледяной дороге встретили деда на велосипеде "Украина" и пообщались с местными молодыми людьми, которые смотрели на нас с завистью: Вы из "Магистрали"? Да. Здорово. Мы тоже хотим так же путешествовать. Следующий раз обязательно поедем.
Музей в Александрове очень понравился, а дороги - нет, самые разбитые. Нам сказали, что 31 декабря мэра города посадили за взятки. "Тебя посодют, а ты не воруй!" К 13-30 подъехали к Мытищам, потолкались в пробке. Вот мы и дома. Общий пробег составил 1334 км.
Послесловие.
Понравилось. Очень. Большое спасибо организаторам, работникам музеев, местным жителям, которые знают и любят свои города. Глоток свободы и свежего воздуха. Заряд надолго. Обязательно вернемся.
А также отдельная благодарность создателям открытой и бесплатной карты OpenStreetMap и навигационной программы Navit.
С уважением, экипаж № 40.
четверг, 16 декабря 2010 г.
[bash] Move array elements
Here is the task:
1. move all array elements to offset (index) 'off'. I.e. such, that first
array element will be at index 'off'.
2. make holes (sequences of unset elements) in the array by moving
different array parts to different offsets (indexes) off1, off2,
off3,.. .
This can't be done by
arr=( [i]="${arr[@]}" )
because "${[@]}" expansion works not as it should: it treats assignment prefix
specially, and in such case works the same as "${[*]}".
So, we should use some workaround, like
#!/bin/bash
declare -a arr=(
1
2
3
4
5
)
declare -i off=15
declare -p arr
b=${arr[0]}
unset arr[0]
arr=( [off]=$b "${arr[@]}" )
declare -p arr
This can be done simplier, but i'm not sure which version of bash this
requires
#!/bin/bash
declare -a arr=(
1
2
3
4
5
)
declare -i off=15
declare -p arr
arr=( [off]=${arr[0]} "${arr[@]:1}" )
declare -p arr
Second task can be solved like this
#!/bin/bash
declare -a arr=(
1
2
3
4
5
6
7
8
9
10
11
12
13
)
declare -i off1=15 len1=2 i1=0
declare -i off2=21 len2=3 i2=$((i1 + len1))
declare -i off3=40 len3=4 i3=$((i2 + len2))
declare -i off4=70 i4=$((i3 + len3))
declare -p arr
arr=(
[off1]=${arr[i1]} "${arr[@]:i1 + 1: len1 - 1}"
[off2]=${arr[i2]} "${arr[@]:i2 + 1: len2 - 1}"
[off3]=${arr[i3]} "${arr[@]:i3 + 1: len3 - 1}"
[off4]=${arr[i4]} "${arr[@]:i4 + 1}"
)
declare -p arr
Note, that all existed holes in the array after such operations would be lost.
[bash] Write filenames to array.
Here is two general tasks:
1. Assign strings (e.g. filenames) separated by '\0' from some input
stream to corresponding array elements.
2. Convert array into stream consisting from strings separated by '\0'.
I.e we have some bash script, which somewhere get such stream, e.g. by `find`
find $root -wholename "*/$project" -prune -print0
and then we want to place this filenames into array elements. But there is a
problem: we can't use '\0' in IFS, so we can't split find's output stream
using bash word splitting expansion. And we can't use any other character to
separate filenames, because filename may contain any character.
One possible method (i don't know other, though it may be) is to transform
stream to bash code and then `eval` it.
But here is another problem: we can't simply escape string with double or
single quotes, because string may contain un-escaped double or single quotes
inside (find does not escape characters in filenames, and hence we assume,
that string contain un-escpaed characters, i.e written "as is"). For example
a"' b.txt
than after escaping with double quotes
"a"' b.txt"
or with single quotes
'a"' b.txt'
In both cases some part of string remain unescaped and not-matched quote
appears. (Write anothre example with command).
So, we can't escape string by commands like sed or awk, which perfom text
editing without string parsing. But we can use bash itself to parse and escape
string properly, and then output escaped result, like this
eval "$(find $root -wholename "*/$project" -prune -print0 \
| sort -z -s \
| xargs -0 -x bash -c '
arr=( "$@" );
declare -p arr
' escape_filename)"
(the last argument 'escape_filename' is used as $0. It may be any, but
required for correct work. For details see chapter 7.4.2 from 'info find')
Script for bash instance, invoked by xargs, may do some other operations with
strings, like
j=15;
eval "$(find $root -wholename "*/$project" -prune -print0 \
| sort -z -s \
| xargs -0 -x bash -c "
set -- \"\${@#$root/}\"
set -- \"\${@%$project}\"
arr2=( [$j]=\"\$1\" \"\${@:2}\" );
declare -p arr2
" escape_filename)"
Here we delete leading ($root) and trailing ($project) portions of filename
and then assign resulted set of strings to array starting at index $j.
Variables $root, $project and $j are substituted by main bash process before
executing pipeline.
If you use in this script array name you want assign to in main script, no
further editing of output will be needed.
Here is another example to what incorrect quoting may lead to. If we have in
input stream string like this
a' rm -rf ~ '
then quote it with single quotes and add assignment (with sed, for example)
var='a' rm -rf ~ ''
when it will be eval-ed it execute command
rm -rf ~
Here is sample script
#!/bin/bash
str="a' ls ~ '"
to_eval="$(echo "$str" | sed -e"s/^/var='/;s/$/'/")"
eval "$to_eval"
четверг, 7 октября 2010 г.
[bash][part][draft]Return values from bash function
UPD. 2010.09.07
If we need to return several values from function, we can return through
- either setting some global variables,
- or through pipe ("stdout").
- or use both.
1. Global variables.
If we do not want to hardcode these global variable names into script, we
can pass their names as arguments and then use `eval` to assign values in
child function,
f() {
..
eval "$1=\"a b\""
}
f g1
declare -p g1
but in this case there is possible name conflicts between local function's
variables and global ones. If local variable has the same name as global,
it replaces it and we no longer able to set global variable in this
child function.
2. Pipe.
If we use pipe, we'll first gather all values from local child function's
variables and send them into pipe, but then, in caller function, we'll
parse pipe content to separate these values again and assign to proper
variables. This is waste. Also, order, in which results will be outputed
into pipe, should be fixed since cutting of result into separate values
again and assigning them to proper variables into caller function assumes
certain order. And if this order sometimes accidently will change,
returned values will be messed.
f3() {
local l1='a b'
local l2=' c d'
echo "\"$l1\""
echo "\"$l2\""
return 0
}
eval "$(echo "$(f3)" | sed -e'1s/^/g1=/;2s/^/g2=/')"
declare -p g1 g2
3. Mixed.
But we can use both those techniques at the same time to eliminate all
listed above problems each of them have.
Names of global variables to be set should be passed through arguments,
but in the pipe we'll send not raw values, but assignment statements with
already expanded values, and in caller function we should simply execute
them (assignment statements) in `eval`. In such case we get:
- no name conflicts, since we're no longer need global variables into
child function: we need only their names to generate proper output,
but values to them will be assigned into `eval` in caller function.
- no parsing of pipe by caller, since child function knows names of
variables to which caller want to assign values, and send the output
with already prepared assignment statements (values in this
assignment statements must be expanded in child function, since
child's local variables will be deleted upon it returns).
- Order of variables in child function's output may be any (except,
very special cases, where one variable use the value of previous
ones), since all assignments already written and no parsing
required.
f() {
local l1='local 1'
local l2='local 2'
echo "g1=\"$l1\""
echo "g2=\"$l2\""
return 0
}
declare g1=''
declare g2=''
eval "$(f g1 g2)"
declare -p g1 g2
Note1:
- when function is called through command substitution, then it'll be
executed in the subshell (unlike, when it's called normally, it is
executed in the main shell), so you can _not_ set any parent shell's
variable inside it. Thus, all parent shell's variable, which should be
set must be written into pipe in assignment statements form.
- you can not reinitialize array in parent shell using 'arr=( ${arr[@]} )'
syntax, when writing to pipe, if array elements contain shell
'metacharacters' (not IFS characters!). This is because in `eval`-ed
script all values will be already expanded (yet before it'll be written
into pipe) and will not contain any quotes, so they'll be broken into
tokens (words and operators) by shell 'metacharacters' far before any
expansions (including word splitting by 'IFS' characters) will take
place.
- the only way to reinitialize array in parent shell is to write either
'arr[index]=value' for each element or `declare -p arr` into pipe.
Here is example.
# ./t.sh {{{
#!/bin/bash
f() {
echo "SUBSH=$BASH_SUBSHELL" >/dev/tty
arr1[3]=' g h '
arr2[3]='i k l'
IFS=" "
echo "arr1=( ${arr1[*]} )"
echo "$(declare -p arr2)"
return 0
}
declare -a arr1=(
'a b'
'c '
)
declare -a arr2=(
' d'
' e f '
)
eval "$(f)"
declare -p arr1 arr2
# }}}
Note2:
Even if we don't need any results from function, which write result into pipe,
following considerations should be looked at:
- such function should always be invoked in subshell, even if we don't
need its results. Otherwise, it'll be executed in the same shell as
parent function and its actions with FDs may result in unexpected FD
table state for parent.
- FD=1 of such function should always be connected to something like
'/dev/null'. Otherwise, it can write results somewhere you don't expect
them for.
Example-1.
Here both functions f() and f2() return result through pipe, but function f()
does not need what f2() returns, so f2() is called (before f() moves pipe)
without subshell and without pipe connected. This results in the following
problems:
- f2() closes 'fd_t_stdout' in the parent function's FD table, though f()
thinks it's still open. This resluts in "9: Bad file descriptor error",
when f() tries to move pipe and f() loses correct value for stdout.
Hence, f() writes into pipe both output intended for 'stdout' and
intended for 'pipe'.
- f2() writes its output intended for 'pipe' into the _same_ pipe, as f()
uses, because it was invoked before f() moves its own pipe.
# cat ./t.sh {{{
#!/bin/bash
declare -r -i fd_t_stdout=9
declare -r -i fd_t_pipe=7
declare -r log_file='./2.tmp'
declare -r read_file='/dev/zero'
[ -f "$log_file" ] && rm -f "$log_file"
exec 2>>"$log_file" <"$read_file"
f2() {
echo "f2(): start: \$\$: $$, BASH=$BASHPID, SUBSH=$BASH_SUBSHELL" >>"$log_file"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f2(): move pipe" >>"$log_file"
eval "exec $fd_t_pipe>&1 1>&$fd_t_stdout-"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f2(): write to stdout"
echo "f2(): restore pipe" >>"$log_file"
exec 1>&$fd_t_pipe-
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f2(): write to pipe"
}
f() {
echo "f(): start: \$\$: $$, BASH=$BASHPID, SUBSH=$BASH_SUBSHELL" >>"$log_file"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
f2
echo "f(): move pipe" >>"$log_file"
eval "exec $fd_t_pipe>&1 1>&$fd_t_stdout"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f(): write to stdout"
echo "f(): restore pipe" >>"$log_file"
eval "exec 1>&$fd_t_pipe-"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f(): write to pipe"
}
eval "exec $fd_t_stdout>&1"
v="$(f)"
echo "v: '$v'"
eval "exec $fd_t_stdout>&-"
# }}}
# ./t.sh >|./1.tmp {{{
f(): start: $$: 12681, BASH=12683, SUBSH=1
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12683 root 0r CHR 1,5 920 /dev/zero
t.sh 12683 root 1w FIFO 0,6 31967 pipe
t.sh 12683 root 2w REG 8,7 43 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12683 root 9w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f2(): start: $$: 12681, BASH=12683, SUBSH=1
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12683 root 0r CHR 1,5 920 /dev/zero
t.sh 12683 root 1w FIFO 0,6 31967 pipe
t.sh 12683 root 2w REG 8,7 438 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12683 root 9w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f2(): move pipe
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12683 root 0r CHR 1,5 920 /dev/zero
t.sh 12683 root 1w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
t.sh 12683 root 2w REG 8,7 805 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12683 root 7w FIFO 0,6 31967 pipe
f2(): restore pipe
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12683 root 0r CHR 1,5 920 /dev/zero
t.sh 12683 root 1w FIFO 0,6 31967 pipe
t.sh 12683 root 2w REG 8,7 1175 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
f(): move pipe
./t.sh: line 36: 9: Bad file descriptor
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12683 root 0r CHR 1,5 920 /dev/zero
t.sh 12683 root 1w FIFO 0,6 31967 pipe
t.sh 12683 root 2w REG 8,7 1492 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
f(): restore pipe
./t.sh: line 43: 7: Bad file descriptor
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12683 root 0r CHR 1,5 920 /dev/zero
t.sh 12683 root 1w FIFO 0,6 31967 pipe
t.sh 12683 root 2w REG 8,7 1812 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
# }}}
# cat ./1.tmp {{{
f2(): write to stdout
v: 'f2(): write to pipe
f(): write to stdout
f(): write to pipe'
# }}}
Example-2.
If f() moves its pipe before calling f2(), but calls it also without subshell,
this will not be better:
- f2() will write its output intended for 'pipe' into 'stdout', since
stdout instead of pipe will be opened at FD=1, when f2() starts.
- f2() replaces FD='fd_t_pipe' value into parent function's FD table, so
saved f()'s pipe will be replaced with 'stdout' (since 'stdout' will be
at FD=1 in f2()). So, f() loses correct value for its pipe and writes
into 'stdout' both output intended for pipe and intended for 'stdout'.
# cat ./t.sh {{{
#!/bin/bash
declare -r -i fd_t_stdout=9
declare -r -i fd_t_pipe=7
declare -r log_file='./2.tmp'
declare -r read_file='/dev/zero'
[ -f "$log_file" ] && rm -f "$log_file"
exec 2>>"$log_file" <"$read_file"
f2() {
echo "f2(): start: \$\$: $$, BASH=$BASHPID, SUBSH=$BASH_SUBSHELL" >>"$log_file"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f2(): move pipe" >>"$log_file"
eval "exec $fd_t_pipe>&1 1>&$fd_t_stdout-"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f2(): write to stdout"
echo "f2(): restore pipe" >>"$log_file"
exec 1>&$fd_t_pipe-
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f2(): write to pipe"
}
f() {
echo "f(): start: \$\$: $$, BASH=$BASHPID, SUBSH=$BASH_SUBSHELL" >>"$log_file"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f(): move pipe" >>"$log_file"
eval "exec $fd_t_pipe>&1 1>&$fd_t_stdout"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
f2
echo "f(): write to stdout"
echo "f(): restore pipe" >>"$log_file"
eval "exec 1>&$fd_t_pipe-"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f(): write to pipe"
}
eval "exec $fd_t_stdout>&1"
v="$(f)"
echo "v: '$v'"
eval "exec $fd_t_stdout>&-"
# }}}
# ./t.sh >|./1.tmp {{{
f(): start: $$: 12713, BASH=12715, SUBSH=1
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12715 root 0r CHR 1,5 920 /dev/zero
t.sh 12715 root 1w FIFO 0,6 32160 pipe
t.sh 12715 root 2w REG 8,7 43 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12715 root 9w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f(): move pipe
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12715 root 0r CHR 1,5 920 /dev/zero
t.sh 12715 root 1w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
t.sh 12715 root 2w REG 8,7 409 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12715 root 7w FIFO 0,6 32160 pipe
t.sh 12715 root 9w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f2(): start: $$: 12713, BASH=12715, SUBSH=1
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12715 root 0r CHR 1,5 920 /dev/zero
t.sh 12715 root 1w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
t.sh 12715 root 2w REG 8,7 893 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12715 root 7w FIFO 0,6 32160 pipe
t.sh 12715 root 9w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f2(): move pipe
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12715 root 0r CHR 1,5 920 /dev/zero
t.sh 12715 root 1w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
t.sh 12715 root 2w REG 8,7 1349 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12715 root 7w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f2(): restore pipe
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12715 root 0r CHR 1,5 920 /dev/zero
t.sh 12715 root 1w REG 8,7 22 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
t.sh 12715 root 2w REG 8,7 1752 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
f(): restore pipe
./t.sh: line 43: 7: Bad file descriptor
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12715 root 0r CHR 1,5 920 /dev/zero
t.sh 12715 root 1w REG 8,7 63 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
t.sh 12715 root 2w REG 8,7 2105 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
# }}}
# cat ./1.tmp {{{
f2(): write to stdout
f2(): write to pipe
f(): write to stdout
f(): write to pipe
v: ''
# }}}
Example-3.
And here is how this can be done correctly. Shortly, we should call f2() like
( f2 >/dev/null )
In this case, f2() result will be discarded (but we do not need it, right?), but
f() will output its result as expected.
# cat ./t.sh {{{
#!/bin/bash
declare -r -i fd_t_stdout=9
declare -r -i fd_t_pipe=7
declare -r log_file='./2.tmp'
declare -r read_file='/dev/zero'
[ -f "$log_file" ] && rm -f "$log_file"
exec 2>>"$log_file" <"$read_file"
f2() {
echo "f2(): start: \$\$: $$, BASH=$BASHPID, SUBSH=$BASH_SUBSHELL" >>"$log_file"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f2(): move pipe" >>"$log_file"
eval "exec $fd_t_pipe>&1 1>&$fd_t_stdout-"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f2(): write to stdout"
echo "f2(): restore pipe" >>"$log_file"
exec 1>&$fd_t_pipe-
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f2(): write to pipe"
}
f() {
echo "f(): start: \$\$: $$, BASH=$BASHPID, SUBSH=$BASH_SUBSHELL" >>"$log_file"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f(): move pipe" >>"$log_file"
eval "exec $fd_t_pipe>&1 1>&$fd_t_stdout"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
( f2 >/dev/null )
echo "f(): write to stdout"
echo "f(): restore pipe" >>"$log_file"
eval "exec 1>&$fd_t_pipe-"
lsof -a -p $BASHPID -d'^mem,^txt,^rtd,^cwd' >>"$log_file"
read -rsn1
echo "f(): write to pipe"
}
eval "exec $fd_t_stdout>&1"
v="$(f)"
echo "v: '$v'"
eval "exec $fd_t_stdout>&-"
# }}}
# ./t.sh >|./1.tmp {{{
f(): start: $$: 12777, BASH=12779, SUBSH=1
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12779 root 0r CHR 1,5 920 /dev/zero
t.sh 12779 root 1w FIFO 0,6 32627 pipe
t.sh 12779 root 2w REG 8,7 43 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12779 root 9w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f(): move pipe
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12779 root 0r CHR 1,5 920 /dev/zero
t.sh 12779 root 1w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
t.sh 12779 root 2w REG 8,7 409 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12779 root 7w FIFO 0,6 32627 pipe
t.sh 12779 root 9w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f2(): start: $$: 12777, BASH=12784, SUBSH=2
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12784 root 0r CHR 1,5 920 /dev/zero
t.sh 12784 root 1w CHR 1,3 898 /dev/null
t.sh 12784 root 2w REG 8,7 893 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12784 root 7w FIFO 0,6 32627 pipe
t.sh 12784 root 9w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
t.sh 12784 root 10w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f2(): move pipe
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12784 root 0r CHR 1,5 920 /dev/zero
t.sh 12784 root 1w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
t.sh 12784 root 2w REG 8,7 1410 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12784 root 7w CHR 1,3 898 /dev/null
t.sh 12784 root 10w REG 8,7 0 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f2(): restore pipe
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12784 root 0r CHR 1,5 920 /dev/zero
t.sh 12784 root 1w CHR 1,3 898 /dev/null
t.sh 12784 root 2w REG 8,7 1874 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12784 root 10w REG 8,7 22 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
f(): restore pipe
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
t.sh 12779 root 0r CHR 1,5 920 /dev/zero
t.sh 12779 root 1w FIFO 0,6 32627 pipe
t.sh 12779 root 2w REG 8,7 2248 1590776 /home/sgf/new_tree/src/send_sms/2.tmp
t.sh 12779 root 9w REG 8,7 43 1121662 /home/sgf/new_tree/src/send_sms/1.tmp
# }}}
# cat ./1.tmp {{{
f2(): write to stdout
f(): write to stdout
v: 'f(): write to pipe'
# }}}
