Loading [MathJax]/extensions/tex2jax.js
Ask Your Question
1

Úloha z 2. zápočtového testu PS1

asked Nov 12 '14

shejby gravatar image

updated Nov 12 '14

6b: V /usr najděte a vypište jméno podadresáře, (ignorujte skryté) který má nejvíce podadresářů (nerekurzivně). Při více shodných, stačí vypsat 1 kandidát. (Nevím zda jsem to přepsal správně.) Může mi někdo napsat správné, pokud možno úsporné řešení (aby se vlezlo do 4 testových řádků - čtverečky ) Nápověda výstup z příkazu ls -l /.(tuším)

EDIT: cutit ls -l je prasárna, ale jako pseudo řešení asi bude postačovat, protože problém je skoro u každého řešení, skoro všech příkladů:

1)Uzávorkování: echo "$var" Je "good practice", které řeší většinu případů, ale zdaleka ne vše. TLDP - Quoting variables

2)Jména souborů: Fakt, že název souboru může obsahovat prakticky cokoliv, dokáže nepříjemně potrápit v mnoha situacích. Viz LinuxDays14 Lukáš Bařinka jména souborů.

3) Speciální znaky např: '!' Lukáš Bařinka !

....asi se najdou další oblasti...

Nicméně, zajímá mě tedy správné řešení problému :-). EDIT2: Dal jsem upvote všem co se snažili (krom sebe), až někdo přijde s neprůstřelným řešením, co je realizovatelné v testu dám to jako správnou odpověď. (V úterý se na to zeptám na cviku)

Comments

Ve čtvrtek po testech. Od toho byly konzultace, nyní je třeba zajistit stejné podmínky pro studenty, kteří budou psát ve čtvrtek, a studenty, kteří psali včera.

Josef Kokeš (Nov 12 '14)
1

rozumím, rád si na něj počkám :-) Opravdu mě to zajímá.

shejby (Nov 12 '14)

Ad 3) Vykřičník (history expansion) funguje implicitně pouze v interaktivních shellech. Má tu někdo pocit, že test na papír je práce s interaktivním shellem!? S tím vykřičníkem bych se proto neukvapoval.

Defugio Auctoritatem (Nov 13 '14)

"!" jako history expansion, je jen jedna z mála možností jak lze v shellu použít, doporučuju tu přednášku (má i druhou část)

shejby (Nov 13 '14)
add a comment

5 Answers

Sort by » oldest newest most voted
4

answered Nov 13 '14

updated Nov 13 '14

Protože "správná" odpověď už tu několikrát zazněla a opravdu hezkou "správnou" odpověď napsal @Josef Kokeš, tak tu uvedu nějaký ten nástřel správne (== praktické) odpovědi, která rozhodně není vyžadována testem.

Problematické je především odřádkováni v názvu, které zabije jakoukoliv práci s cutem a grepem. Na výběr tedy máme dvě možnosti. Buď upravíme vstup na něco použitelnějšího, nebo to se vyhneme používání jmen.

Takže jedna z metod, jak bych to mohl v praxi řešit "aby se to nerozbilo":

ls -l --quoting-style=c /usr | awk '$1 ~ /^d/ { if($1 >= max) {max=$1; maxR=$0} } END {print substr(maxR, index(maxR, "\""))}'

Výsledkem bude jméno souboru, které bude mít všechny fujky znaky escapované po céčkovém způsobu. Pokud se mi to nelíbí, můžu to nechat interpretovat ještě nějakým echo -e nebo podobně...

EDIT: a jak tak koukám, tak @Josef Kokeš dodal krom "správné" i správnou :-)

link

Comments

Jen tak ze zvědavosti: Když to můžu pajpovat do awk, můžu to pajpovat i do pythonu?

Miro Hrončok (Nov 13 '14)

Awk máš všude (sice to není shell builtin), python ne..takže bych čekal, že ne.

shejby (Nov 14 '14)

Jak jsi přišel na to, že awk máš všude? To samé můžu říct o pythonu. Ani jedno není pravda, platí, že obojí je skoro všude.

Miro Hrončok (Nov 14 '14)

@Miro Hrončok: Nejprve prakticky, není to úplně tak, je větší šance, že na systému bude spíš awk nez python. Awk je dokonce (v nějaké okleštěnější verzi) součást busyboxu, takže ho mám v i v telefonu. Python ne. Na většině serverů bude instalována nějaká minimální sada, kde bude mimo jiné vim a (g/m)awk. Teď teoreticky: pajpovat se může klidně i do vlastního céčkového programu, pokud se předtím napíše zdroják a zkompiluje se. Radši bych to ale udělal v perlu, neboť python je hůře portabilní ;-)

Honza Baier (Nov 14 '14)
add a comment
3

answered Nov 13 '14

Josef Kokeš gravatar image

updated Nov 13 '14

stat -c "%A %h %n" /usr/* | grep ^d | sort -k2,2n | tail -1 | cut -d" " -f3-

Už zde bylo uvedeno, že můžeme vyjít z počtu hardlinků, protože hardlink na adresář nedokážeme explicitně vytvořit. Vytvoříme ho jedině implicitně tím, že ten adresář vytvoříme (dva hardlinky - jeden kvůli odkazu z rodičovského adresáře, druhý kvůli odkazu na sebe sama přes tečku) nebo v něm vytvoříme podadresář (za každý podadresář jeden hardlink kvůli adresáři ..).

Jediný problém je, odkud vzít data, abychom měli počet hardlinků, jméno souboru a také informaci o tom, jestli jde o adresář nebo ne. ls není vhodné kvůli zarovnávání mezerami, protože pak nedokážeme získat nepoškozené jméno souboru. Ale můžeme použít stat, kde si oddělovače můžeme zvolit podle svého a pokud umístíme název do posledního sloupečku, dokážeme ho snadno vytáhnout pomocí sloupečku 3-.


Pro zájemce: Tento skript by měl dát správné řešení i pro případ souborů, které mají v názvu \n. Zdůrazňuji, že do papírových testů toto řešení není potřeba, tam úplně stačí jednoduché řešení uvedené na začátku tohoto příspěvku. Jde skutečně jen o variantu pro perfekcionisty.

#!/bin/bash
max_links=0
max_name=
for name in * .*
do
  if ! [ "." == "$name" ]
  then
    if ! [ ".." == "$name" ]
    then
      if [ -d "$name" ]
      then
        links=`stat -c "%h" "$name"`
        if [ $max_links -lt $links ]
        then
          max_links=$links
          max_name=$name
        fi
      fi
    fi
  fi
done
echo "$max_name"

(Takhle do mnoha IFů je to rozepsané proto, aby se mi to lépe ladilo.) (Navíc tu mám zohlednění i skrytých podadresářů, to papírová úloha myslím také nepožaduje. Kdyby požadovala, musel by se ze statu výše odfiltrovat "podadresář" .., který fakticky podadresářem není.)

link

Comments

Děkuji, vypadá to již celkem pěkně akorát to vypsuje absolutní cestu a ne jméno, což by šlo asi nějak upravit třeba: stat -c "%A %h %n" /usr/* | grep ^d | sort -k2,2n | tail -1 | cut -d" " -f3- | sed 's/\/usr\///'. Bohužel větší problém je, že to neřeší případ názvu adresáře "A\nB" v tomto případě to vypíše pouze A resp. Vaše řešení pouze /usr/A. EDIT:chtěl jsem přidat výpis, ale nevypadalo to hezky

shejby (Nov 13 '14)

Absolutní cesta je daná tím, že žádám o /usr/*. Kdybych udělal napřed cd /usr a pak ve statu chtěl jen *, bylo by to bez cest. V testu je to jedno, takže jsem volil spíš přístup, který mě nenutí změnit aktuální pracovní adresář (protože měnit PWD je lepší dělat, až když není jiná alternativa).

Soubory s \n v názvu nejsou předmětem PS1, proto jsem je nepsal ani sem do řešení. Šly by řešit v rámci for nebo find, kdybychom vážně chtěli úplné řešení.

Josef Kokeš (Nov 13 '14)

To jsem nevěděl, že v některých případech stačí pseudo řešení a v některých ne. Vidím v tom trochu problém proměnná do "" opatrně na tr -s ale newline v názvech řešit nemusím a to ani v testu u PC ? Nebo se pletu ? EDIT: skript je hezký, to bych asi také svedl, ale ne na papír :-( proto mi přijde lepší řešení od @Honza Baier

shejby (Nov 13 '14)

Řešení od @Honza Baier je hezké a jednoduché a dalo by se jistě snadno napsat i do testu, ale:

1) Nebude fungovat všude (např. na Frayi), ale jen tam, kde je dostatečně pokročilý ls

2) Značná část dodatečné složitosti tohoto mého řešení jde na vrub skrytých adresářů, které Honza neřeší. Když si odmyslíte ty, tak ten for také snadno napíšete i do testu.

Pokud jde o ale newline v názvu řešit nemusím: Jde o to, že uvozovkovat proměnnou je triviální, to by měl zvládnout každý, kdo chce na FITu studovat. Obejít se bez tr -s už může být složitější, ale konec konců, stat byl zmiňován na přednáškách i na cvičeních; že na něj nebyly explicitní příklady, to je pravda, ale jsme na vysoké škole, student by měl vykazovat i vlastní iniciativu - takže když vidím ve slajdech něco, co bylo zmíněno jen ve výčtu, tak bych se měl aspoň podívat, co to zhruba dělá. Naproti tomu vypořádat se se znakem \n v názvu souboru, když těch souborů potřebuji zpracovat víc, je docela problém; shodou okolností nám pomohlo ls, protože na to má vhodný parametr, ale jinak by se to řešilo obtížně. Takže radši volíme cestu, že zpracování takhle problematického znaku po prvácích nevyžadujeme. (To mi připomíná, možná by se to mělo přidat do BI-PS2.)

Josef Kokeš (Nov 13 '14)

Jinak komu se nelíbí ty hnusné testy na . a .., tak do for místo * .* může napsat * .[^.]* ..?* Ale skoro bych řekl, že ty testy jsou přehlednější.

Ale pokud bych měl k dispozici escapující ls, tak použiju radši ten.

Josef Kokeš (Nov 14 '14)
add a comment
1

answered Nov 12 '14

S. gravatar image

updated Nov 12 '14

Jak bylo zjištěno v komentářích, tak záleží, jak ls vypisuje. U mě na Archu je to ls -l /usr | grep '^d'|sort -k2,2nr|head -1|tr -s ' '|cut -d' ' -f8-, zatímco na Solarisu je to ls -l /usr | grep '^d'|sort -k2,2nr|head -1|tr -s ' '|cut -d' ' -f9-.

link

Comments

Na konci spíš -f9- ne?

Greg (Nov 12 '14)

No, když si to vyzkouším, tak mi to na konci vychází na tu osmičku.

S. (Nov 12 '14)

Asi máme jinej výpis... drwxr-xr-x 413 root root 16384 lis 7 22:29 share

Greg (Nov 12 '14)

Je to tak, já mám datum ve formátu "DD.MM.YYYY HH:MM". Tudíž tam má být 8/9 dle systému. Doplním do komentáře.

S. (Nov 12 '14)

On teoreticky i ten uživatel může mít ve jméně třeba 5 mezer. Malinko univerzálnější (a delší) by pak bylo asi něco jako ls -d /usr/*/*/ | cut -d'/' -f3 | uniq -c | sort -nr | head -1 | tr -s ' ' | cut -d' ' -f3-

Greg (Nov 12 '14)
add a comment
1

answered Nov 12 '14

shejby gravatar image

updated Nov 12 '14

Tak naivní řešení je zde: (za kvalitu neručím) ls -l /usr | grep '^d' | tr -s ' ' | cut -d" " -f2,9- | sort -nr | head -1 | cut -d" " -f2-

EDIT: Jen doplním, toto řešení, není mé.(FIT-WIKI) Mě právě napadlo pár cest, jak to řešit různě přes cykly proměnné atp...ale to není řešení, které bych zvládl do těch čtverečků zplodit za 6m (navíc správně), tak mě zajímalo "správné řešení" které by se nechalo naspat s klidem do testu. Až naleznu správné řešení milerád ho sem updatnu.

link

Comments

1

V papírovém testu bychom to asi uznali (nevím jistě, jen odhaduji), ale v počítačovém ne. Uvědomte si, že tr -s ' ' vám zničí názvy souborů obsahující dvě a více po sobě jdoucích mezer. Plus se může stát, že narazíte na systém, kde jsou například v názvech skupin mezery a máte to rozhozené úplně. Miro Hrončok správně říká, že "cutovat ls -l je prasárna", právě proto, že to je extrémně systémově závislé.

Josef Kokeš (Nov 12 '14)

Předpokládám tedy, že i do papírového testu bych měl napsat správné řešení, nejlépe takové, aby fungovalo i na PC. Zajímá mě tedy správné řešení. Pokud se ho nedozvím zde, půjdu a budu se ptát na cvičení, pokud ani tam neuspěji, budu se ptát přednášejícího. Opravdu mě zajímá správné řešení, možná trochu s křížkem po funuse, co se 2. testu týče, ale třeba mi pomůže v PC testu nebo mě alespoň přinese zkušenost do života.

shejby (Nov 13 '14)

@shejby: Ono existuje správné a "správné" řešení. V rámci tohoto předmětu jsou uznávány obě. V praxi se občas může stát, že to "správné" řešení nebude na jednom z tisíce souborů fungovat, protože je prostě ve jméně třeba zrovna newline. EDIT: A aby nedošlo k nedoruzemění, tr -s, když se pracuje s názvy souborů, nepatří ani do jedné kategorie.

Honza Baier (Nov 13 '14)
add a comment
0

answered Nov 12 '14

Miro Hrončok gravatar image

updated Nov 12 '14

for dir in `find /usr -maxdepth 1 -type d -not -name .\*`; do echo -n "$dir " && find "$dir" -type d | wc -l; done | sort -rnk2 | head -1 | cut -d" " -f1

EDIT: Koukám, že jsme si nevšiml to nerekurzivně, tzn tohle řešení je stejně špatně.

link

Comments

BTW deset minut a s intenzivním zkoušením

Miro Hrončok (Nov 12 '14)

for ani find by v tomhle testu být neměl.

Greg (Nov 12 '14)

Tak to je humáč.

Miro Hrončok (Nov 12 '14)

Nevhodný algoritmus vede na obtížné programování a problematické výsledky, to by nemělo být až tak překvapující. Kdybyste zvolil vhodnější přístup, máte řešení na poloviční délku a funkční i bez zkoušení.

Josef Kokeš (Nov 12 '14)

Nevhodný algoritmus? Co konkrétně je na něm nevhodného? Vezmu všechny adresáře v /usr. Na to je vhodný find, ne ls a grep - používám nástroje ke svému účelu. No a pro každý adresář počítám adresáře uvnitř, na to je vhodný opět find, ne ls a grep. Obecně grepovat a cutovat ls -l je prasárna, protože výpis ovlivňuje mnoho faktorů.

Miro Hrončok (Nov 12 '14)

Vhodnější (ne ideální - skutečně není vhodné cutovat ls -l) řešení už zde byla uvedena. Proč to funguje, to bylo zmíněno na přednášce i na cvičeních (nevím, jestli na všech). Pokud vám toto doporučované řešení nevyhovuje a chcete z jakýchkoliv důvodů ("na to je vhodný find") použít vlastní postup, můžete - ovšem je na vás, abyste se vyrovnal s vyšší složitostí vašeho řešení.

Co konkrétně je na vašem algoritmu nevhodného? Přílišná složitost, přitom úplně zbytečná.

Josef Kokeš (Nov 12 '14)
1

Nevhodný algoritmus ovlivněn pokusem o použití v praxi. V rámci PS1 je jasné vodítko nerekurzivně == ls. Pokud si budeme hrát na praxi (ano, parsovat ls -l je prasárna, protože...), tak nestačí ani ten find. Mimochodem, for cyklus je zbytečný a nebude to s ním fungovat, protože hnusné znaky ve jménech souborů. Je potřeba použít exec ve findu a v rámci něj dělat něco dál (třeba pouštět další find).

Honza Baier (Nov 12 '14)

Pro úplnost: Vaše řešení trpí velmi podobnými neduhy jako "cutování ls -l", zejména v tom, že pro řadu adresářů nebude fungovat správně. Stačí, aby některý adresář ve svém jménu používal metaznaky shellu. Evidentní jsou mezery, ale zkuste třeba adresář pojmenovaný "*".

Josef Kokeš (Nov 12 '14)

@Greg: for cyklus byl přeci probraný již před 1. testem. Kdyby byl vhodný, proč ho nepoužít?

Defugio Auctoritatem (Nov 12 '14)

Aha, o tom jsem nevěděl...

Greg (Nov 12 '14)
see more comments

Your answer

Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!

Add answer

[hide preview]

Question tools

Follow
1 follower

Stats

Asked: Nov 12 '14

Seen: 729 times

Last updated: Nov 13 '14