Bash parallel scripting

Fórumok

Hello!

Van egy ilyenem:


#!/bin/bash

maxjobs=10
running=0

while read host source target ; do
        (ssh root@$host "./valamit-csinalunk $source $target") &
        #(dd if=/dev/zero of=/dev/null bs=1000 count=1000000) &
        #(echo "lofasz"; sleep 10 ) &

        running=$(($running+1))

        if [ "$running" -ge $maxjobs ]; then
                wait
                running=0
        fi
done < replication.cfg

wait

Ez egy nagyon egyszeru dolog lenne arra, hogy bizonyos dolgokat parhuzamosan vegezzek el. Azt tapasztalom, hogy ha ott van az ssh barmilyen formaban a ciklusban, akkor a while ciklus $maxjobs utan kiszall. Ha barmi egyeb van ott (lasd kikommentezett utasitasok) akkor a program az elvarasoknak megfeleloen mukodik. Biztosan elsiklok valami trivialis dolog folott, de mi az?
Koszi!

Hozzászólások

Biztos vagy benne, hogy az ssh parancsot al-shell-ben akarod a háttérben futtatni? szerintem első körben vedd ki körüle a kerek zárójeleket. (Értem, hogy a többit is úgy próbáltad, de azért .. )

Na. Így működik:


#!/bin/bash

maxjobs=10
running=0

doit() {
while read host source target ; do
        ssh root@$host "./valamit-csinalunk $source $target" &

        running=$(($running+1))

        if [ "$running" -ge $maxjobs ]; then
                wait
                running=0
        fi
done
}

cat replication.cfg | doit
wait

(ha esetleg hiba van, az azért, mert csak átírtam itt a hupon a lényeget, az eredeti script jóval bonyibb)

Vajon mi erre a magyarázat?

Az ssh nem ad visszatérési értékat, amíg a kapcsolat be nem fejeződik, vagy le nem választja magát a shell -ről, így a nohup sem hajtódik végre.

Azt próbáld, hogy az ssh -t minusz-kis-ef kapcsolóval indítod, tehát így:

ssh -f user@host

U.I.: Különben mi lesz ez?

----
올드보이
http://molnaristvan.eu/

Ezt írtad: "a & nohup -ot küld azzal indítja a subshell". Én meg ezt: "a *megfogalmazás* erősen messze van a valóságtól". Szóval, most történelemóra következik:

régen, még az eredeti Bourne-shell idejében, mikor egy parancsot háttérben indítottunk és nem volt átirányítás a parancssorban, akkor az (a shell) még gyönyörűen kommunikált az eredeti stdin/stdout-tal, azaz a terminállal. (Az volt a szép idő, amikor egy háttérprocessz elkapkodta az előtérben futó shell elől az inputot.) Ezért is volt nagyon fontos az átirányítás. Ha pedig az ember Ctrl-D-vel/exittel kilépett a (fő)shellből, akkor a shell *küldött* egy HUP-szignált a háttérprocessz(ek)nek. Ettől az általában megdeglett. Ha ezt az ember nem akarta, akkor kellett "nohup parancs & " formában indítani. Mőködése: a nohup parancs elindult háttérben, beállította a *saját* környezetét úgy, hogy ignorálta a HUP-szignált, majd exec-cel elindította a paraméterként megadott parancsot maga helyett. Eredménye, mivel a SIGIGN öröklődik exec-nél, a valójában futó parancs is ignorálta a HUP-ot, így képes volt tovább futni, amikor az eredeti (szülő) shell kilépett (az ugyanis hiába küldött neki megszakítást). Valamikor (én egy korai Korn-shell-re tippelek, de nem tudom) aztán lett annyi előrelépés, hogy ha "parancs &" formájú futtatás történik (nohup nélkül akár), akkor a shell maga a gyerekprocesszre beállítja ezt a SIGIGNORE-t a hup-megszakításra. Tehát nem *küldi* a shell a nohup-ot, hanem beállítja a hup-megszakításra érzéketlenséget. (Az csak hab a tortán, hogy úgy tudom, ettől függetlenül küldi azt a szerencsétlen HUP-ot exit-nél.)

Bash-ben shopt (egyesszám) a parancs neve, a ksh-val meg az a baj, hogy a gépemen se a ksh93-nak, sem a pdksh-nak nem létezik ilyen opciója. Milyen ksh-verzióban találtad ezt? Eseteg az se ksh, hanem mondjuk zsh? (Bár ez utóbbira meggyőző érveket nem találtam egy gyors guglizással.)

Debian ill ubuntu alatt: `apt-get install pexec`, egyebkent forrasbol is. Ha van publikus kulcsos ssh-d, akkor azt is, maga'tol. (Igen, kis o"nreklam, node pont eme dolgokat potolta a maga idejeben...).

Hi!

Köszönöm a válaszokat, de félreértettétek a dolgot. Természetesen az említett tool-okat, módszereket ismerem, a feladatot bárhogy meg lehet oldani, de itt ráfutottam egy -számomra- érdekességnek tűnő dologra, amit szeretnék megérteni. Egyébként az egyik link: http://pebblesinthesand.wordpress.com/2008/05/22/a-srcipt-for-running-p… pontosan ugyanaz, mint az enyém és hasonlóképp viselkedik.)

Kifejezetten az érdekelne, hogy a lenti 3 dolog közül,

1. http://pastebin.com/wLJTusu6
2. http://pastebin.com/bFtnAJ4v
3. http://pastebin.com/1FzbE660

egy több ezer soros replication.txt inputtal az 1. miért fejeződik be $maxjobs után, ha a 2. változat nem és a 3. sem.

Igen. az `ssh` kliens valamit csinal a terminallal, amit a szu"lo" (bash) nem vesz jo'ne'ven. Nem tudom pontosan mie'rt, de a

< /dev/null

atiranyitas megszu"nteti a proble'ma't, ugy szepen szokatak menni ezek a dolgok, meg most az altald irt (nyito) peldadat is megoldja. A fentebb linkelt 3-as peldaban is azert oldja meg a kozvetett fuggvenyezes, mert ugy az ssh stdin-je nem egy tty lesz, hanem a cat pipe-ja.

Amit viszont nem ertek, hogy a ptty allokaciot (buzeralast) ki lehet kapcsolni az ssh -T kapcsoloja'val; viszont onmagaban az sem oldja meg az eredeti problemat.

Talan egy strace/ltrace kozelebb vinne a megoldashoz, hha me'g -T-vel is raenged valami olyan termios-os vagy egyeb ioctl()-es dolgot az fd0-ra, amiert a bash azt mondja hogy bocs.

A.

Közben debuggoltam, a bash nem azt mondja hogy bocs, hanem simán egyszerűen az ssh flusholja az stdin-t a session _végén_. Így logikus, hogy miért tud ez megtörténni a while < file esetben és nem a másikban. És nem $maxjobs -szor megy le a ciklus, az csak egy sajátosság volt a futások hossza miatt. :)
Bevallom csak akkor kezdtem ebben az irányban nézelődni, amikor kicseréltem a bash-t default solaris sh-ra (bash gyakrabban szokott meglepni), az ssh-t meg rsh-ra.. ja, az rsh is flusholja a stdint :)

Hát én ezt futtattam, de nem sikerült reprodukálnom az említett viselkedést: http://pastebin.com/JW8qTh13
A yes.txt-ben 'y'-ok voltak soronként.
Másrészt kérlek fejtsd ki részletesebben amit írtál itt, mert nagyon érdekelne, de nem értem. (Ez valsz az én hibám, de a tényen nem változtat.)

Write only mód, itt az én megoldásom:

http://pastebin.com/bSzw63nr

Nem a processek számát figyelem, hanem hogy ráér-e a cpu, de ez tökmindegy. Egy, a scriptben definiált függvényt hívok meg, és jónapot.

Viszont szerintem ez se oldja meg a gondodat.... bosszantó.

Igen, az ssh kavarja el, eheh, man ssh:

-n Redirects stdin from /dev/null (actually, prevents reading from stdin).

Ja, most látom, hogy erre már rájöttetek, bocs...

ott a pont a -n opcióval, így én bemásolom a következő sort is a man-ból:

-n Redirects stdin from /dev/null (actually, prevents reading from
stdin). This must be used when ssh is run in the background.

problem solved, egyedül Fisher tud olvasni az itt megszólalók közül. (Engem is beleértve, mert én is csak ennek hatására néztem bele.)

Rsh-nak is van ilyenje: (okulásként)


     -n             Redirect the input of rsh to  /dev/null.  You
                    sometimes  need  this  option to avoid unfor-
                    tunate interactions between rsh and the shell
                    which  invokes  it.  For  example, if you are
                    running rsh and invoke a  rsh  in  the  back-
                    ground  without  redirecting  its  input away
                    from the terminal, it blocks even if no reads
                    are  posted  by  the  remote  command. The -n
                    option prevents this.

Ma is okosabb lettem, thx all! Igazából a |while és a while< közti különbséget nem fogtam fel.

Azt nem tudom, hogy mi az oka annak, hogy a 0-ás descriptor a pipe-os változatnál /dev/null az ssh processz esetében,
de emiatt van az, hogy az egyik működik a másik nem.
Szóval a pipe-os verziónál más lesz az stdin ezért nem "eszi meg" az input-ot.
A /proc/PID/fd -t listáztam itt.

pipe -al:
- valamelyik ssh process:
lr-x------ 1 user user 64 2011-10-21 00:58 0 -> /dev/null
lrwx------ 1 user user 64 2011-10-21 00:58 1 -> /dev/pts/0
lrwx------ 1 user user 64 2011-10-21 00:58 2 -> /dev/pts/0
lr-x------ 1 user user 64 2011-10-21 00:58 3 -> socket:[969279]
lrwx------ 1 user user 64 2011-10-21 00:58 5 -> /dev/pts/0
lrwx------ 1 user user 64 2011-10-21 00:58 6 -> /dev/pts/0

- ./test.sh -hoz tartozó bash
lrwx------ 1 user user 64 2011-10-21 00:59 0 -> /dev/pts/0
lrwx------ 1 user user 64 2011-10-21 00:59 1 -> /dev/pts/0
lrwx------ 1 user user 64 2011-10-21 00:58 2 -> /dev/pts/0
lr-x------ 1 user user 64 2011-10-21 00:59 255 -> /home/user/Desktop/test.sh

- cat valami.txt | doit által nyitott shell:
lr-x------ 1 user user 64 2011-10-21 00:59 0 -> pipe:[968361]
lrwx------ 1 user user 64 2011-10-21 00:59 1 -> /dev/pts/0
lrwx------ 1 user user 64 2011-10-21 00:58 2 -> /dev/pts/0

pipe nélkül:
- valamelyik ssh process:
lr-x------ 1 user user 64 2011-10-21 00:39 0 -> /home/user/Desktop/yes.txt
lrwx------ 1 user user 64 2011-10-21 00:39 1 -> /dev/pts/0
l-wx------ 1 user user 64 2011-10-21 00:39 2 -> /dev/pts/0
lr-x------ 1 user user 64 2011-10-21 00:39 3 -> socket:[915716]
lrwx------ 1 user user 64 2011-10-21 00:39 5 -> /dev/pts/0
l-wx------ 1 user user 64 2011-10-21 00:39 6 -> /dev/pts/0

- ./test.sh -hoz tartozó bash
r-x------ 1 user user 64 2011-10-21 00:40 0 -> /home/user/Desktop/yes.txt
lrwx------ 1 user user 64 2011-10-21 00:40 1 -> /dev/pts/0
lrwx------ 1 user user 64 2011-10-21 00:40 10 -> /dev/pts/0
l-wx------ 1 user user 64 2011-10-21 00:39 2 -> /dev/pts/0
lr-x------ 1 user user 64 2011-10-21 00:40 255 -> /home/user/Desktop/test.sh

Hát a válasz a forráskódban megtalálható (bash 4.2, execute_cmd.c 1431. és 493. sor):


  /* If this is a user subshell, set a flag if stdin was redirected.
     This is used later to decide whether to redirect fd 0 to
     /dev/null for async commands in the subshell.  This adds more
     sh compatibility, but I'm not sure it's the right thing to do. */
  if (user_subshell)
    {
      stdin_redir = stdin_redirects (command->redirects);
      restore_default_signal (0);
    }

  /* If this is an asynchronous command (command &), we want to
     redirect the standard input from /dev/null in the absence of
     any specific redirection involving stdin. */
  if (should_redir_stdin && stdin_redir == 0)
    async_redirect_stdin ();

async_redirect_stdin ()
{
  int fd;

  fd = open ("/dev/null", O_RDONLY);
  if (fd > 0) 
    {    
      dup2 (fd, 0);
      close (fd);
    }    
  else if (fd < 0) 
    internal_error (_("cannot redirect standard input from /dev/null: %s"), strerror (errno));
}

Szóval ha egy subshell-ről van szó, az indított parancs a háttérben van futtatva (&)
és át van irányítva az stdin-je a futtató shell-nek, akkor a bash még mielőtt elindítaná az ssh-t beállítja az stdin-t /dev/null-ra.
sh kompatibilitás miatt :]
Ez látszik az strace-ből is.