Seminár k operačným systémom: Cvičenie 5 [Bash I.]

Prvé cvičenie

Otvorte si ľubovoľný shell

  1. Prihlásením cez SSH k vhodnému serveru (s.ics.upjs.sk)
  2. V Linuxe spustením terminálu.
  3. Na Windowse nainštalovaním prostredia CygWin

Vypíšte Hello World

echo Hello World

Vypíše dva stringy: Hello a World.

Alternatívne:

echo "Hello World"

V tomto prípade je Hello World uvedený ako double quoted string. Shell rozoznáva viacero druhov reťazcov, o nich potom.

Kalkulačka

Vyskúšajte vlastnosti kalkulačky, zrátajte (2 + 3) * 10.

Prvý pokus

(2 + 3) * 10

povedie k chybe:

Syntax error near unexpected token *10

Shell totiž nie je vyhodnocovač aritmetických výrazov (na rozdiel od Powershellu, či pythonovského REPL).

Riešenie:

echo $(((2+3)*10))

$((..)) je prostredie pre vyhodnocovanie matematických výrazov.

V starých bashoch sa používa

echo $[(2+3)*10)]

Toto je deprecated, odporúča sa používať hore-uvedenú notáciu.

Zistite, koľko je 5 deleno 2 [cez shell]

Polosprávne riešenie

echo $((5 / 2))

Výsledkom je:

2

Aritmetické prostredie podporuje len celočíselnú aritmetiku.

Ak chcete reálnu aritmetiku, použite napr. nástroj bc, univerzálnu kalkulačku s ľubovoľnou presnosťou.

Zistiť aktuálnu verziu shellu

Na Bashi funguje

echo $SHELL

Výpisy adresárov

Zistite, v ktorom adresári sa práve nachádzate.

Zistiť to môžeme pohľadom na výzvu (prompt).

Zistite, v ktorom adresári sa práve nachádzate pomocou pwd.

pwd

Zobrazte súbory a adresáre v aktuálnom podadresári pomocou ls.

ls

Zobrazte len názvy súborov či adresárov v aktuálnom adresári.

ls –1

Parametre majú dlhú formu (cez –) a krátku formu (cez -). Nefunguje svojvoľné skracovanie parametrov ako v PowerShelli.

Vypíšte obsah vlastného domovského adresára [variant 1]

cd
ls
  • cd bez parametrov presúva do domovského adresára.
  • ls vypíše obsah aktuálneho adresára

Vypíšte obsah vlastného domovského adresára — viacero príkazov na riadku

Na riadku možno uviesť viacero príkazov oddelených bodkočiarkou:

cd;ls

Expanzia vlnky

Vypíšte cestu k domovskému adresáru

echo ~

Výsledkom je (napr.)

/home/novotnyr

Pred vykonaním príkazu prebehne v shelli expanzia slov (word expansion), kde sa počas viacerých fáz vykoná nahrádzanie špeciálnych _token_ov. Fázy teraz nebudeme rozoberať, zaujíma nás len prvá: expanzia vlnky.

Ešte pred vykonaním príkazu sa vlnka ~ nahradí slovom, ktoré obsahuje cestu k domovskému adresáru. Príkaz echo ~ teda expanduje napr. na echo /home/novotnyr a až následne sa vykoná.

Skutočná cesta sa berie z premennej $HOME, ale o tom neskôr.

Vypíšte cestu k domovskému adresáru roota

echo ~root

Expanzia vlnky má špeciálnu vlastnosť: ak uvedieme za ňou login používateľa, expanduje sa na cestu k jeho domovskému adresáru:

/root   

Ale zase

echo ~novotnyr

vedie k

/home/novotnyr  

Poznámka

Špecifikácia k expanzii vlnky je na http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_01

Späť k adresárom

Vypíšte obsah vlastného domovského adresára — ls s parametrom

Z ľubovoľného adresára:

ls ~

Parameter ls uvádza adresár, ktorého obsah sa má vylistovať. V tomto prípade sa príkaz expanduje na

ls /home/novotnyr

a vypíše sa výsledok (napr.)

data  public_html

Vypíšte všetky súbory a adresáre v /home

ls /home

Filozofia je rovnaká ako v predošlom príklade:

apache  john25  novotnyr

Vypíšte všetky súbory a adresáre v /home pod seba

ls -1 /home

Vypíše jeden súbor na riadok:

apache
john25
novotnyr

Vytvorte adresár textfiles v domovskom adresári

cd
mkdir textfiles
cd textfiles

Vytvorte tri prázdne súbory file1.txtfile3.txt a súbor data.dat.

touch file1.txt file2.txt file3.txt data.dat

Vypíšte zoznam všetkých súborov [repríza, expanzia cesty]

echo *

Čo sa presne stane?

Spomínali sme, že pred vykonaním príkazu prebehne v shelli word expansion. O niekoľko fáz po expanzii vlnky prebieha expanzia cesty (pathname expansion), zvané tiež globbing

Každý globbovateľný výraz expanduje na zoznam názvov súborov, ktoré spĺňajú jeho definíciu. Napr. * znamená „ľubovoľný reťazec” a v adresári z predošlého príkladu expanduje na

file1.txt file2.txt file3.txt data.dat

Príkaz

echo *

sa teda expanduje na

echo file1.txt file2.txt file3.txt data.dat

Vypíšte zoznam všetkých .txt súborov

echo *.txt

to expanduje na všetky názvy súborov, ktoré začínajú ľubovoľným reťazcom a končia na .txt, čiže všetky texťáky.

Globbing

V globbingu sa používa obmedzená sada regulárnych výrazov podobná C:

*   hviezdička (nematchuje skryté súbory!)
*   otáznik
*   zoznam znakov v hranatých zátvorkách
*   zobákom sa neguje

Globbing je záležitosť shellu a nie operačného systému! Rozličné shelly môžu ponúkať ďalšie pokročilé možnosti pre globbing.

Historické okienko: v 70tych rokoch globbing nefungoval, riešil sa cez /etc/glob, ktorý hľadal súbory v /bin, neskôr v PATH, teraz sa hľadá v aktuálnom adresári. Popis viď http://cm.bell-labs.com/cm/cs/who/dmr/man71.pdf

Vypíšte zoznam všetkých .txt súborov [verzia 2, cez ls, podivná]

ls *.txt

Toto bude fungovať, ale zo zlých dôvodov.

V skutočnosti tento príkaz expanduje na

ls file1.txt file2.txt file3.txt

ls teda vypíše obsah súboru file1.txt, file2.txt atď, čo zhodou okolností urobí presne to, čo chceme.

Vypíšte všetky súbory a adresáre v /etc, ktoré sa začínajú na „p“.

echo /etc/*p

Keďže poznáme zásady globbingu, máme, čo sme chceli.

Zlý nápad

ls /etc/p*

Opäť prebehne expanzia a až následne spustenie ls. Výraz /etc/p* expanduje na zoznam súborov (u vás na iný):

/etc/pam.conf /etc/pam.d /etc/pango /etc/papersize /etc/passwd /etc/passwd- /etc/perl /etc/php5 /etc/phpmyadmin /etc/ppp /etc/profile /etc/profile.d /etc/protocols /etc/pulse /etc/python /etc/python2.6

Príkaz teda bude?

ls /etc/pam.conf /etc/pam.d /etc/pango /etc/papersize /etc/passwd /etc/passwd- /etc/perl /etc/php5 /etc/phpmyadmin /etc/ppp /etc/profile /etc/profile.d /etc/protocols /etc/pulse /etc/python /etc/python2.6

ls postupne vypíše súbor pam.conf, ale následne vypíše obsah adresára /etc/pam.d, čo nie je to, čo chceme, následne vypíše /etc/pango, atď.

Zlý nápad cez grep

ls | grep p*

Začne grepovať obsahy súborov.

Verzia cez find

find –name "p*"

Funguje, ale začne hľadať aj v podadresároch rekurzívne.

Verzia cez find (korektná, nerekurzívna)

find -name 'p*' -maxdepth 1

Verzia cez ls

ls –d /etc/p*

Dokumentácia tvrdí, že parameter –d pre argument typu adresár nevypíše jeho obsah, ale meno. To vieme s výhodou použiť a zabrániť divokému listingu pre položky expandované v globbingu.

Vypíšte všetky súbory a adresáre v /etc, ktoré sa začínajú na „p“ pod seba

printf "%s\n" *

printf má známu syntax z C. Pozor ale: nepoužívajú sa zátvorky oddeľujúce argumenty a ani čiarky! Používame shellovú filozofiu! printf má minimálne parametre: formátovací reťazec a dáta.

V tomto prípade sa expanduje na

printf "%s\n" file1.txt file2.txt file3.txt

Hoci máme len jeden formátovací príkaz %s, ten sa opakovane použije na všetky parametre, kým to ide. \n je klasický koniec riadka.

Spočítajte počet súborov z predošlej úlohy

Použijeme wc, počítadlo slov, riadkov…

echo /etc/*p | wc -w

Parameter -w spočíta počet slov na vstupe.

Alternatívne

ls –d /etc/p* | wc -l

Parameter -l spočíta počet riadkov na vstupe.

Vypíšte len adresáre v domovskom adresári

ls –l | grep "^d"

Alternatívne

find . –type d –maxdepth 1

Alternatívne

ls –d */

Výraz */ expanduje na zoznam adresárov ukončených lomkou

public_html/  textfiles/

Celý ls -d */ expanduje na

ls –d public_html/  textfiles/

Parameter -d zabráni tomu, aby sa ls postupne vnáral do adresárov, ale vypíše len ich mená.

Zlý pokus

Pokus s –d nevedie k ničomu. Dokumentácia tvrdí, že parameter –dpre argument typu adresár nevypíše jeho obsah, ale meno.

ls -d

Zistite, koľko súborov sa nachádza v domovskom adresári a jeho podadresároch.

find . –type f | wc -l

Vypíšte súbory a adresáre v domovskom adresári zotriedené podľa mena

ls | sort

Tento príkaz robí to isté, čo prostý ls, pretože súbory na výstupe sú už štandardne zotriedené podľa mena.

Vypíšte súbory zotriedené podľa veľkosti

ls –la | sort –k5n

Parametre: * -l vypíše kompletný tabuľkový zoznam * -a vypíše aj skryté súbory

Parametre pre sort: * -k5n utriedi podľa piateho stĺpca, ktorý bude chápať ako číslo (-n). Stĺpce sú defaultne oddelené bielymi miestami.

Alternatívne cez ls –S

`ls -Sa1`
  • -S sortí podľa veľkosti,
  • -a berie aj skryté súbory,
  • -1 vypíše pod seba len názvy

Poznámka

Sortiť sa dá aj cez

  • -X podľa prípony
  • -S podľa veľkosti
  • -t podľa času
  • -v podľa verzie

Vypíšte súbory a adresáre zotriedené podľa veľkosti zostupne

ls  –la | sort –k2,2n -r

Nájdite najväčší súbor v domovskom adresári (nevnárajte sa do podadresárov)

ls –la | sort –k5,5n –R | tail –n 1

Alebo naopak

ls –la | sort –k5,5n | head –n 1

Osekať zbytočné dáta môžeme awkom.

Alternatívne cez find

find . –maxdepth 1 –type f –printf "%f\t%s\n" |sort –k2n | tail –n1 |cut –f1

Alternatívne cez find a ls

find / -type f -exec  ls -s {} \; | sort -n | tail -1

Alternatívne cez du, ls, head a subshell

du -k $(ls -Sa1 | head -n1)
  • Parameter -k vypisuje 1024bajtové bloky miesto štandardných 512bajtových.
  • Parameter -S je GNU parameter pre triedenie podľa veľkosti
  • Parameter -a vráti aj skryté súbory.
  • head -n1 vráti prvý riadok

Vypíšte plné cesty k všetkým súborom a adresárom v aktuálnom adresári

ls -d1 $PWD/*

Grepovanie

Vypíšte mená používateľov v systéme

Úvaha: vypíšeme používateľov podľa adresárov z /home. Nie každý používateľ však musí mať domovský adresár, a už vôbec nie v /home (príkladom je root v /root).

Prvý nástrel:

cat /etc/passwd | cut -d":" -f1

Toto je ale zbytočné použitie cat. Postačí presmerovanie súboru na štandardný vstup programu cat

cut -d":" -f1 < /etc/passwd     

Vypíšte mená používateľov, ktorí nemajú domovský adresár v /home

Môžeme zobrať šiestu položku z /etc/passwd a overiť, že nezačína na /home:

cut -d':' -f6 /etc/passwd | grep -v ^/home

Parameter -v znamená negáciu pre grep. Zobák znamená “od začiatku riadku”.

Ak chceme aj mená používateľov, vyberme prvú a šiestu položku. Pozor, cut nedokáže vrátiť najprv šiestu a potom prvú položku, preto musíme upraviť výraz: hľadáme výraz začínajúci dvojbodkou a /home:

cut -d':' -f16 /etc/passwd | grep -v :/home

Vypíšte skupiny, do ktorých patrí používateľ X

cat /etc/group | grep novotnyr | grep –v ^novotnyr | cut –d : -f1

Toto je nezmyselné použitie catu. Použime buď presmerovanie:

grep novotnyr < /etc/group | grep –v ^novotnyr | cut –d : -f1   

alebo využime vlastnosť grepu, ktorý môže zobrať aj názov súboru ako parameter

grep novotnyr /etc/group | grep –v ^novotnyr | cut –d : -f1 

Alternatívne

grep [,:]novotnyr /etc/group | cut –d : -f1

Zaspievajte si so shellom “Šedzi mucha na scene”

Vytvorme mucha.txt

Sedzi mucha na scene
na scene
na scene
sedzi mucha na scene
sedzi a spi
sedzi a buvinka
potvora malinka
sedzi mucha na scene
sedzi a spi

Vyskúšajme

tr aeiou a < mucha.txt

tr nahrádza jeden znak iným znakom. Berie dva zoznamy znakov a ity znak z prvého zoznamu nahradí itym znakom z druhého zoznamu.

Zobrazte rebríček najpopulárnejšich shellov v systéme

cut -d':' -f7 /etc/passwd | sort | uniq -c | sort -rn

Zobrazte najpopulárnejší shell v systéme

cut -d':' -f7 /etc/passwd | sort | uniq -c | sort -rn | tr -s ' ' | cut -d' ' -f3

Vygenerujte heslo pre používateľa

tr -dc [:alnum:] < /dev/urandom | head -c 10

Vysvetlenie viď: http://ics.upjs.sk/~novotnyr/blog/75/generovanie-hesiel-pre-loginy-v-shelli

Vypíšte 10 krát “Budem si robiť domáce úlohy”

seq 10 | sed 's/.*/Budem si robit DU/'

Vypíšme 10 čísiel na štandardný vstup a každé z nich nahraďme sedom za konštantný text.

Vypíšte 10 krát “Budem si robiť domáce úlohy” [shell]

#!/bin/bash

for I in 1 2 3 4 5 6 7 8 9 10
do
  echo Budem si robit domace ulohy
done

for cyklus je v skutočnosti foreach. Prechádza každým slovom za klauzulou in a vykoná pre ňu iteráciu. (Slovo je reťazec, čísla sa v shelli špeciálne nerozlišujú).

Ak sa nechceme upísať k smrti, môžeme využiť $(...): príkaz v tomto prostredí sa spustí v podshelli a jeho výstup sa použije ako výsledok expanzie:

for I in $(seq 10)

spustí príkaz seq a jeho výstup (slová 1, 2, … 10) sa použije v expanzii:

for I in 1 2 3 4 5 6 7 8 9 10

Celý skript

#!/bin/bash

for I in $(seq 100)
do
  echo Budem si robit domace ulohy
done

Skript začína tzv. “shebang” riadkom (od hash bang, teda mreža-výkričník) s cestou k programu, ktorý spustí náš skript — u nás je ním shell.

Vytvorte skript pre generovanie hesla

#!/bin/bash

POCET_ZNAKOV=$1

tr -dc [:alnum:] < /dev/urandom | head -c $POCET_ZNAKOV
printf "\n"

Využime zároveň premennú pre dĺžku. Zásady:

  • pri deklarovaní premennej nesmieme použiť dolár
    • dolár chápme ako “čítaj obsah z premennej”
  • pri priradzovaní hodnoty nesmú byť okolo “rovná sa” medzery
    • shell by chápal POCET_ZNAKOV = 10 ako “zavolaj príkaz POCET_ZNAKOV a daj mu dva parametre: rovná sa a 10″

Premenná $1 obsahuje prvý parameter z príkazového riadka. Skript potom môžeme volať cez

./generuj_heslo 15

Dokonca aj jeho výstup môžeme využiť v podshelli:

echo novotnyr:$(./generuj_heslo 15)

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *