Menu

Deváté cvičení (19. 4.)

Materiály

Příklady

Zobrazit řešení
  1. Napište program, který vypíše jméno signálu, který mu byl zaslán (kromě SIGKILL) a běží dál bez ukončení. Stačí umět zpracovat standardní POSIXové signály. Posílejte tomuto programu různé signály pomocí kill, abyste vyzkoušeli jeho chování.
  2. #!/bin/sh
    trap 'signal INT' INT
    trap 'signal TERM' TERM
    trap 'signal QUIT' QUIT
    
    echo $$
    
    signal() {
        echo "Zachycen signál $1"
    }
    
    while true; do sleep 1; done
    
  3. Napište program, který bude číst řádky ze standardního vstupu a hledat na nich řetězce uzavřené mezi @. Tyto řetězce bude vypisovat (jeden na řádek) do dočasného souboru (vytvořte jej pomocí mktemp). Ve chvíli, kdy zachytí signál SIGINT nebo SIGTERM, vypíše obsah dočasného souboru, smaže jej a ukončí se.
  4. #!/bin/sh
    trap 'terminate' INT TERM
    file=$(mktemp)
    
    terminate() {
      cat $file
      rm $file
      exit
    }
    
    while read line; do
      echo "$line" | sed -r 's#(@[^@]+@)#\n\1\n#g' | grep "^@.*@$" | tr -d @ >>$file
    done
    
    Samotné zapisování do souboru musíme uzavřít do while smyčky a plnit jej po řádcích, jinak by se soubor při vypsání jevil prázdný.
  5. Napište program, který vypíše řetězec hello, world, každé písmeno jedním procesem, přičemž každý proces nejdříve vyčká na ukončení procesu pro předchozí písmeno, pak vyčká náhodně zvolenou dobu mezi 0 a 3 sekundami, vypíše svoje písmeno a skončí.
  6. #!/bin/sh
    write_delay() {
        if [ $2 -eq 0 ]; then
            printf "$1"
        else
            RAND=0
            while [ $(ps $2 | wc -l) -gt 1 ]; do
                RAND=$(((RAND+1) % 3))
            done
            sleep $RAND
            printf "$1"
        fi
    }
    
    write_delay h 0 &
    
    for i in e l l o , " " w o r l d; do
        write_delay "$i" $! &
    done
    
    wait
    echo
    
    Protože čekáme na proces, který není naším potomkem, musíme si pomoci vyčkávací smyčkou. V tomto případě jsme ji zároveň šikovně využili jako generátor náhodných čísel. Každému procesu vypisujícímu písmenko (kromě prvního) předáváme PID předchozího spuštěného procesu, aby měl na co čekat.
  7. Vytvořte webového pavouka, který bude sbírat e-mailové adresy. Začne u stránky zadané jako parametr na příkazové řádce (zkuste třeba úvodní stránku cvičení), najde všechny odkazy na další webové stránky (pozor, odkazy můžou být absolutní i relativní) a prohledá rekurzivně i je. Nalezené e-mailové adresy bude vypisovat na standardní výstup. Program poběží, dokud nebude zastaven signálem SIGTERM či SIGINT (nebo do doby, než prohledá celý internet). Ve chvíli, kdy bude zastaven počká na ukončení všech svých podprocesů a uklidí po sobě dočasné soubory. Zkuste si pamatovat již projité stránky a neprohledávejte je znovu.

    Pozor! Abychom se vyhnuli stížnostem ze strany různých organizací, že jim někdo ze sítě MFF útočí na web (což už se stalo, neberte to prosím na lehkou váhu), proveďte prosím dvě opatření:

    • Stahujte pouze stránky z domény kam.mff.cuni.cz. Jakékoliv odkazy vedoucí mimo tuto doménu ignorujte.
    • Po každém stažení obsahu webové stránky (např. příkazem curl) skript na 1 sekundu uspěte.
  8. #!/bin/sh
    
    trap 'cleanup' INT TERM
    
    # webpages = url ke zpracování
    # mails = seznam nalezených e-mailů
    # webpages_done = již zpracovaná url
    webpages=$(mktemp)
    mails=$(mktemp)
    webpages_done=$(mktemp)
    
    cleanup() {
        echo "Terminating ..."
        wait
        rm $webpages $mails $webpages_done
        exit
    }
    
    process_page() {
        file=$(mktemp)
        adresy=$(mktemp)
        wp="$2"
        ms="$3"
        wpd="$4"
    
        # base = část adresy až po poslední lomítko
        # site = adresa domény
        base=$(echo "$1" | sed -r 's#^(https?://[^/]*([^/]*/)*)[^/]*$#\1#;s#/+$##')
        site=$(echo "$1" | sed -r 's#^(https?://[^/]*).*$#\1#')
    
        # Stáhneme stránku a vytáhneme z ní řetězce vypadající jako e-mailové adresy
        curl "$1" > $file 2>/dev/null
        for mail in $(< $file egrep -Io '[a-zA-Z0-9+_\.]+@[a-zA-Z0-9+_\.]+'); do
            if ! grep -q -IxF "$mail" $ms; then
                echo $mail
                echo $mail >> $ms
            fi
        done
    
        # Vytáhneme ze stránky všechny odkazy
        strings $file | sed -rn 's#.*<a href="([^"]*)".*#\1#;Te;p;:e' > $adresy
    
        # Rovnou vyřadíme pdf a zip soubory, protože jsou velké a žádné e-maily z nich 
        # tak jako tak nezískáme.
        # Zároveň odstraníme vícenásobná lomítka z adres, abychom zabránili duplicitám.
        sed -ri '/\.(zip|pdf)/d;s#//+#/#g;s#^(https?:/)#\1/#' $adresy
        
        # Zpracování absolutních odkazů
        # - v souladu se zadáním se omezujeme pouze na doménu kam.mff.cuni.cz
        for adresa in $( egrep -I '^https?://' $adresy | grep -I "kam.mff.cuni.cz" ); do
            if ! grep -q -IxF "$adresa" $wpd; then
                echo "$adresa" >> $wp
            fi
        done
    
        # Zpracování absolutních odkazů uvnitř domény
        for adresa in $( egrep -I '^/' $adresy | sed -r 's#^#'"$site"'#' ); do
            if ! grep -q -IxF "$adresa" $wpd; then
                echo "$adresa" >> $wp
            fi
        done
        
        # Zpracování relativních odkazů
        for adresa in $( egrep -I -v -e '^/' -e '^https?://' -e '^#' $adresy \
    	| sed -r 's#^#'"$base"'/#' ); do
            if ! grep -q -IxF "$adresa" $wpd; then
                echo "$adresa" >> $wp
            fi
        done
    
        rm $file $adresy
    }
    
    # Začneme na stránce, kterou jsme předali skriptu jako argument
    echo "$1" > $webpages
    
    # Dokud je soubor webpages neprázdný, vezmeme první řádek, smažeme jej a
    # zpracujeme stránku. Abychom se nezasekli v nějaké beznadějné větvi,
    # promícháme náhodně pořadí url v souboru webpages. V souladu se zadáním po
    # každé zpracované stránce 1 sekundu vyčkáme.
    while [ $(wc -l < $webpages) -ge 1 ]; do
        page=$(head -n1 $webpages)
        sed -i '1d' $webpages
        
        #echo "Processing page '$page'" 1>&2
        echo "$page" >> $webpages_done
        process_page "$page" $webpages $mails $webpages_done
        
        rand=$(mktemp)
        sort -uR $webpages > $rand
        mv $rand $webpages
        
        sleep 1
    done
    
  9. Webového pavouka z předchozího příkladu zparalelizujte tak, aby najednou zpracovával $2 stránek.