Ask Your Question
1

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

asked 2014-11-12 07:13:20 +0100

shejby gravatar image

updated 2014-11-12 23:43:01 +0100

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)

edit retag flag offensive close delete

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š ( 2014-11-12 07:38:16 +0100 )edit
1

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

shejby ( 2014-11-12 23:22:52 +0100 )edit

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 ( 2014-11-13 15:27:20 +0100 )edit

"!" 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 ( 2014-11-13 15:58:15 +0100 )edit

5 Answers

Sort by » oldest newest most voted
4

answered 2014-11-13 15:30:24 +0100

updated 2014-11-13 15:38:11 +0100

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

edit flag offensive delete publish link more

Comments

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

Miro Hrončok ( 2014-11-13 20:18:55 +0100 )edit

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

shejby ( 2014-11-14 08:04:45 +0100 )edit

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 ( 2014-11-14 11:33:09 +0100 )edit

@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 ( 2014-11-14 12:19:48 +0100 )edit
3

answered 2014-11-13 13:28:44 +0100

Josef Kokeš gravatar image

updated 2014-11-13 15:33:08 +0100

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

edit flag offensive delete publish link more

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 ( 2014-11-13 14:40:22 +0100 )edit

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š ( 2014-11-13 14:44:22 +0100 )edit

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 ( 2014-11-13 16:07:21 +0100 )edit

Ř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š ( 2014-11-13 16:30:05 +0100 )edit

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š ( 2014-11-14 12:58:46 +0100 )edit
1

answered 2014-11-12 10:38:19 +0100

S. gravatar image

updated 2014-11-12 11:20:57 +0100

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

edit flag offensive delete publish link more

Comments

Na konci spíš -f9- ne?

Greg ( 2014-11-12 10:48:06 +0100 )edit

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

S. ( 2014-11-12 10:51:50 +0100 )edit

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

Greg ( 2014-11-12 10:54:29 +0100 )edit

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. ( 2014-11-12 11:18:32 +0100 )edit

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 ( 2014-11-12 12:15:35 +0100 )edit
1

answered 2014-11-12 09:46:38 +0100

shejby gravatar image

updated 2014-11-13 00:03:14 +0100

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.

edit flag offensive delete publish link more

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š ( 2014-11-12 13:05:11 +0100 )edit

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 ( 2014-11-13 10:20:57 +0100 )edit

@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 ( 2014-11-13 14:41:48 +0100 )edit
0

answered 2014-11-12 11:58:51 +0100

Miro Hrončok gravatar image

updated 2014-11-12 22:28:49 +0100

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

edit flag offensive delete publish link more

Comments

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

Miro Hrončok ( 2014-11-12 11:59:33 +0100 )edit

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

Greg ( 2014-11-12 12:03:21 +0100 )edit

Tak to je humáč.

Miro Hrončok ( 2014-11-12 12:05:04 +0100 )edit

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š ( 2014-11-12 12:17:52 +0100 )edit

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 ( 2014-11-12 12:43:52 +0100 )edit

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š ( 2014-11-12 12:53:11 +0100 )edit
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 ( 2014-11-12 12:55:43 +0100 )edit

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š ( 2014-11-12 13:00:09 +0100 )edit

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

Defugio Auctoritatem ( 2014-11-12 19:50:49 +0100 )edit

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

Greg ( 2014-11-12 20:10:21 +0100 )edit

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: 2014-11-12 07:13:20 +0100

Seen: 729 times

Last updated: Nov 13 '14