Minden shell kompatibilis, de némely shell még kompatibilisebb

Egy egyszerű kérdést vizsgálunk, mit csinál ez a programka:

echo 101 202 lonc | read A B C
printf 'A="%s" B="%s" C="%s"\n' "$A" "$B" "$C"

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=""

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" ; }

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

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 "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...

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.

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; } |...

 

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

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.)

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 "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)

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?

> 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.

Blog | @hron84

valahol egy üzemeltetőmaci most mérgesen toppant a lábával 

via @snq-

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?

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!

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.

Blog | @hron84

valahol egy üzemeltetőmaci most mérgesen toppant a lábával 

via @snq-

É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.

 

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.

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

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!

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 :)). 

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

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

Ú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!

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)

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!

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?

Várom Zahy mester szakértését is :-)

Szerkesztve: 2024. 08. 07., sze – 12:49

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 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...

Blog | @hron84

valahol egy üzemeltetőmaci most mérgesen toppant a lábával 

via @snq-

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")

Í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?