Hát nyilván a shell-től függő dolgot:
# ksh: A="101" B="202" C="lonc"
# ksh93: A="101" B="202" C="lonc"
# bash: A="" B="" C=""
# dash: A="" B="" C=""
- NevemTeve blogja
- A hozzászóláshoz be kell jelentkezni
Hozzászólások
Talan nem veletlen a script elejen az interpreter kivalasztasa?
#!/bin/bash
echo 101 202 lonc | { read A B C ; printf 'A="%s" B="%s" C="%s"\n' "$A" "$B" "$C" ; }
- A hozzászóláshoz be kell jelentkezni
Ez egy rossz válasz, de valamire ráhibáztál! :-D
Linux parancssorból próbálva - ahol a default shell bash - szintén nem működik, a tied meg igen. És csak akkor, ha a printf is az utasítászárójelen belül van. Az interpreter lehet /bin/sh is, amire Bourne shellt kellene indítania és mégis hasonlóan működik mindkét esetben.
Tehát fogtál egy olyan side effectet, amikor a transzparens utasítászárójel az stdin viselkedését módosítja a read számára. :-DD
- A hozzászóláshoz be kell jelentkezni
Programozas kozben tisztaban kell lenni a valtozok hataskorevel, eletciklusaval. Shellek eseten azzal is, hogy mikor nyilik sub-shell, es hogyan kozlekedhetnek koztuk a valtozok (es egyebek, "environment").
transzparens utasítászárójel
Transzparens?
stdin viselkedését módosítja a read számára
Ezt nem ertem...
Az van, hogy a pipe uj sub-shell(eket)t nyit. Szimpla parancs (pl. read onmagaban) eseten arra az egyetlen parancsra. A zarojel itt egy utasitascsoport, amelynek egeszere nyilik a subshell. Sub-shellbol, legyen az egy vagy tobb parancsra, fuggvenyhivas stb., nem turemkedik ki semmilyen valtozas (environment) csak ugy.
https://www.gnu.org/software/bash/manual/bash.html, 3.2.3, 3.7.3, 4.3.2
-- szek -- es 3.2.5.3 a ( ... ) es { ...; }-rol
- A hozzászóláshoz be kell jelentkezni
A "transzparens" alatt a shubshell nyitasat erted? Mert az alapbol tenyleg mas a {} es () eseten. Ezekbol latszik, hogy a subshellbol "( ... )" nem jon ki a valtozo, a "transzparens" csoportbol pedig igen:
A="alma" ; echo $A ; { echo $A ; A="kapocs"; echo $A ; } ; echo $A
alma
alma
kapocs
kapocs
A="alma" ; echo $A ; ( echo $A ; A="zarojel"; echo $A ; ) ; echo $A
alma
alma
zarojel
alma
Azonban a fenti peldaban mindenkepp nyilik subshell a pipe miatt hiaba hasznalunk "{ ...; }"-t (shopt azert bekavarhat talan, hogy a pipe utolso lepese hol is fut le), es a subshellbol nem jon ki a valtozo...
- A hozzászóláshoz be kell jelentkezni
Mint látod, ez sem igaz. Az szülő shell potleszarja, hogy honnan jön a szöveg - és kiírja.
Az első példából látszik, hogy egy environment alatt fut minden.
A másodikban pedig "nincs visszafelé öröklődés", tehát a subsell a sajátját használja..
- A hozzászóláshoz be kell jelentkezni
A szulo shell mit ir ki? Honnan jon neki szoveg? Mivan?
Az elso peldaban nincs subshell. A masodikban van.
A topicnyitoban es az arra adott valaszomban is van subshell a pipe miatt. Ezert nem marad meg az $A erteke a pipe befejezese utan.
- A hozzászóláshoz be kell jelentkezni
Bocsánat, úgy látszik rosz az ángolom. De amit írtam - a tükörfordítás miatt - szintén igaz, bár hibás. (kiemelés tőlem)
part of a pipeline are also executed in a subshell environment
Tehát nem subshell futtatja, hanem fork+exec történik. Igaz, mindez lehetetlenné teszi a "visszafelé öröklődést".
Vagyis az utasítászárójeles megoldásod azért működött, mert abban a pipeline elemben közös environmentet használt a két parancs, de ez még nem csinált globális változókat. Én a subshell environment helyett separated-et írtam volna.
A process substitution azért tudja megoldani, mert kikerüli a pipeline használatát egy extra pipe fd-vel. Iyenkor a builtin sem indul el külön, tehát a globális változót képes írni.
- A hozzászóláshoz be kell jelentkezni
No, itt égnekállt a (maradék) hajam. Ekkora faszságot!
De kivédted, mert tényleg benne van a bash leírásában.
Így csak annyi a hibád, hogy minden hülyeséget elhiszel.
Mutatom:
gzip -dc bigfile.gz |cat|cat >bigfile &
pstree -p
|-sshd(754)-+-sshd(3534)---sshd(3578)---bash(3586)-+-cat(4777)
| | |-cat(4778)
| | |-gzip(4776)
| | `-pstree(4779)
Te meg mutasd meg az ujjocskáddal a subshellt! ;)
gzip -dc bigfile.gz | cat | ( echo hello > /dev/null; cat ) > bigfile
pstree -p
|-sshd(754)-+-sshd(3534)---sshd(3578)---bash(3586)-+-bash(4821)---cat(4822)
| | |-cat(4820)
| | |-gzip(4819)
| | `-pstree(4823)
No, itt már nagy nehezen sikerült egy subshellt indítani (4821) - az meg így néz ki.
Each command in a multi-command pipeline, where pipes are created, is executed in its own subshell, which is a separate process
Tehát ez nem így működik.
Sajnos az első példában a pipeline minden eleme egy shell (3586) alatt fut.
Egyszerűbb, ha megjegyzed az alapszabályt: Egy pipeline az egy unix command, tehát kizárható, hogy egyszerre több environmentet használjon. Az első példában a pipeline minden eleme a szülő shell változóit használja. Ha biztos akarsz lenni, akkor beállíthatod a set -a (a=allexport) shell opciót. Sőt a subshell is örököl mindent, mert a fork() miatt is örökli az eredeti environmentet.
A transzparens utasítászárójel {} értsd: Nem csinál semmit, csak a parancsokat groupolja.
A subshell () új shellt indít, az environment öröklődik, de subshell alatt módosított environment nem hat vissza a subshellt indító shell változóira. Tehét a subshell environment a subshell számára lokális. Mutatok egy példát, ami ezt a módszert használja (igen pszeudo kód):
új munkaterület kijelölése
environment beálítása az aktuális munkaterülethez
( a munkaterület kezelése ) &
loop
Kb. így működik egy webszerver is, csak munkaterület->request.
stdin viselkedését módosítja a read számára
Ezt nem ertem...
A read szemmel láthatóan nem olvas a stdin-ről. Azzal, hogy utasítászárójelek közé tetted, mégis olvas. Márpedig annak a kapcsos zárójelnek nincsilyen tulajdonsága, hogy a stdin mibenlétét átfogalmazza. Az ezzel groupolt parancsok kezelhetők egy parancsként. Tehát pl. ha a zárójelek közti utasításcsoport olvassa a stdint és ír a stdoutra, akkor a zárójeles kifejezés úgy viselkedik, mint egyetlen parancs. Pl.:
#Írj mindig hellot!
....| { cat >/dev/null; echo hello; } |...
- A hozzászóláshoz be kell jelentkezni
Te meg mutasd meg az ujjocskáddal a subshellt!
Nono, fiam, kőből talán nem lehet hidat építeni? :) Ugyanis a példád rossz, a cat és a gzip önálló, külső programok. Ezzel szemben a read az a shell belső parancsa, minek következtében ha a read-be pipe-olsz, el kell indulnia a shellnek, amely részeként fut a read, de egyben ennek a shellnek a teljesen elszeparált memória foglalásaként jelennek meg a read változói. Utána kilép, azt a memóriaterületet felszabadítja, huss, már nincs ott az értéket kapott változó, a külső shell meg néz hülyén, hogy ki akarnak íratni olyan változót, amely nem kapott értéket. Hát jó, akkor az legyen ott egy nagy üresség.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Hátugye. A builtin érterlme az, hogy semilyen külső programot vagy forkot ne kelljen végrehajtani. A shell, mint interpreter nem úgy működik, hogy minden sor után elindít egy shellt. ;)
Ha igazad lenne, akkor az echo $$ nem tudná kiírni a saját PID-jét. (Az echo is egy builtin.)
- A hozzászóláshoz be kell jelentkezni
Itt azért becsúszott egy kis félreértés; ha nem csalódom nagyot, az alábbiak mind ugyanazt írják ki:
echo $$; /usr/bin/echo $$; sh -c "echo $$"; sh -c "/usr/bin/echo '$$'"
- A hozzászóláshoz be kell jelentkezni
Pontosabban, van echo builtin, es van /usr/bin/echo is. Es mindketto vigan kiirja ugyanazt az erteket a $$-ra.
Viszont az meg veletlenul sem a "sajat PID-je".
1) A beepitett echo-nak nincs PID-je, csak a shellnek.
2) A /usr/bin/echo $$ pedig nem a sajat PID-jet irja, hanem a hivo shell PID-jet, mivel az fejti ki a $$-t, amit a /usr/bin/echo mar szamkent kap meg.
- A hozzászóláshoz be kell jelentkezni
A "saját PID" (a gyengébbek kedvéért).
bash(2)--bash(3)--echo(4)
1) Ha nem indít subshellt (=3), akkor a kiírt érték 2.
2) Ha indít, akkor a kiírt érték 3.
Próbáld meg kiíratni a subshell PID-jét!. (3)
Bár az echo builtin nem külö processz, ezért a saját pid == bash PID (2)
- A hozzászóláshoz be kell jelentkezni
Nem az echo az érdekes, hanem a $$ kiértékelése ebből a szempontból.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Most mar megint mirol beszelsz? Maradhatunk abban, hogy az echo semmikepp sem a sajat PIDjet irja ki?
Beepitett esetben az a bash-nak van PID-je, mivel az echo nem egy processz.
Kulso echo eseten pedig nem a sajat PID-jet irja az echo, hanem a hivo (bash) altal parancssorban atadott stringet, amit a bash a $$ kiiertekelesevel allitott elo.
Egyeb ehhez?
- A hozzászóláshoz be kell jelentkezni
Hagyd, neked van igazad, bár azt azért hozzátenném, hogy ha az echo belső parancs (ellenőrizhető a "type echo" vagy "whence echo" paranccsal), akkor bash=echo meg bash=cd, meg bash=set, stb :-)
- A hozzászóláshoz be kell jelentkezni
> Ha igazad lenne, akkor az echo $$ nem tudná kiírni a saját PID-jét. (Az echo is egy builtin.)
Dehogynem. Azért tudja a script PID-jét kiírni, mert a változóbehelyettesítés az utasítás végrehajtása előtt megtörténik. Azaz itt nem teljesen lineáris a végrehajtás sorrendje. Ez abból is látszik, hogyha valaminek az outputját nézed (pl: echo $(uname -r)) akkor a kiértékelendő cucc jóval előbb meghívódik, mint az előtte levő echo. És ez minden változókiértékelésnél így van, nincs kivétel, a $barakarmit is előbb behelyettesíti, majd ráadja a vezérlést a parncsra, ami előtte van.
Így gyakorlatilag az echo nem "tudja" a script/shell PID-jét - azt az echo-t elindító parent shell tudja, és helyettesíti be, még mielőtt az echo - builtin vagy sem - vezérléshez jutna. Maga az echo már egy statikus integert kap.
- A hozzászóláshoz be kell jelentkezni
A transzparens utasítászárójel {} értsd: Nem csinál semmit, csak a parancsokat groupolja.
Ennek azert fussunk neki meg egyszer.
Az siman lehet, hogy a pipe, nem mindig hoz letre _subshellt_. Ugy latom, kioptimizalja erosen, hogy kell-e neki. Szimpla parancshoz nem kell feltetlenul. Ezt nem reszletezik a doksiban.
Viszont probald ki ezeket:
sleep 5 | sleep 5 | sleep 5 & pstree -p > /tmp/ww
sleep 5 | { sleep 5; } | { sleep 5; } & pstree -p > /tmp/qq
Latod mar a subshelleket?
- A hozzászóláshoz be kell jelentkezni
Sot, nezz ra erre:
sleep 1 | sleep 2 | read | read | sleep 3 & pstree -p > /tmp/rr
- A hozzászóláshoz be kell jelentkezni
Nem bonyolult igazából a logika.
Akkor hoz létre subshellt, ha a pipe után shell builtin parancs jön. A pipe azt jelenti, hogy ez előző process stdout-ját kösd a következő process stdin-jére. Egy shell builtin-nek máshogy nem tudna stdin-t csinálni, mert a parent shell stdin-je már foglalt, így kénytelen egy subshellt indítani. Ebből a szempontból a { } egy shell builtinnek számít, mert a benne foglalt - esetlegesen több - parancsot egy shell interpreternek kell értelmeznie.
A sleep (ami rendszeren nézem legalábbis, rhel-like) nem shell builtin, hanem sima külső process, amit a shell exec-elni tud és beköti neki az stdin-re a pipeban előző parancs stdoutját. Ergo nem kell subshell.
(Gondolhatná az ember, hogy a shell builtinnek miért pont az stdin-ről kell olvasnia, nem csinálhatna-e helyette a parent shell egy másik file descriptort? Gondolom visszafele-kompatibilitási okokból nem raktak bele ilyen varázs-logikát. Felteszem a ksh valami ilyen trükkel játssza ki ezt a speciális helyzetet, de most nincs kéznél olyan gép, ahol volna ksh, hogy meg tudjam nézni.)
Régóta vágyok én, az androidok mezonkincsére már!
- A hozzászóláshoz be kell jelentkezni
Inkább a standard vagy egységes működés miatt nem kerülik meg a stdin-t. Igen, ez így szarul hangzik, mégis ez a logikus: ha minden más esetben a stdin-stdout-tal machinálunk, a builtinek se lehetnek kivételek. Így sokkal egyszerűbb kódolni is.
- A hozzászóláshoz be kell jelentkezni
Van még néhány lehetőség, mert a stdin, stdout, stderr csak default.
Más fd-hez eszköz az exec, amivel nyitni-csukni lehet a csatornákat, de interprocessz kommunikációhoz barátod a coprocess.
- A hozzászóláshoz be kell jelentkezni
Érdekes. Miközben a man read ezt mondja:
read - Read a line from the standard input and split it into fields.
Mindegy, mert a bináris ugyanazt teszi mint a builtin.
Tanulság: Mivel van még 572 módja az értékadásnak, talán nem ez az igazi. ;)
De legalább a TEXT HERE is ugyanúgy működik.
Használd a read-et a kézi bevitelhez.
- A hozzászóláshoz be kell jelentkezni
Most hogy így mondod, tényleg van egy /usr/bin/read
Aixon. Talán csak azért, hogy igazolják Váncsa István mondását: Némely emberi agy egészen másképp működik, mint ahogy azt hétköznapi észjárással elvárhatnánk.
- A hozzászóláshoz be kell jelentkezni
Ha a beepitett es a kulso parancs ugyanazt csinalja (tegyuk fel erre az alap peldara), akkor tok mindegy, hogy melyiket hasznalod.
A kulonbozo interpreterek (es azok verzioi) nagyon nem kompatibilisek egymassal.
$ bash --version
GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)
$ shopt
...
compat31 off
compat32 off
compat40 off
compat41 off
compat42 off
compat43 off
compat44 off
...
Hogy mindegyik vegrehajt neked egy hasonlo szintaktikaval irt kodot, az a puszta veletlen muve.
- A hozzászóláshoz be kell jelentkezni
Ez nagyon igaz, ha mondjuk a printf
-ről beszélünk, de milyen lenne egy külső programmal megvalósított read
?
Érdekesség: a resize -s
ajánlott meghívása ez:
$ resize -s
COLUMNS=136;
LINES=35;
export COLUMNS LINES;
$ eval $(resize -s)
# nincs output, de a COLUMNS és LINES felveszi az új értéket
- A hozzászóláshoz be kell jelentkezni
Pontosan ugyanolyan. Csak a magyarázat lenne egyértelműbb. Egy külső stand-alone read parancs nem tud környezeti változót módosítani a parent shelljében. Általánosságban a hívási láncban felfele nem tudsz környezeti változókat terjeszteni.
Ez egy elég kellemetlen szívás a shell scriptekben. Sok esetben megharap és elég agytorzító módon kell gondolkodnod, hogy kapásból úgy írd a kódot, hogy elkerüld.
A resize-nak a -s paramétere, amit lényegében kódot generál, majd eval-olnod kell a parent shellben, egy lehetséges, nem túl szép workaround. (Erre a problémára általában nem szépek a workaroundok.)
Nyilván lehetne egy olyan read parancsot csinálni, ami az stdout-ra kiköpi a match-elt értékeket változódefinícióként és eval-al beolvastatni.
Én a fenti mintaproblémádra kétféle megoldást szoktam (attól függően, hogy szabad-e bash-izmusokat használni):
INPUT_LINE="101 202 lonc"
A=`echo "$INPUT_LINE" | cut -d' ' -f 1`
B=`echo "$INPUT_LINE" | cut -d' ' -f 2`
C=`echo "$INPUT_LINE" | cut -d' ' -f 3`
vagy ha szabad bash-t használni:
INPUT_LINE="101 202 lonc"
A="${INPUT_LINE%% *}"
TEMP="${INPUT_LINE#* }"
B="${TEMP%% *}"
TEMP="${TEMP#* }"
C="${TEMP%% *}"
Ez utóbbi bár ocsmányabbul néz ki, lényegesen gyorsabb, mert egyáltalán nem hív subprocesst.
Régóta vágyok én, az androidok mezonkincsére már!
- A hozzászóláshoz be kell jelentkezni
Igen... ez a pipe + read marcsak ilyen :/ Nem mondom hogy igy a legjobb a felhasznalonak de legalabb a bash/dash is ertheto, logikus hogy miert igy (vagyis miert nem, na :)).
- A hozzászóláshoz be kell jelentkezni
Pipe esetén az a read subshelben fut. Az A, B, C változók megkapják ugyan az értékeket, majd azzal a lendülettel ki is lépsz a subshellből, eldobod az értékeiket, majd az eredeti shell A, B, C változóit íratod ki, amelynek senki sem adott értéket. Jó hír, hogy így működni fog:
read A B C < <(echo 101 202 lonc)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Gratula! Ez a korrekt megoldás. Inkább az a jó magyarázat, hogy a read néha mégsem olvas az stdinről.
- A hozzászóláshoz be kell jelentkezni
Az előttem sem teljesen tiszta, hogy ebben a konstrukcióban hogyan valósulhat meg az, hogy a read built-in parancsot az aktuális shell futtatja, így a változók is az aktuális shellben lesznek. Az a balra kacsacsőr pedig olyan, mint amikor file-t irányítok a read-be, csak most a file a <(commands) outputja lesz.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Úgy, hogy pontosan azt csinálja. File-t irányít a read-be. Az echo megy subshell-be, a parent shell csinál neki named pipe-ot a filerendszerben, amibe beirányítja a subshell stdout-ját. A read-nek meg a named pipe filenevét helyettesíti be (ps-ben, top-ban látszik is). Igazából mkfifo-val meg background process indítással "lábbalhajtós" módon is le lehet programozni ugyanezt a logikát, csak egy kulturáltabb szintaxist adtak rá.
Régóta vágyok én, az androidok mezonkincsére már!
- A hozzászóláshoz be kell jelentkezni
Érdekesség:
ls -l <(echo 'nested command')
- A hozzászóláshoz be kell jelentkezni
Szépen megmutatja, mi is az a file.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Aix: prw------- 1 user group 0 Aug 6 21:38 /tmp//sh-np.mKvqaa
Linux: lr-x------ 1 user group 64 Aug 6 21:39 /dev/fd/63 -> pipe:[44560715]
- A hozzászóláshoz be kell jelentkezni
Jól látszik, hogy a shell megvalósításán múlik, hogy mit használ.
ksh93 alatt :
cr-xr-xr-x 1 user group 0x7 aug. 7 08:05 /dev/fd/4
bash alatt :
prw------- 1 user group 0 aug. 7 09:30 /tmp//sh-np.NlPS9b
yash alatt pedig ez a szintaxis nem processz helyettesítés (process substituttion), hanem processz átirányítás (process redirection)
- A hozzászóláshoz be kell jelentkezni
Ez az entitás, amit írtál a process substitution: <(command)
- A hozzászóláshoz be kell jelentkezni
Amúgy ezt process substitution-nek hívják, ha valaki esetleg rá akar keresni. (Én is hajlamos vagyok elfelejteni, hogy mi a neve ennek a módszernek.)
Itt is van subshell, csak fordított szereposztásban, az echo fut subshellben, ami jelen esetben pont nem probléma. A motorháztető alatt csinál pár named pipe-ot és valójában a read-be valami /dev/fd/<valami> van beleirányítva.
Régóta vágyok én, az androidok mezonkincsére már!
- A hozzászóláshoz be kell jelentkezni
Teljesen ugy nez ki, mint ha a megfelelo scriptnyelvet a neki megfelelo ertelmezovel kellene futtatni.. mik vannak!
nyos@shodan ~ $ cat bin/teveteszt
echo 101 202 lonc | read A B C
printf 'A="%s" B="%s" C="%s"\n' "$A" "$B" "$C"
nyos@shodan ~ $ bash bin/teveteszt
A="" B="" C=""
nyos@shodan ~ $ csh bin/teveteszt
read: Command not found.
A: Undefined variable.
nyos@shodan ~ $ tcsh bin/teveteszt
read: Command not found.
A: Undefined variable.
nyos@shodan ~ $ xonsh bin/teveteszt
xonsh: For full traceback set: $XONSH_SHOW_TRACEBACK = True
xonsh: subprocess mode: command not found: read
Did you mean one of the following?
red: Command (/bin/red)
ed: Command (/bin/ed)
rar: Command (/usr/bin/rar)
tred: Command (/usr/bin/tred)
head: Command (/usr/bin/head)
Command 'read' not found, did you mean:
command 'head' from deb coreutils (8.30-3ubuntu2)
command 'aread' from deb atm-tools (1:2.5.1-4)
command 'rear' from deb rear (2.5+dfsg-1)
command 'red' from deb ed (1.16-1)
Try: sudo apt install <deb name>
A="$A" B="$B" C="$C"
Sot, ez meg a normalisabb programnyelvekre is igaz:
nyos@shodan ~ $ gcc bin/teveteszt
/usr/bin/ld:bin/teveteszt: file format not recognized; treating as linker script
/usr/bin/ld:bin/teveteszt:1: syntax error
collect2: error: ld returned 1 exit status
nyos@shodan ~ $ python bin/teveteszt
File "bin/teveteszt", line 1
echo 101 202 lonc | read A B C
^
SyntaxError: invalid syntax
Nyilvan Javaban/C#-ban/Rustban is csak a hibauzenet szovege terne el, ott sem futna. Pikachu
A strange game. The only winning move is not to play. How about a nice game of chess?
- A hozzászóláshoz be kell jelentkezni
Most akkor a gcc meg a javac nem számít Posix-szerű shell-nek?! Megyek, beülök egy sarokba, és szomorgok egyet.
- A hozzászóláshoz be kell jelentkezni
Várom Zahy mester szakértését is :-)
- A hozzászóláshoz be kell jelentkezni
Nincs itt semmi látnivaló, fent kb. minden lényegeset leírtak már (vannak marhaságok - "a read nem olvas a stdin-ről" - szerencsére kiigazítva; meg nem annyira marhaságok is).
Shell és a zárójelek:
{ } - a kapcsos zárójel NEM generál önmaga subshell-t (így a shell saját változói látszódnak), a közé zárt parancsok csoportosítására szolgál, a nyitó zárójelet el kell választani a mögé írt parancstól (szóköz, tabulátor vagy akár újsor), a záró zárójel pedig "parancs pozícióban" kell álljon, azaz vagy ; vagy & vagy soremelés után, azaz:
{ parancs1;parancs2;}
( ) - a kerek zárójel önmagában generál egy subshell-t a benne levő parancsok számára (melyben amúgy az eredeti shell lokális változóinak saját másolatai is elérhetőek). Ugyanúgy csoportosít, mint a kapcsos, de mind a nyitó, mind a záró tag akár egybe is írható a mögötte (előtte) levő paranccsal, azaz írható így:
(parancs1;parancs2)
Mind a kettő hatása, hogy a be- és kimenetek közösek lesznek a közbezárt parancsoknak, és értelemszerűen a ki- és bemenet (meg az stderr) együtt irányítható át. Lásd: mást jelenít meg a
cat < f ; cat < f
mint a
{ cat ; cat ; } < file
és ugyanez az eredménye kimenet szempontjából a
( cat ; cat ) < file
verziónak. A zárójel nélküli forma 2x írja ki az f file tartalmát, a zárójelesek meg egyszer
A { } specialitása, hogy a parancsok az eredeti shellben hajtódnak végre, így azon parancsok hatása, amelyek shellen belül okoznak változást, azok a zárójelen kívül (értelemszerűen: utána) is láthatóak. Ilyenek
- A változókkal kapcsolatos dolgok (változó létrehozása, módosítása, törlése, jellegének megváltoztatása - pl. A=5; unset A ; readonly A; export A ; typeset / declare parancs).
- A shell belső beállításaival kapcsolatos beállítások - ezeket jellemzően a set paranccsal szokás beállítani, de ide tartozik pl. a bash-féle shopt
- Hasonlóan kapcsos zárójelen belüli könyvtárváltás a zárójelen kívül is látható, míg kerek zárójelen (azaz egy al-shellen) belüli cd hatása nem érzékelhető a zárójelen kívül. (de amelyik shellnek van pushd, popd - és hasonló - directory stack kezelője, azok is így működnek)
- Shell aliasok (alias, unalias parancs) és shell-függvények (function parancs, unset -f parancs) létrehozása és törlése
- umask beállítás
- átadott paraméterek eldobása a shift paranccsal (vagy épp újrainicializálása a set -- paranccsal)
Ami pedig a pipe és a read ilyetén működését illeti, elég jól dokumentálják a különböző manualok. Pl. a pdksh manual legvégén, a BUGS szekcióban ez áll:
BTW, the most frequently reported bug is
echo hi | read a; echo $a # Does not print hi
I'm aware of this and there is no need to report it.
Mindez azért szerepel így, mert már az eredeti ksh (ma ksh88-ként szoktunk rá hivatkozni) is, meg az új verziójú ksh (ksh93) is ettől eltérően működik - ahogy a kérdésfelvetésben is látszik (szerintem amúgy némi dup / dup2 rendszerhívás-mágia segítségével lehetne megoldani). De nagyjából semmi más nem optimalizálja ki ezt az egyetlen speciális helyzetet (sh, ash, dash, bash, yash egyike sem - sőt látható, a pdksh sem, és gyanítom az ezen alapuló mirsh sem).
Ui: Újszülötteknek azt szoktam javasolni, hogy szorgosan olvasgassák a dokumentációt, egészen meglepő dolgokat lehet benne találni. (Pl. van olyan shell manual, amely explicit leírja, hogy egyes régebbi Bourne-shell szintaxist használó shellekben a ^ karakter a | szinonímája - és milyen jól jött ez a tudás, amikor az egyik rendszer konzolján gyakorlatilag lehetetlen volt elérni a | jelet.) Megtalálhatóak olyan - mai napig használható - csemegék, mint hogy a különböző összetett parancsok (for, while, until parancsok) használatakor a do / done helyett nyugodtan lehet a klasszikus { } zárójelpárt használni. :-) Ha valaki szeretne rendesen megismerkedni a shelle(kke)l (ez kb. minden *X admin számára nélkülözhetetlen lenne), akkor minimum azt értse meg, hogy mi a különbség a shell belső és külső parancsainak végrehajtása között, de bátrabbak elindulhatnak megkeresni, hogy mi a különbség a (normál) belső parancsok és az un. (POSIX-) speciális belső parancsok végrehajtása között; ad absurdum, azt hogy melyek melyek, és ha nem tudom ezt fejből, hogyan lehet ezt szabályos eszközzel megkérdezni (hint type és whence parancsok, és megint csak a pdksh manualja elég korrekten leírja ezeket. Külön öröm, hogy pl. a bash nem jelzi ezt a különbséget, és ugyan a man-ban hivatkozik a POSIX special builtins nevű izére, de ki már nem fejti, hogy mi a frász is ez, és melyek ezek a parancsok. Tudom, GNU világban nem man van, hanem info. További pontosítás: mivel a bash ezeket csak POSIX módban különbözteti meg, ezért ezeket az infókat is csak POSIX-módban jeleníti meg.).
Jav:
kicsit pontosítottam és részletesebbé tettem a zárójelekben másként működő dolgok listáját, megjelent a bash POSIX-módja, és lett egy utóirat.
- A hozzászóláshoz be kell jelentkezni
> a do / done helyett nyugodtan lehet a klasszikus { } zárójelpárt használni.
Mindig tanul az ember újat. Pedig párszor olvastam már a Bash dokumentációját, de valahogy ezen nem akadt meg a szemem. Mennyivel egyszerűbb lesz így kódolni...
- A hozzászóláshoz be kell jelentkezni
Majdnem használható. Konkrétabban: öntökönszúrás használni, mert hozzászoksz, és megpróbálod valamilyen 'mindenhol kell fusson'-szerű kontextusban is használni, és akkor jön egy ilyen:
dash -c 'for i in 1 2 3; { echo $i; }'
dash: 1: Syntax error: "{" unexpected (expecting "do")
- A hozzászóláshoz be kell jelentkezni
Így visszatekintve elég sok furcsaságot vélek látni, pl. mit jelent az, hogy a 'read nem olvas a stdin-ról'? Hát akkor honnan olvas, mondjuk lemegy az újságoshoz napilapért, és abból olvas?
- A hozzászóláshoz be kell jelentkezni