Lehetséges megoldás lenne:
# példa függvény
fuggveny()
{
echo "nincsspace"
echo "van space"
}
# feltöltjük t-t
fuggveny | { while read i; do
t=("${t[@]}" "$i")
done };
# kiolvassuk t-t
for i in "${t[@]}"; do
echo "elem: $i"
done
# elvárt kimenet:
#
# elem: nincsspace
# elem: van space
Ahogy a fenti linken is olvasható, van bash-only workaround, kénytelen leszek azt használni, de ez így undorító (mivel a shebang-ot is át kell írni #!/bin/bash-ra, #!/bin/sh-val nem megy). Ha teheted inkább ne használj basht.
- BaT blogja
- A hozzászóláshoz be kell jelentkezni
- 2118 megtekintés
Hozzászólások
ilyenre személy szerint nem használnék bash-t. lassú és kényelmetlen. akkor már ruby, perl vagy python.
- A hozzászóláshoz be kell jelentkezni
Megvan az oka, hogy miért shell script, és nem más. Bash-t a környezet adja, elvileg használhatnék más shellt de az további problémákat vetne fel.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
A #!/foo/bar/([ackz]|([bd]a)|(pdk))sh -on kívül milyen problémákat vetne fel?
- A hozzászóláshoz be kell jelentkezni
Initrd-be megy, elvileg persze tetszőleges shellt rakhatok bele, de gyakorlatilag más scriptekkel is együtt kell működnöm, ezért bash már adott. Mivel figyelnem kell a tárhelyre is (gyakorlatilag minden kilobyte számít), ezért más shellt nem akarok belerakni.
Meg egyébként is, utálom azokat a workaroundokat amik csak azért kellenek mert amivel dolgozok az szar.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
Mit keres initrd-ben a bash...?
- A hozzászóláshoz be kell jelentkezni
Úgy van, hogy a futó rendszerre bash van telepítve (függőségek miatt bash mindenképpen települ), és helyspórolás miatt nincs más shell telepítve. Az initrd-et egy script rakja össze, ami a futó rendszer programjait használja. Így csak bash maradt. Nem, nem fogom megcsinálni, hogy az initrd összerakó scriptbe belegányoljam, hogy más shellt rakjon bele.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
A bash maga 800k, az initrd-m /bin könyvtára meg 592, amiből a busybox 224... Tényleg helytakarékos megoldás :-P
Ja, a bash-hoz szükséges plusz libek mérete 230k, azaz bash (800-224)+230=806k-val több helyet zabál az initrd-ben...
- A hozzászóláshoz be kell jelentkezni
Viszont lzma-val tömörítve a teljes initrd-em (benne: bash, mount, sleep, és egy statikus bináris + a libek és a /init) kicsivel kevesebb, mint 2MB. Busybox telepítése minimum 200kb. Ha kiszórom bash-t, mount-ot és sleep-et, azzal nyerek nyersen 1MB-ot a libjeikkel együtt, ez betömörítve nem tudom mennyire jön ki, de szerintem ez úgy pont 200kb környékén van. Szóval gyakorlatilag ugyanott volnék, csak akkor a /init helyett az initrd összerakó scriptembe kellene belegányolni, azt viszont nem szeretném mert abban jelenleg nincs csúnya hack, initrd-ben meg már gyakorlatilag mindegy.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
A bash+mount+sleep trió nálam pont egy darab busybox meg két link :) A bash belerámolása az initrd-be már egy randa hack egyébként :-P
- A hozzászóláshoz be kell jelentkezni
Tudom, pont azért mondtam h. azt a 3-at ki lehet vágni. A bash belerámolása meg egy darab cp, az nem hack. :)
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
A bash belerámolása három cp minimum, merthogy kettővel több libet igényel, az meg, hogy egy binárisra van n+1 link, az kit érdekel...?
- A hozzászóláshoz be kell jelentkezni
Tökmindegy, a lényeg hogy inkább gányolok a /init-ben mint az initrd összelapátoló scriptben, mivel a /init sokkal kevésbé kell, hogy általános legyen, mint az initrd összelapátoló script. Pontoabban gyakorlatilag egyiknek sem kell általánosnak lennie, de én magam felé fel szoktam állítani olyan követelményeket amikre a gyakorlatban nincs szükség, az ilyenek miatt. Arról már nem is beszélve, hogy a fenti bash bugot egyszer talán még javítják, és akkor mennyivel jobb lesz kiszedni azt az egy kommentet és kikommentezni egy másik kódrészletet, mint egy egész scriptet visszaírni. :)
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
- A hozzászóláshoz be kell jelentkezni
"Meg egyébként is, utálom azokat a workaroundokat amik csak azért kellenek mert amivel dolgozok az szar."
Eppen ezert nem kene bash-t hasznalni :)
- A hozzászóláshoz be kell jelentkezni
"
t=("${t[@]}" "$i")
"
Jujj.
- A hozzászóláshoz be kell jelentkezni
Why?
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
Gondold végig, hogy mit csinál: minden egyes alkalommal átmásolja a tömb teljes tartalmát, majd a végére odabiggyeszt még egy elemet.
szerk.: Illusztrálom:
$ time for i in $(seq 1 1000) ;do t=("${t[@]}" "$i") ;done
real 0m1.262s
user 0m1.250s
sys 0m0.000s
$ unset t
$ time for i in $(seq 1 1000) ;do t[${#t[@]}]="$i" ;done
real 0m0.010s
user 0m0.010s
sys 0m0.000s
- A hozzászóláshoz be kell jelentkezni
Az az érdekes, hogy ezt nem optimalizálja a shell. Egyébként jogos.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
Azért a shell nem egy optimalizáló fordító, hogy akár arra is odafigyeljen, hogy az értékadás bal és jobb oldala ugyanazon változókra történő hivatkozást tartalmaz-e, vagy sem. Ráadásul a példában szereplő esetben kéne még egy kis intelligencia is ahhoz, hogy felfogja, hogy itt neki volna mit gyorsítania. Persze amekkora a bash, akár ez is belférhetne. Mellesleg sz megoldása nemcsak hogy gyorsabb, de legalább a POSIX-szintaxissal is egybevág (ellentétben az eredeti "t=( a b c )" formával, ami helyett pl ksh-ban "set -A t a b c" formában kéne írni).
- A hozzászóláshoz be kell jelentkezni
"Mellesleg sz megoldása nemcsak hogy gyorsabb, de legalább a POSIX-szintaxissal is egybevág"
Ezt nem egészen értem. AFAIK POSIX shellben nincs is array...
- A hozzászóláshoz be kell jelentkezni
OT: Úgy tűnik gyors doksinézés után, hogy igazad van. Totál megzavart, hogy a különböző kereskedelmi Jujnikszok POSIX-kompatibilisnek kikiáltott shell-verziói kb mind a Korn-shellen alapszanak, és általában nem dobták ki a Korn-shell-féle tömbkezelési szintaxist. (Azt amúgy nem látom, hogy ha tényleg nincs tömbkezelés, akkor hogyan akarhat bárki is tömbváltozók segítségével hordozható kódot írni. Az viszont gyanúsan fennáll, hogy az "a[N]=M" forma *több* shellben működik, mint a legelőször emlegetett a=(x y z). Ugyanis megy bash-ban is, *ksh*-ban is (ami minimum ksh88, ksh93, és pdksh). A zsh-t nem tudom, melyiket szereti.)
- A hozzászóláshoz be kell jelentkezni
+1 ez a bugfeature mar nehanyszor az en eletemet is megkeseritette.
---
Internet Memetikai Tanszék
- A hozzászóláshoz be kell jelentkezni
subscribe
színes aláírás
- A hozzászóláshoz be kell jelentkezni
Most komolyan nem tudod beallitani az IFS-t hogy a szokoz ne legyen benne ?
- A hozzászóláshoz be kell jelentkezni
Gányolás az is.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
A problémára természetesen létezik tökéletesen hordozható megoldás. Például egy összegzés:
gen_ints()
{
seq 1 100
}
use()
{
printf '%d\n' "$1"
}
gen_ints \
| (
SUM=0
while read CUR; do
SUM=$((SUM + CUR))
done
use "$SUM"
)
Gyerekből a szülő környezetét nem lehet megváltoztatni. Nem is érdemes megpróbálni; számolja ki a gyerek, amit kell, aztán használja is. Semmi értelme visszatérni a parent shell-be. Ha nem szeretjük a mély indentációt, csináljunk függvényeket legkívül, azok kötelezően másolódnak a subshell env-be is.
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.htm…
A bash nagyszerűen megfelel interaktív munkára, illetve ha olyan szkriptet írunk, amelynek nem kell hordozhatónak lennie. Ha hordozható szkriptet írunk, akkor a POSIX vonatkozó fejezetét böngészgetve dolgozunk, és dash-sal, vagy valamely más, kifejezetten POSIX-kompatibilitásra kihegyezett shell-lel teszteljük, és a felhasználótól is megköveteljük a dokumentációban, hogy olyannal futtassa.
- A hozzászóláshoz be kell jelentkezni
+1
- A hozzászóláshoz be kell jelentkezni
Azt mutasd meg, hogy a fenti use-os megoldással hogyan tudsz szóközhelyesen(!) tömböt kipasszolni. Ugyanis sajnos amint stringgé konvertálódik a változó, a separator whitespace és az elemen belüli whitespace között megszűnik a különbség.
Egyébként arra én is gondoltam, hogy a subshellen belül folytatom a végrehajtást, csak az a baj, hogy eleve egy ciklusban futna a fenti kód (egymásba ágyazás), azaz a végén n subshellem lenne, ami szerintem nem túl szerencsés.
A dasht hoztad alternatívának, ahogy a bejegyzésben írtam, a bashon kívül a dashban sem megy a "shellfüggetlen" workaround. (Ash port netbsd-ből, azóta debian fejleszti, nem is vártam mást.)
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
"Azt mutasd meg, hogy a fenti use-os megoldással hogyan tudsz szóközhelyesen(!) tömböt kipasszolni. Ugyanis sajnos amint stringgé konvertálódik a változó, a separator whitespace és az elemen belüli whitespace között megszűnik a különbség."
Idézőjeleket mindenhova.
use ()
{
for i in "$@"; do
echo "elem: $i"
done
}
t=("nincsspace" "van space")
use "${t[@]}"
- A hozzászóláshoz be kell jelentkezni
Úgy értem, oké hogy kiprinteled, de építs belőle tömböt.
Egyébként pont ugyanígy működik a saját fv-em is, van egy tömb bemenete és kiprinteli bizonyos szabályok szerint a tömb bizonyos elemeit, egy sorba 1 elemet. Tehát ha én a tömbépítő whileból csinálok egy ilyen use-t akkor pont ott vagyok ahol voltam.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
A tömbépítésre már ott vannak fentebb a megoldások/workaroundok.
- A hozzászóláshoz be kell jelentkezni
Mégegyszer: van egy függvény, kiköp n sort stdoutra. Ebből kell tömböt építeni, egy sor -> egy elem. Kigondolt, de nem jó megoldás:
fuggveny|while read i; do
t[${#t[@]}="$i"
done
Erre írta lacos, hogy a whileból hívjak meg egy fv-t ami megint csak printel. Akkor most mennyivel is vagyok előrébb?
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
Nézd meg jobban, hogy lacos mit írt, mert egyáltalán nem ezt írta.
- A hozzászóláshoz be kell jelentkezni
Valóban, utána rájöttem.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
hogyan tudsz szóközhelyesen(!) tömböt kipasszolni
Nem passzolom ki. A függvényt legelőször kint definiáljuk, de az bemásolódik a subshell exec env-be is, futni pedig ott fut.
use_1()
{
local K
K=${#MYARR[@]}
while test $K -gt 0; do
K=$((K-1))
printf '%u: \"%s\"\n' $K "${MYARR[K]}"
done
}
use_2()
{
local K
K=0
while test 0 -lt $#; do
printf '%u: \"%s\"\n' $((K++)) "$1"
shift
done
}
for ((K=0; K<10; K++)); do
printf '%d %d\n' $((2*K)) $((2*K+1))
done \
| (
declare -a MYARR
while read X Y; do
MYARR[ ${#MYARR[@]} ]="$X $Y"
done
use_1
use_2 "${MYARR[@]}"
)
- A hozzászóláshoz be kell jelentkezni
Tehát az a megoldás, hogy ha n* kell lefutnia a függvényemnek, akkor n egymásba ágyazott subshellem lesz.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
The unix way! =)
- A hozzászóláshoz be kell jelentkezni
escape()
{
local TMP
while test 0 -lt $#; do
TMP=\'${1//\'/\'\\\'\'}\'
printf '%s ' "$TMP"
shift
done
}
f1()
{
escape "a'b&"
escape "c d e" 'f"g\h'
escape i$'\n'j
}
assign_to()
{
local ARRAY="$1"
local ESC_LIST="$2"
eval "$ARRAY=($ESC_LIST)"
}
assign_to Z "$(f1)"
for ((K=0; K < ${#Z[@]}; K++)); do
printf '%u: >>%s<<\n' $K "${Z[K]}"
done
- A hozzászóláshoz be kell jelentkezni
Ez olyan ronda, hogy elhányom tőle magam, de legalább használod a totálisan nem standard ${1//...} formát, és már megint a bash-specifikus a=( ... ) formát. (Ja meg a C-szerű for-ciklust.) De respect amúgy.
- A hozzászóláshoz be kell jelentkezni
Ahogy más is rámutatott már: onnantól, hogy tömböket használunk, nincs értelme hordozhatóságról beszélni. A jelenleg érvényes POSIX szabvány Shell Command Language fejezete nem tartalmazza azt a szót, hogy "array". A POSIX nélkül számomra meg érdektelen (általánosabban: hivatalos szabvány nélkül valamelyest értelmezhetetlen) a hordozhatóság kérdése. (Igen, ebből az is következik, hogy a GNU autotools-nak nem vagyok nagy barátja.) A kérdésben bash szerepelt, a script bash script.
eval-lal természetesen megoldhatók array támogatás nélkül is az egészekkel indexelt tömbök: eval "MYARR_${IDX}=$ESC_VAL" :) A cserélős behelyettesítést kiválthatjuk sed-del, az "array initializer list"-et meg azzal, hogy az escape függvény közvetlenül a fenti értékadás-sztringeket generálja (pontosvesszővel lezárva), amelyeket összefűzünk, majd egyetlen script-ként futtatunk végül az eval-ban. Átírjam? :)
De köszönöm amúgy :)
- A hozzászóláshoz be kell jelentkezni
Én úgy értelmeztem, hogy fusson minimum ksh-ban is.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
Na végül sikerült megoldani a problémát subshellek és rekurzív függvényhívás nélkül, az által, hogy a függvényt inlineoltam a kódba. A tömböket nem úsztam meg (pedig az lett volna az igazán elegáns megoldás) és a futásidő se túl kedvező (O(k*n^2) ahol k egy file sorainak száma, n pedig egy könyvtárban levő fileok száma), de legalább működik, és a tömböket leszámítva shellfüggetlen. :)
Persze ha a bash nem viselkedik máshogy mint kellene, akkor már 2 napja kész lennék.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
Engem azért még érdekelne, hogy mi volt az eredeti probléma, ami ezt az egészet triggerelte. Hátha el lehetne kerülni a tömböt illetve meg lehetne oldani csak POSIX feature-ök használatával.
- A hozzászóláshoz be kell jelentkezni
Előfeltétel: csak shell builtinek használata, azaz nem hívhatunk meg semmilyen külső programot, coreutilsból se.
Van egy könyvtár, benne egy nagy rakás csomag. A csomagkezelő paraméterein át kell adni ezeket a csomagokat, pl. install a.pkg b.pkg c.pkg d.pkg e.pkg f.pkg. Erre ugye az a triviális megoldás, hogy install *.pkg. Csakhogy nem mindegy, milyen sorrendben adjuk át ezeket a csomagokat az installnak, mert van pár csomag, amiknek előbb kell teleülnie mint a többinek. Azaz pl. install e.pkg b.pkg a.pkg c.pkg d.pkg f.pkg (e.pkg és b.pkg került előre).
Azt találtam ki, hogy létrehozok egy filet, amiben felsorolom az először telepíteni kívánt csomagokat. Azaz a konkrét példában e.pkg és d.pkg. File olvasás megy pl. read-del, ahhoz nem kell külső command. Könyvtár listázásához se kell külső command, wildcarddal és for ciklussal megoldható a dolog. Most már csak az a probléma, hogy azokat a fileokat, amiket már hozzáadtunk az install command line-jához, azokat a könyvtár listázás során ne adjuk át még egyszer (ezt ugyanis a csomagkezelő nem díjazza). Ezért kellett tömb, hogy végig tudjunk iterálni a már hozzáadott csomagokon, így ellenőrizve, hogy a soron következő csomagot hozzáadhatjuk-e a command line-hoz.
A pontos megvalósításban persze egy picit máshogy oldottam meg, mert eleve csak a tömböt építem fel, és csak a legvégén adom át install "${t[@]}" formában.
Persze ha hozzávesszük, hogy elvileg a csomagnevekben nincs space, akkor valamivel nagyobb a mozgásterünk, de úgy voltam vele, hogy ha szép megoldás kell akkor erre is fel kell készülni.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
Ezért kellett tömb, hogy végig tudjunk iterálni a már hozzáadott csomagokon, így ellenőrizve, hogy a soron következő csomagot hozzáadhatjuk-e a command line-hoz.
Ezert nem kell tomb, erre vannak a listak. A tombok akkor jok, ha kulon akarsz hivatkozni egy elemre cikluson kivul, ha mar ugyis mindig vegig kell raja menni teljesen felesleges.
IFS=:
keep="e.pkg:ur csomag.pkg:b.pkg"
for pkg in *.pkg; do
for i in $keep; do
if [ "$pkg" = "$i" ]; then
echo "skip: $pkg"
continue 2
fi
done
echo $pkg
done
- A hozzászóláshoz be kell jelentkezni
Jo de akkor : nem lehet a filenevben. :D (Persze, az én megoldásomban meg \n nem lehet.)
A legvégén hogy adod oda a listát az install-nak?
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
Mar is eleg erdekes, ha space van a csomagnevben nemhogy ":" es tarsai. :D A lenyeg, hogy egy valoszinutlen karaktert kell valasztanod ami lehet %-tol a \n-ig akarmi.
A legvégén hogy adod oda a listát az install-nak?
Ha ragaszkodsz a legvegehez, akkor epithetsz belole egy uj listat, de szerintem ez felesleges. Mert nem jo, ha echo helyett egybol installt tolsz?
- A hozzászóláshoz be kell jelentkezni
Egyszerre kell odaadni az összes csomagot neki a dependency check miatt, nem lehet egyesével.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
Na igen, igy mar kicsit trukkosebb.
for pkg in *.pkg; do
...
pkglist="$pkglist \"$pkg\""
done
eval install $pkglist
- A hozzászóláshoz be kell jelentkezni
Na így valóban nincs tömb, már csak egy eval. De mivel eval is evil, szerintem a tömbös megoldás se rosszabb. :)
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
'"; rm -rf / #.pkg'
? :D
Az hogy az eval mennyire evil mindig a korulmenyektol fugg. Ha mar a csomag neveben is turpissagok vannak, felvetodik a kerdes, hogy vajon mi lehet benne? :)
Tehat ez esetben szerintem nem kell felned attol az evaltol.
- A hozzászóláshoz be kell jelentkezni
Nem először, de a busybox-ot nézted már...? busybox ls *.pkg | busybox grep -Ev "$(busybox cat nemkellmar)"
A nemkellmar fájlban felsorolod a csomagokat "|"-pal elválasztva, és kész a lista, hogy mit kell még.
Nagyjából:
install $(busybox tr "|" " " < nemkellmar) $(busybox ls *.pkg | busybox grep -Ev "$(busybox cat nemkellmar)")
igaz, itt a szóközöket tartalmazó csomagnevek gondot okozhatnak, de azt már rád bízom :-P
- A hozzászóláshoz be kell jelentkezni
Bár a téma végérvényesen elsüllyedt, belefutottam újra a problémába, ezúttal talán jobban érzékelhető lesz, mennyire súlyos is valójában.
Adott egy textfile, amiben tetszőleges tartalmú soraink lehetnek (tehát nem kimondottan filenevek), és megkülönböztetünk speciális sorokat, amik a többi sor tartalmának feldolgozását befolyásolják. Például:
#title
Cím ide
#author
Szerző is van
#lehet komment is
#summary
Egy rövid összefoglaló
Lehet több sor is
Ez egy klasszikus állapotgépes feladat, átültetve shellre valahogy így oldanám meg:
#!/bin/sh
. /usr/lib/dsw_fuggvenyek #do something width függvények
filename="$1" #az nem opció, hogy beleirányítsuk a file tartalmát a scriptbe
state="none"
cat "$filename"|while read line; do
if [ "$(echo $line|cut -c 1)" = "#" ]; then
case "$line" in
"#title")
state="title"
;;
"#author")
state="author"
;;
"#summary")
state="summary"
;;
esac
else
case "$state" in
"title")
dsw_title "$line"
state="none" #title csak egysoros lehet
;;
"author")
dsw_author "$line"
state="none" #author is csak egysoros lehet
;;
"summary")
dsw_summary "$line"
;;
esac
fi
done
Persze ez így nem fog működni, mert a state változót hiába módosítjuk a pipeon "belül", az "kívül" változatlan marad. Megoldás? Nekem nincs rá ötletem.
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni
Mire is kell a megoldás? A state változót sehol sem használod a while cikluson kívül. Egyébként a fenti script a fenti inputtal működik dash-sel és bash-sel is; legalábbis ha a dsw_* függvények simán echo "* $@"-t csinálnak, akkor azt írja ki, amit szerintem ki kellene írnia.
És hogy a mai optimalizálós tipp is meglegyen:
if [ "$(echo $line|cut -c 1)" = "#" ]; then
helyett
if [ "${line#"#"}" != "$line" ]; then
- A hozzászóláshoz be kell jelentkezni
Hát ha probléma a pipe-on "belüliség/kívüliség", akkor felejtsd el a pipe-ot:
cat "$1" | while read line ; do
...
done
helyett, ahogy kitaláltad, használd ezt:
while read line ; do
...
done < "$1"
formát, és készen is vagy. Bár lehet, hogy a "beleirányítani nem lehet" kitételedet valahogyan nem értem (mivel nem mondhatnám, hgy túlaludtam magam, lehet hogy nem értem a problémát, akkor bocs).
- A hozzászóláshoz be kell jelentkezni
Oké igazatok van, ezt most benéztem. :)
--
Don't be an Ubuntard!
- A hozzászóláshoz be kell jelentkezni