Principalul atu al utilizării aplicațiilor în linie de comandă, și nu în mediul grafic, este viteza cu care rezolvăm anumite sarcini. Această viteză este dată de posibilitatea de a combina utilitare în linia de comandă pentru a automatiza procese manuale, repetitive. Întotdeauna ne dorim să fim mai rapizi și să automatizăm cât mai mult din sarcinile noastre, deoarece cu cât ne terminăm treaba mai repede, cu atât avem mai mult timp liber la dispoziție.
De regulă, cu cât petrecem mai mult timp cu mâna pe tastatură și mai puțin pe mouse, cu atât suntem mai rapizi. Aliniate la această idee, aplicațiile ne pun la dispoziție scurtături pe care suntem încurajați să le folosim pentru a ne utiliza timpul mai eficient.
Așa cum spuneam mai devreme, marele avantaj al utilizării terminalului este că ne ajută să rezolvăm sarcini foarte rapid. Rezolvăm sarcini folosind utilitarele pe care le avem disponibile în linia de comandă, fie că acestea fac parte din sistemul nostru sau le-am instalat.
Cel mai important utilitar pe care îl avem la dispoziție este man
. Utilitarul man
ne deschide pagina de manual în care este documentat un alt utilitar pe care dorim să-l folosim.
student@uso:~$ man What manual page do you want?
Putem consulta însăși pagina de manual a utilitarului man
student@uso:~$ man man
Vom fi întâmpinați de următorul program interactiv:
MAN(1) Manual pager utils MAN(1) NAME man - an interface to the on-line reference manuals SYNOPSIS man [-C file] [-d] [-D] [--warnings[=warnings]] [-R encoding] [-L locale] [-m system[,...]] [-M path] [-S list] [-e extension] [-i|-I] [--regex|--wildcard] [--names-only] [-a] [-u] [--no-subpages] [-P pager] [-r prompt] [-7] [-E encoding] [--no-hyphenation] [--no-justifi‐ cation] [-p string] [-t] [-T[device]] [-H[browser]] [-X[dpi]] [-Z] [[section] page[.section] ...] ... man -k [apropos options] regexp ... man -K [-w|-W] [-S list] [-i|-I] [--regex] [section] term ... man -f [whatis options] page ... man -l [-C file] [-d] [-D] [--warnings[=warnings]] [-R encoding] [-L locale] [-P pager] [-r prompt] [-7] [-E encoding] [-p string] [-t] [-T[device]] [-H[browser]] [-X[dpi]] [-Z] file ... man -w|-W [-C file] [-d] [-D] page ... man -c [-C file] [-d] [-D] page ... man [-?V] Manual page man(1) line 1 (press h for help or q to quit)
Observăm că ultima linie din terminal, Manual page man(1) line 1 (press h for help or q to quit), ne oferă mai multe informații:
h
pentru a acesa meniul de ajutorq
pentru a ieși din manual
Navigăm cu câte o linie de terminal în joș și în sus folosind folosind tastele Ctrl+n
și Ctrl+p
. Putem folosi tastele Ctrl+f
și Ctrl+b
pentru a naviga, cu câte un ecran de terminal, în jos și în sus în pagină. Mai simplu, putem folosi tasta Enter
pentru a naviga cu câte o linie în jos și tasta Space
pentru a naviga cu câte un ecran în jos. Navigăm la începutul paginii folosind tasta g
. Navigăm la sfârșit paginii folosind tasta G
.
j
și k
ca alternative pentru Arrow Down
și Arrow Up
. Astfel suntem mai rapizi pentru că nu ne mai mutăm mâna de pe tastele caractere.
Folosim man
ca să vedem dacă un utilitar oferă o anumită funcționaltiate. Citim întreaga pagină de manual ca să vedem toate funcționalitățile sau căutăm o funcționalitate folosind cuvinte cheie. Pașii pentru căutarea unui cuvânt cheie sunt următorii:
/
în sesiunea interactivă din man
.Enter
. Vom fi duși la primul rezultat care se potrivește căutării, dacă acesta există.n
. Dacă vrem să navigăm la un rezultat anterior apăsăm tasta N
.Căutarea1) are loc de la poziția curentă în pagină către sfârșitul paginii. Dacă am navigat deja în interiorul paginii, trebuie să avem în vedere că rezultatul de interes al căutării noastre se poate afla undeva între începutul paginii și poziția noastră curentă2).
La o primă vedere, textul paginii de manual poate fi intimidant; unele utilitare au mai multe opțiuni și argumente, unele opționale, altele nu. O să trecem prin sintaxă și o să vedem că lucrurile sunt mult mai simple decât par. Să analizăm pagina de manual a utilitarului ls
; man ls
:
LS(1) User Commands LS(1) NAME ls - list directory contents SYNOPSIS ls [OPTION]... [FILE]... DESCRIPTION List information about the FILEs (the current directory by default). Sort entries alphabetically if none of -cftuvSUX nor --sort is speci‐ fied. Mandatory arguments to long options are mandatory for short options too. -a, --all do not ignore entries starting with . -A, --almost-all do not list implied . and .. --author Manual page ls(1) line 1 (press h for help or q to quit)
ls
, descrierea ne informează că acesta afișează informații despre fișierele din calea indicată, sau din directorul curent atunci când nu specificăm o cale.SYNOPSIS ls [OPTION]... [FILE]...
Sintaxa [ ] ne spune că acea categorie este opțională. Astfel, pentru ls
, deducem că atât opțiunile ([OPTION]...
) cât și argumentele ([FILE]...
, calea către fișiere sau directoare) sunt opționale. Cele trei puncte ...
înseamnă mai multe din categoria precedentă: deci [OPTION]...
înseamnă că nu suntem limitați la o singură opțiune, dar opțiunile pot să și lipsească în totalitate datorită [ ].
O comandă poate avea atât opțiuni, cât și argumente. Opțiunile îi spun unei comenzi cum să își modifice comportamentul, și de obicei sunt precedate de -
(ex. -l
, --verbose
, etc.). Argumentele îi spun unei comenzi pe ce să acționeze.
În exemplul de mai jos:
student@uso:~$ ls -l Desktop/
Avem utilitarul ls
care primește opțiunea -l
și argumentul Desktop/
.
Ultima observație pe care o facem este că opțiunile unei comenzi pot avea o formă prescurtată, -a
, sau o formă lungă, --all
. Nu este obligatoriu ca o opțiune să expună ambele forme, deși majoritatea o fac. Opțiunile în formă prescurtată pot fi concatenate și precedate de un singur -
, ca în exemplul de mai jos:
student@uso:~$ ls -la Desktop/
Deschideți pagina de manual a utilitarului ls
.
-a
. Rulați comanda ls -a
.-d
. Rulați comanda ls -d
.-F
. Rulați comanda ls -F
.list
. Treceți la următoarea apariție a cuvântului cheie până ajungeți la opțiunea -l
.G
. Căutați cuvântul cheie color
până ajungeți la opțiunea --color
(Hint: ?
).
În laboratorul Lucrul cu fișiere am văzut cum folosim comanda ls
pentru a afișa conținutul unui director și pentru a explora sistemul de fișiere. În continuare vom vedea cum folosim ls
pentru a afișa mai multe informații despre conținutul unui director sau despre fișiere.
În mediul linux, un fișier este ascuns dacă numele său începe cu caracterul .
(punct). În mod implicit, utilitarul ls
omite fișierele ascunse. Pentru a afișa fișierele ascunse folosim opțiunea -a
(all).
student@uso:~$ ls -a . .emacs.d .ssh Pictures .. .gconf .sudo_as_admin_successful Public .ICEauthority .gitconfig .tmux Templates .bash_aliases .gnome2 .tmux.conf Videos .bash_history .gnome2_private .vim examples.desktop .bash_logout .gnupg .viminfo uso.git .bashrc .java .vimrc vm-actions-log.txt .cache .lesshst Desktop workspace .config .local Documents .dbus .mozilla Downloads .emacs .profile Music
Observăm că avem o mulțime de fișiere ascunse prezente în directorul nostru home. Multe dintre acestea sunt fișiere de configurare (.bashrc
, .vimrc
, etc.) folosite de diferite programe instalate pe sistemul nostru. Vom vorbi mai multe despre acestea în viitorul apropriat.
De cele mai multe ori suntem interesați să aflăm mai multe informații despre fișiere: cum ar fi tipul fișierului, permisiuni, ownership, dimensiunea și data ultimei modificări. Toate acestea sunt afișate prin utilizarea opțiunii -l
:
student@uso:~$ ls -l total 60 drwxr-xr-x 2 student student 4096 aug 6 2018 Desktop drwxr-xr-x 3 student student 4096 aug 20 2018 Documents drwxr-xr-x 2 student student 4096 aug 11 19:35 Downloads drwxr-xr-x 2 student student 4096 aug 6 2018 Music drwxr-xr-x 3 student student 4096 aug 31 23:26 Pictures drwxr-xr-x 2 student student 4096 aug 6 2018 Public drwxr-xr-x 2 student student 4096 aug 6 2018 Templates drwxr-xr-x 2 student student 4096 aug 6 2018 Videos -rw-r--r-- 1 student student 8980 aug 6 2018 examples.desktop drwxr-xr-x 14 student student 4096 aug 20 2018 uso.git -rw-r--r-- 1 student student 4827 aug 21 2018 vm-actions-log.txt drwxr-xr-x 4 student student 4096 aug 13 18:38 workspace
Vom analiza informațiile afișate pentru directorul Desktop.
drwxr-xr-x 2 student student 4096 aug 6 2018 Desktop
Vom începe cu prima coloană din exemplul de mai sus: drwxr-xr-x
. Aceasta este formată din zece caractere care formează patru grupuri:
d
ne informează că ne uităm la un fișier de tip director. În cazul fișierelor obișnuite (text, imagini, etc.) primul caracter este -
, așa cum putem observa în cazul fișierului examples.desktop
.r
(read) permisiuni de citire, w
(write) permisiuni de scriere și x
(execute) permisiuni de rulare. Dacă utilizatorul nu are o anumită permisiune, caracterul corespunzător este înlocuit de caracterul -
. Spunem că aceste permisiuni se aplică pentru User. Mai multe despre permisiuni in laboratoarele viitoare, deoarece sunt un topic foarte important.rwx
. Spunem că aceste permisiuni se aplică pentru Group.rwx
. Spunem că aceste permisiuni se aplică pentru Others.Acum, pe baza informațiilor din prima coloană, putem spune următoarele despre fișierul Desktop:
d
)r
), scriere (w
) și execuție (x
)r
), NU are drepturi de scriere (-
) și are drepturi de execuție (x
)r
), NU are drepturi de scriere (-
) și are drepturi de execuție (x
).
x
) asupra acestuia. Trebuie să avem drepturi de execuție indiferent că vrem să navigăm în interiorul său sau să afișăm conținutul directorului. Pentru a crea noi fișiere și directoare în cadrul acestuia, avem nevoie de drepturi de scriere (w
).
Cea de-a treia coloană ne spune care este utilizatorul care deține fișierul. Astfel observăm că directorul Desktop este deținut de către utilizatorul student
. Asta înseamnă că permisiunile rwx
corespund utilizatorului student
.
Cea de-a patra coloană ne spune care este grupul care deține fișierul. Astfel observăm că directorul Desktop este deținut de către grupul student
. Asta înseamnă că permisiunile r-x
se aplică oricărui utilizator care este membru al grupului student
.
Cea de-a cincea coloană ne arată dimensiunea fișierului, exprimată în octeți. Putem să-i cerem utilitarului ls
să ne afișeze dimensiunea folosind multiplii (K(ilo), M(ega), G(iga), etc) utilizând opțiunea -h
(human readable)
student@uso:~$ ls -lh total 60K drwxr-xr-x 2 student student 4,0K aug 6 2018 Desktop [...]
Ultimele coloane ne arată data ultimei modificări, în ordinea lună, zi, an.
Am observat că, în mod implicit, utilitarul ls
ne afișază informații despre conținutul unui director atunci când primește calea către un director ca argument:
student@uso:~$ ls -l Desktop/ total 0 -rw-r--r-- 1 student student 0 sep 2 19:39 todos.txt
Pentru a-i specifica lui ls
că suntem interesați de informații despre fișierul de tip director, și nu despre conținutul său, folosim opțiunea -d
.
student@uso:~$ ls -ld Desktop/ drwxr-xr-x 2 student student 4096 sep 2 19:39 Desktop/
/home
, Downloads
și /tmp
./home
, /home/student
și /tmp
.
În sintaxa globbing, caracterul *
poate fi înlocuit cu orice caracter de oricâte ori, sau poate lipsi cu totul. În directorul nostru home (~
), executăm următoarele comenzi:
student@uso:~$ ls Desktop Downloads Pictures Templates examples.desktop vm-actions-log.txt Documents Music Public Videos uso.git workspace student@uso:~$ ls -d D* Desktop Documents Downloads student@uso:~$ ls -d Music* Music
Observăm că în expresia D*
, caracterul *
înglobează toate caracterele care urmează literei D: “esktop”, “ocuments” și “ownloads”. Observăm că în cazul expresie Music*
, *
nu ține locul nici unui caracter.
Există cazuri când numele fișierelor conțin caractere speciale. Unele fișiere pot fi prefixate cu o categorie din care fac parte, ca în exemplul de mai jos:
student@uso:~/uso-lab/labs/05-cli/support$ ls tutorial/uni/ '[USO] Course 01.pdf' '[USO] Course 02.pdf'
În exemplul de mai sus, fișierele pdf de curs sunt prefixate cu numele materiei: [USO]. Vrem să îi spunem sintaxei de globbing că în acest caz, șirul [USO] nu trebuie tratat ca o expresie, ci ca un șir de caracter normale. Pentru a face acest lucru, încadrăm șirul între “:
student@uso:~/uso-lab/labs/05-cli/support$ ls support-globbing/"[USO]"* 'support-globbing/[USO] cursuri 1.ppt' 'support-globbing/[USO] slides 1.ppt' 'support-globbing/[USO] cursuri 10.ppt' 'support-globbing/[USO] slides 10.ppt' 'support-globbing/[USO] cursuri 2.ppt' 'support-globbing/[USO] slides 2.ppt' 'support-globbing/[USO] cursuri 3.ppt' 'support-globbing/[USO] slides 3.ppt' 'support-globbing/[USO] cursuri 4.ppt' 'support-globbing/[USO] slides 4.ppt' 'support-globbing/[USO] cursuri 5.ppt' 'support-globbing/[USO] slides 5.ppt' 'support-globbing/[USO] cursuri 6.ppt' 'support-globbing/[USO] slides 6.ppt' 'support-globbing/[USO] cursuri 7.ppt' 'support-globbing/[USO] slides 7.ppt' 'support-globbing/[USO] cursuri 8.ppt' 'support-globbing/[USO] slides 8.ppt' 'support-globbing/[USO] cursuri 9.ppt' 'support-globbing/[USO] slides 9.ppt'
Citim expresia "[USO]"*
: orice fișier al cărui nume începe cu șirul de caractere [USO] și este urmat de orice caracter. Operația prin care eliminăm semnificația specială a unui caracter poartă numele de escaping; cu alte cuvinte, informal, spunem că am făcut escaping semnificației speciale a sintaxei []
. Termenul vine de la cuvântul escape (a scăpa), și exprimă că scăpăm de semnificația specială a unui caracter / set de caractere.
Pentru exercițiile următoare vom folosi fișierele din directorul de suport ~/uso-lab/labs/05-cli/support/support-globbing
.
pdfs
. Mutați toate fișierele cu extensia .pdf
din directorul curent în directorul pdfs
.cursuri/anul-I
. De ce nu funcționează crearea directorului? Există directorul cursuri
? Mutați toate fișierele care conțin cuvintele curs sau slide în directorul creat. Extra: Folosiți sintaxa *{curs,slide}*
.Excursie Brasov, 2020-2021
. Mutați fișierele DCIM din intervalul 1400 - 1700 în directorul creat.De multe ori ne aflăm în situația în care căutăm un fișier pe disc: ex. doar ce am clonat un proiect de pe GitHub și vrem să inspectăm fișierul Makefile pentru a vedea cum compilăm și rulăm proiectul. Un alt exemplu poate fi că vrem să vedem cum arată fișierele de test existente în proiect; de multe ori, ințelegem mai bine proiectul doar prin simpla inspectare a testelor.
Există două utilitare care ne permit să căutăm în cadrul sistemului de fișiere: locate
și find
.
Utilitarul find
îndeplinește scopul evident de a căuta în fișierele de pe sistem. find
este un utilitar mai complex decât locate
. Acesta ne permite să căutăm fișiere după nume, permisiuni, tipul fișierelor, data ultimei modificări și multe altele. Inspectăm pagina de manual a utilitarului pentru a vedea cum îl putem folosi.
student@uso:~$ man find SYNOPSIS find [-H] [-L] [-P] [-D debugopts] [-Olevel] [starting-point...] [expression]
Să clonăm un repository cu algoritmi de sortare:
student@uso:~$ mkdir workspace student@uso:~$ cd workspace student@uso:~/workspace$ git clone https://github.com/TheAlgorithms/C.git student@uso:~/workspace$ cd C
La o primă vedere, find
poate părea complex și intimidant, dar lucrurile stau foarte simplu. Folosim find
cu sintaxa find [starting-point] [expression]
, ca în exemplul de mai jos:
student@uso:~/workspace/C$ cd ../.. student@uso:~$ find . -name "*search*" ./workspace/C/searching ./workspace/C/searching/linear_search.c ./workspace/C/searching/other_binary_search.c ./workspace/C/searching/binary_search.c ./workspace/C/searching/modified_binary_search.c ./workspace/C/searching/jump_search.c ./workspace/C/searching/interpolation_search.c ./workspace/C/searching/fibonacci_search.c ./workspace/C/searching/ternary_search.c ./workspace/C/searching/pattern_searc h ./workspace/C/searching/pattern_search/naive_search.c ./workspace/C/searching/pattern_search/boyer_moore_search.c ./workspace/C/searching/pattern_search/rabin_karp_search.c ./workspace/C/data_structures/binary_trees/binary_search_tree.c
În exemplul de mai sus observăm că am folosit ca starting-point .
(căutarea pleacă din directorul curent), iar ca expression -name "*search*"
.
Utilitarul find
folosește o expresie compusă pentru căutare. În exemplul anterior am folosit opțiunea -name PATTERN
. Exact ca în cazul utilitarului locate
, PATTERN poate folosi sintaxa globbing, așa cum am făcut în exemplul de mai sus "*search*"
. Există mai multe opțiuni pentru căutarea cu find
, prezente în manual.
"
(ghilimele), așa cum am făcut în exemplul de mai sus. Trebuie să facem asta pentru ca sintaxa globbing să fie interpretată de către utilitarul find
și nu de către terminalul (bash
) din care lansăm utilitarul.
În secțiunea anterioară, Inspectarea sistemului de fișiere
, am văzut cum căutăm fișiere în sistem cu ajutorul utilitarului find
. Căutăm un fișier cu un scop: vrem să găsim fișierul README
pentru informații despre compilarea proiectului, vrem să ne amintim un detaliu de implementare din cod, etc.
De cele mai multe ori acțiunea noastră se poate grupa în una din următoarele două categorii:
Pentru a vedea rapid conținutul unui fișier folosim utilitarul less
.
man
folosește utilitarul less
pentru a afișa paginile de manual. less
este pagerul implicit în majoritatea distribuțiilor Linux.
Avem fișierul workspace/C/searching/binary_search.c
. Vrem să ne facem rapid o idee despre cum arată implementarea algoritmului binary_search. Inspectăm conținutul fișierului workspace/C/searching/binary_search.c
, folosind utilitarul less
, ca în exemplul de mai jos:
student@uso:~$ less workspace/C/searching/binary_search.c /** * @file * @brief Program to perform [binary * search](https://en.wikipedia.org/wiki/Binary_search_algorithm) of a target * value in a given *sorted* array. * @authors [James McDermott](https://github.com/theycallmemac) - recursive * algorithm * @authors [Krishna Vedala](https://github.com/kvedala) - iterative algorithm */ #include <assert.h> #include <stdio.h> /** Recursive implementation * \param[in] arr array to search * \param l left index of search range * \param r right index of search range * \param x target value to search for * \returns location of x assuming array arr[l..r] is present * \returns -1 otherwise */ int binarysearch1(const int *arr, int l, int r, int x) { if (r >= l) { int mid = l + (r - l) / 2; // If element is present at middle if (arr[mid] == x) return mid; :
Observăm că acum avem o sesiune interactivă în interiorul căreia putem explora fișierul.
less
putem folosi aceeleași taste ca în cadrul sesiunii interactive man
pentru navigarea în pagină:
Ctrl+n
/Ctrl+p
sau j
/k
pentru a naviga, cu câte o linie, în jos, respectiv în sus; recomandăm utlizarea tastelor j
/k
pentru a fi mai eficiențiCtrl+f
/Ctrl+b
pentru a naviga, cu câte o pagină de terminal, în jos, respectiv în sus/
, ?
, n
, N
)g
), go down (G
)h
) pentru a afla mai multe despre cum putem folosi mai bine sesiunea interactivăq
) pentru a ieși din sesiunea interactivă
Toate aceste informații se găsesc în pagina de manual a utilitarului less
: man less
.
În sesiunea interactivă căutăm după cuvântul cheie search. Pentru a porni căutarea apăsăm tasta /
, introducem textul căutat (search) și apăsăm tasta Enter
. Apăsăm tasta n
pentru a merge la următoarea apariție a textului căutat; apăsăm n
până când ajungem la implementarea funcției binarysearch2
.
less
, algoritmii de căutare din fișierele workspace/C/searching/linear_search.c
și workspace/C/searching/binary_search.c
. Ce implementare este mai eficientă: binary_search sau linear_search?find
pentru a găsi fișierul sursă care conține algoritmul de sortare quick_sort. Analizați implementarea acestuia folosind utilitarul less
.find
pentru a găsi fișierul sursă care conține algoritmul de sortare merge_sort. Analizați implementarea acestuia folosind utilitarul less
.
Pentru a afișa pe ecran conținutul unui fișier folosim utlitarul cat
. Rulăm comanda de mai jos, pentru a exemplifica:
student@uso:~$ cat workspace/C/searching/binary_search.c /** * @file * @brief Program to perform [binary * search](https://en.wikipedia.org/wiki/Binary_search_algorithm) of a target * value in a given *sorted* array. * @authors [James McDermott](https://github.com/theycallmemac) - recursive * algorithm * @authors [Krishna Vedala](https://github.com/kvedala) - iterative algorithm */ #include <assert.h> #include <stdio.h> [...]
Observăm că pentru un fișier cu un număr mare de linii, așa cum este binary_search.c, afișarea întregului conținut pe ecran devine un impediment în a putea înțelege și urmări conținutul. De aceea vă încurajăm să folosiți less
în loc de cat
pentru a inspecta un fișier: vă este mult mai ușor să vă plimbați în interiorul fișierului și puteți folosi funcția search pentru a căuta în fișier. De asemeni, folosind less
vă păstrați consola curată și puteți urmări mai ușor ce comenzi ați dat anterior și care au fost rezultatele acestora.
Folosim comanda cat
în combinație cu alte comenzi pentru a extrage sau filtra conținutul anumitor fișiere. Comanda cat
primește ca argument calea către unul sau mai multe fișiere și afișează pe ecran conținutul concatenat al acestora.
Un exemplu uzual este faptul că vrem să extragem informațiile despre starea memoriei sistemului din fișierul /proc/meminfo
. Pentru aceasta rulăm comanda de mai jos:
student@uso:~$ cat /proc/meminfo | grep "Mem" MemTotal: 2041248 kB MemFree: 236092 kB MemAvailable: 874420 kB
În exemplul de mai sus folosim cat
pentru a oferi ca intrare conținutul fișierului /proc/meminfo
utilitarului grep
; cu utilitarul grep
filtrăm conținutul după textul "Mem"
. Despre operatorul |
vom vorbi mai jos.
Cu alte cuvinte, outputul comenzii cat /proc/meminfo
, adică conținutul fișierului /proc/meminfo
este textul pe care utilitarul grep
îl prelucrează.
Exercițiu: Plecând de la exemplul de mai sus, extrageți din fișierul /proc/cpuinfo
dimensiunea memoriei cache a procesorului vostru; filtrați conținutul după textul "cache"
.
Am văzut că utilitarul cat
afișează întreg conținutul unui fișier. Există scenarii în care suntem interesați doar de începutul sau sfârșitul unui conținut. Pentru aceste cazuri putem folosi utilitarele:
head
- afișează primele 10 linii din conținuttail
- afișează ultimele 10 linii din conținut
Așa cum am observat în capitolul despre procese, putem folosi utilitarul ps
pentru a vedea care sunt procesele din sistem și ce resurse consumă acestea. Memoria sistemului este una dintre cele mai importante resurse; dacă sistemul nostru rămâne fără memorie disponibilă, tot sistemul este afectat: sistemul se va “mișca” mai greu, procesele se vor “mișca” mai greu sau pot chiar să își întrerupă activitatea. Știind acest lucru, suntem interesați să vedem care sunt primele zece procese care consumă cea mai multă memorie.
Folosim utilitarul ps
pentru a afișa toate procesele din sistem:
student@uso:~$ ps -e -ouser,uid,pid,%mem,%cpu,rss,cmd --sort=%mem USER UID PID %MEM %CPU RSS CMD root 0 2 0.0 0.0 0 [kthreadd] [...] student 1000 8338 3.0 0.0 61860 /usr/lib/evolution/evolution-calendar-factory-subprocess --factory all --bus-name org.gnome.evolution.dataserver.Subprocess.Backend.Calendarx8307x2 --own-path /org/gnome/evolution/dataserver/Subprocess/Backend/Calendar/8307/2 student 1000 8307 3.1 0.0 64628 /usr/lib/evolution/evolution-calendar-factory root 0 1338 3.8 0.0 78880 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock student 1000 7782 3.9 0.0 81312 /usr/lib/xorg/Xorg vt1 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -background none -noreset -keeptty -verbose 3 student 1000 8437 8.4 0.0 171916 /usr/bin/gnome-software --gapplication-service student 1000 7938 18.0 0.1 368304 /usr/bin/gnome-shell
Am folosit opțiunea -ouser,uid,pid,%mem,%cpu,rss,cmd
pentru a selecta coloanele pe care să le afișeze ps
.
Am folosit opțiunea --sort
cu argumentul %mem
pentru a sorta procesele după procentul de memorie folosită.
ps -e -ouser,uid,pid,%mem,%cpu,rss,cmd --sort=%mem | less
pentru a vizualiza rezultatul comenzii ps
într-o sesiune interactivă less
.
Observăm că avem procesele sortate crescător după coloana %MEM
. Folosim utilitarul tail
pentru a extrage din rezultatul ps
cele mai consumatoare zece procese:
student@uso:~$ ps -e -ouser,uid,pid,%mem,%cpu,rss,cmd --sort=%mem | tail student 1000 12966 1.8 0.0 38216 /usr/lib/gnome-terminal/gnome-terminal-server root 0 1074 2.2 0.0 45460 /usr/bin/containerd student 1000 8274 2.3 0.0 48296 nautilus-desktop root 0 336 2.6 0.0 53612 /lib/systemd/systemd-journald student 1000 8338 3.0 0.0 61860 /usr/lib/evolution/evolution-calendar-factory-subprocess --factory all --bus-name org.gnome.evolution.dataserver.Subprocess.Backend.Calendarx8307x2 --own-path /org/gnome/evolution/dataserver/Subprocess/Backend/Calendar/8307/2 student 1000 8307 3.1 0.0 64628 /usr/lib/evolution/evolution-calendar-factory root 0 1338 3.8 0.0 78880 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock student 1000 7782 3.9 0.0 81312 /usr/lib/xorg/Xorg vt1 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -background none -noreset -keeptty -verbose 3 student 1000 8437 8.4 0.0 171916 /usr/bin/gnome-software --gapplication-service student 1000 7938 18.0 0.1 368248 /usr/bin/gnome-shell
În acest moment am găsit răspunsul căutat, dar avem două mici neajunsuri:
Rezolvăm cele două probleme prin intermediul opțiunii --sort
: dacă punem un -
(minus) în fața argumentului după care sortăm, o să sortăm descrescător. Rulăm comanda:
student@uso:~$ ps -e -ouser,uid,pid,%mem,%cpu,rss,cmd --sort=-%mem | less USER UID PID %MEM %CPU RSS CMD student 1000 7938 18.0 0.1 368248 /usr/bin/gnome-shell student 1000 8437 8.4 0.0 171916 /usr/bin/gnome-software --gapplication-service student 1000 7782 3.9 0.0 81312 /usr/lib/xorg/Xorg vt1 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -background none -noreset -keeptty -verbose 3 root 0 1338 3.8 0.0 78880 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock student 1000 8307 3.1 0.0 64628 /usr/lib/evolution/evolution-calendar-factory [...]
Observăm că acum avem formatul dorit. Ne mai rămâne să extragem primele 11 linii din rezultatul comenzii de mai sus; 11 deoarece prima este linia antetului iar următoarele zece sunt procesele de interes. Pentru aceasta utilizăm comanda head
cu opțiunea -11
ca în exemplul de mai jos:
student@uso:~$ ps -e -ouser,uid,pid,%mem,%cpu,rss,cmd --sort=-%mem | head -11 USER UID PID %MEM %CPU RSS CMD student 1000 7938 18.0 0.1 367952 /usr/bin/gnome-shell student 1000 8437 8.4 0.0 171916 /usr/bin/gnome-software --gapplication-service student 1000 7782 3.9 0.0 81312 /usr/lib/xorg/Xorg vt1 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -background none -noreset -keeptty -verbose 3 root 0 1338 3.8 0.0 78880 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock student 1000 8307 3.1 0.0 64628 /usr/lib/evolution/evolution-calendar-factory student 1000 8338 3.0 0.0 61860 /usr/lib/evolution/evolution-calendar-factory-subprocess --factory all --bus-name org.gnome.evolution.dataserver.Subprocess.Backend.Calendarx8307x2 --own-path /org/gnome/evolution/dataserver/Subprocess/Backend/Calendar/8307/2 root 0 336 2.6 0.0 53612 /lib/systemd/systemd-journald student 1000 8274 2.3 0.0 48296 nautilus-desktop root 0 1074 2.2 0.0 45460 /usr/bin/containerd student 1000 12966 1.8 0.0 38216 /usr/lib/gnome-terminal/gnome-terminal-server
Așa cum am văzut până în acest punct, majoritatea comenzilor Linux afișează o gamă largă de informații pe care apoi utilizatorul (adică noi) le filtrează pentru a extrage ceea ce îl interesează. La începutul acestui laborator, dar și de-a lungul materiei, am folosit utilitarul grep
ca să filtrăm rezultatul unei comenzi.
Comanda grep
este una dintre cele mai folosite în linia de comandă. Sintaxa de folosire a grep
este următoarea:
SYNOPSIS grep [OPTIONS] PATTERN [FILE...]
grep
caută PATTERN în lista de fișiere primită ca argument și afișează liniile care conțin PATTERN-ul căutat. Atunci când nu primește nici un fișier, citește text de la tastatură (intrarea standard) și afișează liniile care conțin PATTERN-ul căutat.
Până acum noi am utilizat grep
după modelul de mai jos:
student@uso:~$ cat workspace/C/searching/binary_search.c | grep search * search](https://en.wikipedia.org/wiki/Binary_search_algorithm) of a target * \param[in] arr array to search * \param l left index of search range * \param r right index of search range * \param x target value to search for int binarysearch1(const int *arr, int l, int r, int x) [...]
În exemplul de mai sus, operatorul |
trimite textul afișat de comanda cat
către intrarea standard a comenzii grep
. Vom discuta mai multe despre acesta în secțiunea Înlănțuirea comenzilor în funcție de succes sau eșec
.
Comanda următoare este echivalentă cu cea de mai sus:
student@uso:~$ grep search workspace/C/searching/binary_search.c * search](https://en.wikipedia.org/wiki/Binary_search_algorithm) of a target * \param[in] arr array to search * \param l left index of search range * \param r right index of search range * \param x target value to search for int binarysearch1(const int *arr, int l, int r, int x) [...]
Observăm modul de folosire: grep PATTERN cale/către/fișier
.
binary_search.c
, pentru a vedea unde este folosit parametrul left. Observați cât de multe rezultate irelevante ați găsit datorită faptului că am căutat doar caracterul l. Aici există o lecție de învățat. Numele variabilelor sunt foarte importante: nu fac doar codul mai ușor de înțeles, dar ajută și căutarea. Folosiți patternul “param l” în încercarea de a restrânge căutarea.binary_search.c
.binary_search.c
pentru a vedea cum este apelată funcția de căutare.
Folosim opțiunea -n
pentru a afișa și numărul liniei care conține pattern-ul căutat:
student@uso:~$ grep -n search workspace/C/searching/binary_search.c 4: * search](https://en.wikipedia.org/wiki/Binary_search_algorithm) of a target 14: * \param[in] arr array to search 15: * \param l left index of search range 16: * \param r right index of search range 17: * \param x target value to search for 21:int binarysearch1(const int *arr, int l, int r, int x) [...]
Implicit, grep caută în mod case-sensitive patternul, așa cum putem observa din exemplul de mai jos:
student@uso:~$ grep Search workspace/C/searching/binary_search.c
Pentru a efectua căutarea textului în mod case-insesnsitive, folosim opțiunea -i
, ca în exemplul de mai jos:
student@uso:~$ grep -i Search workspace/C/searching/binary_search.c * search](https://en.wikipedia.org/wiki/Binary_search_algorithm) of a target * \param[in] arr array to search * \param l left index of search range * \param r right index of search range * \param x target value to search for int binarysearch1(const int *arr, int l, int r, int x) [...]
Pentru a afișa toate liniile, mai puțin pe cele care conțin pattern, folosim opțiunea -v
, ca în exemplul de mai jos:
student@uso:~$ grep -v search workspace/C/searching/binary_search.c | less /** * @file * @brief Program to perform [binary * value in a given *sorted* array. * @authors [James McDermott](https://github.com/theycallmemac) - recursive * algorithm * @authors [Krishna Vedala](https://github.com/kvedala) - iterative algorithm */ #include <assert.h> #include <stdio.h> [...]
În căutările noastre de până acum, ca și în exemplele de mai sus, am presupus că știm în ce fișiere se găsește informația căutată de noi. Acest lucru este adevărat pentru fișiere din sistem cu informații bine cunoscute, cum ar fi /proc/meminfo
, dar atunci când lucrăm cu un proiect nou, nu vom ști în ce fișiere să căutăm informația dorită. De exemplu, în cazul proiectului cu algoritmi implementați în C, noi am făcut presupunerea că vom găsi linii care conțin patternul search în fișierul workspace/C/searching/binary_search.c
.
Atunci când nu știm în ce fișiere se află informația căutată, putem să-i spunem lui grep
să caute recursiv prin toată ierarhia de fișiere dintr-un anumit director. Pentru a efectua o căutare recursivă folosim opțiunea -r
, ca în exemplul de mai jos:
student@uso:~$ grep -r search workspace/C/ | less workspace/C/leetcode/src/700.c:struct TreeNode *searchBST(struct TreeNode *root, int val) workspace/C/leetcode/src/700.c: return searchBST(root->left, val); workspace/C/leetcode/src/700.c: return searchBST(root->right, val); workspace/C/leetcode/src/35.c:int searchInsert(int *nums, int numsSize, int target) workspace/C/leetcode/src/35.c:int searchInsert(int *nums, int numsSize, int target) workspace/C/leetcode/src/35.c: return searchInsert(nums, numsSize - 1, target); workspace/C/leetcode/src/704.c:int search(int *nums, int numsSize, int target) workspace/C/leetcode/src/704.c:/* Another solution: Using bsearch() */ workspace/C/leetcode/src/704.c:int search(int *nums, int numsSize, int target) workspace/C/leetcode/src/704.c: int *ret = bsearch(&target, nums, numsSize, sizeof(int), cmpint); workspace/C/leetcode/README.md:|35|[Search Insert Position](https://leetcode.com/problems/search-insert-position/) | [C](./src/35.c)|Easy| workspace/C/leetcode/README.md:|108|[Convert Sorted Array to Binary Search Tree](https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/) | [C](./src/108.c)|Easy| workspace/C/leetcode/README.md:|109|[Convert Sorted List to Binary Search Tree](https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/) | [C](./src/109.c)|Medium| workspace/C/leetcode/README.md:|173|[Binary Search Tree Iterator](https://leetcode.com/problems/binary-search-tree-iterator/) | [C](./src/173.c)|Medium| workspace/C/leetcode/README.md:|700|[Search in a Binary Search Tree](https://leetcode.com/problems/search-in-a-binary-search-tree/) | [C](./src/700.c)|Easy| workspace/C/leetcode/README.md:|701|[Insert into a Binary Search Tree](https://leetcode.com/problems/insert-into-a-binary-search-tree/) | [C](./src/701.c)|Medium| workspace/C/leetcode/README.md:|704|[Binary Search](https://leetcode.com/problems/binary-search/) | [C](./src/704.c)|Easy| workspace/C/DIRECTORY.md: * [Binary Search Tree](https://github.com/TheAlgorithms/C/blob/master/data_structures/binary_trees/binary_search_tree.c) workspace/C/DIRECTORY.md: * [Binary Search](https://github.com/TheAlgorithms/C/blob/master/searching/binary_search.c) workspace/C/DIRECTORY.md: * [Fibonacci Search](https://github.com/TheAlgorithms/C/blob/master/searching/fibonacci_search.c)
De cele mai multe ori vom folosi opțiunile -n
, -i
și -r
în aceelași timp. În cazul nostru de până acum, aceasta se traduce în:
student@uso:~$ grep -nri search workspace/C/ | less workspace/C/leetcode/src/700.c:10:struct TreeNode *searchBST(struct TreeNode *root, int val) workspace/C/leetcode/src/700.c:21: return searchBST(root->left, val); workspace/C/leetcode/src/700.c:25: return searchBST(root->right, val); workspace/C/leetcode/src/35.c:1:int searchInsert(int *nums, int numsSize, int target) workspace/C/leetcode/src/35.c:18:int searchInsert(int *nums, int numsSize, int target) workspace/C/leetcode/src/35.c:27: return searchInsert(nums, numsSize - 1, target); workspace/C/leetcode/src/704.c:1:int search(int *nums, int numsSize, int target) workspace/C/leetcode/src/704.c:23:/* Another solution: Using bsearch() */ workspace/C/leetcode/src/704.c:26:int search(int *nums, int numsSize, int target) workspace/C/leetcode/src/704.c:28: int *ret = bsearch(&target, nums, numsSize, sizeof(int), cmpint); workspace/C/leetcode/README.md:26:|35|[Search Insert Position](https://leetcode.com/problems/search-insert-position/) | [C](./src/35.c)|Easy| workspace/C/leetcode/README.md:35:|108|[Convert Sorted Array to Binary Search Tree](https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/) | [C](./src/108.c)|Easy| workspace/C/leetcode/README.md:36:|109|[Convert Sorted List to Binary Search Tree](https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/) | [C](./src/109.c)|Medium| workspace/C/leetcode/README.md:47:|173|[Binary Search Tree Iterator](https://leetcode.com/problems/binary-search-tree-iterator/) | [C](./src/173.c)|Medium| workspace/C/leetcode/README.md:78:|700|[Search in a Binary Search Tree](https://leetcode.com/problems/search-in-a-binary-search-tree/) | [C](./src/700.c)|Easy| workspace/C/leetcode/README.md:79:|701|[Insert into a Binary Search Tree](https://leetcode.com/problems/insert-into-a-binary-search-tree/) | [C](./src/701.c)|Medium| workspace/C/leetcode/README.md:80:|704|[Binary Search](https://leetcode.com/problems/binary-search/) | [C](./src/704.c)|Easy| workspace/C/.github/pull_request_template.md:20:- [ ] Search previous suggestions before making a new one, as yours may be a duplicate. workspace/C/DIRECTORY.md:31: * [Binary Search Tree](https://github.com/TheAlgorithms/C/blob/master/data_structures/binary_trees/binary_search_tree.c) workspace/C/DIRECTORY.md:338:## Searching :
Astfel avem o căutare cât mai cuprinzătoare și putem folosi funcția de căutare în sesiunea interactivă less
pentru a găsi linia și fișierul care ne interesează.
Din rezultatele căutărilor de mai sus observăm că grep
caută patternul dat ca un subșir. Acest lucru se vede foarte ușor în rezultatul anterior:
student@uso:~$ grep -nri search workspace/C/ | less workspace/C/leetcode/src/700.c:10:struct TreeNode *searchBST(struct TreeNode *root, int val)
Observăm că patternul search se regăsește în șirul *searchBST. Dacă dorim să căutăm cuvântul search folosim opțiunea -w
(word) pentru a-i transmite utilitarului că patternul trebuie tratat ca un cuvânt, ca în exemplul de mai jos:
student@uso:~$ grep -nri -w "search" workspace/C/ | less workspace/C/leetcode/src/704.c:1:int search(int *nums, int numsSize, int target) workspace/C/leetcode/src/704.c:26:int search(int *nums, int numsSize, int target) workspace/C/leetcode/README.md:26:|35|[Search Insert Position](https://leetcode.com/problems/search-insert-position/) | [C](./src/35.c)|Easy| [...]
Observăm că acum rezultatele conțin doar cuvântul search.
stdio.h
.binarySearch
.quickSort
.
Așa cum am menționat în laboratorul Instalarea și utilizarea aplicațiilor, modul în care o aplicație rulează este configurabil. Fișierul de configurare al shellului Bash este ~/.bashrc
. În directorul home al fiecărui utilizator se găsește un fișier .bashrc
, pentru a le permite utilizatorilor să își personalizeze comportamentul shellului lor Bash, fără a intra în conflict cu configurările Bash ale altor utilizatori din sistem. Atunci când un utilizator pornește un shell Bash, conținutul fișierului ~/.bashrc
este citit și sunt aplicate configurările specifice utilizatorului.
Shellul Bash, ca majoritatea programelor, vine cu un set de configurări implicite (default), care pot să nu fie pe placul tuturor utilizatorilor. Prin fișierul .bashrc
utilizatorul poate modifica setul default astfel încât să se potrivească cu stilul său: un exemplu des întâlnit este modificarea dimensiunii istoricului de comenzi.
Pentru a înțelege ce este un alias, rulăm comanda de mai jos:
student@uso:~$ alias ls alias ls='ls --color=auto'
Un alias este un nume (placeholder) care înlocuiește un șir de caractere. Atunci când scriem în terminal numele unei comenzi, dacă numele scris este un alias, numele comenzii va fi înlocuit cu șirul de caractere definit în alias. Cu alte cuvinte, atunci când executăm comanda ls
în shellul Bash, de fapt executăm comanda ls --color=auto
. Opțiunea --color=auto
este cea care ne colorează rezultatul comenzii ls
.
Pentru a vedea toate aliasurile definite în instanța curentă de Bash, folosim comanda alias
, ca în exemplul de mai jos:
student@uso:~$ alias alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"' alias boot-cli='systemctl set-default multi-user.target' alias boot-gui='systemctl set-default graphical.target' alias cal='ncal -M' alias egrep='egrep --color=auto' alias fgrep='fgrep --color=auto' alias gigt='git' alias gpre='grep' alias grep='grep --color=auto' alias grpe='grep' alias gti='git' alias l='ls -CF' alias la='ls -A' alias ll='ls -alF' alias ls='ls --color=auto' alias ncal='ncal -M
Observăm că atât grep
cât și egrep
au câte un alias pentru opțiunea --color
, care în cazul acesta evidențiază expresia găsită. Putem defini un alias și pentru un typo pe care îl facem des, așa cum este cazul pentru gti
, un alias pentru comanda git
, sau grpe
pentru comanda grep
.
O parte din aceste aliasuri sunt definite în fișierul ~/.bashrc
, iar altele în fișierul ~/.bash_aliases
. Conținutul fișierului ~/.bash_aliases
este inclus de către fișierul ~/.bashrc
la pornirea shellului Bash. Astfel, pentru o organizare mai bună, este recomandat ca utilizatorul să-și definească aliasurile în fișierul ~/.bash_aliases
.
Putem observa acest lucru folosind comanda următoare:
student@uso:~$ grep alias ~/.bashrc # enable color support of ls and also add handy aliases alias ls='ls --color=auto' #alias dir='dir --color=auto' #alias vdir='vdir --color=auto' alias grep='grep --color=auto' alias fgrep='fgrep --color=auto' alias egrep='egrep --color=auto' [...] student@uso:~$ cat ~/.bash_aliases alias grep='grep --color=auto' alias grpe='grep' alias gpre='grep' alias gti='git' [...]
Utilitarul xdg-open
primește calea către un fișier și deschide fișierul respectiv cu aplicația asociată tipului de fișier. Astfel, comanda xdg-open image.png
va deschide imaginea image.png cu aplicația asociată deschiderii formatului PNG. Putem să folosim și un URL ca argument al comenzii xdg-open
; astfel, comanda xdg-open https://www.google.com
va deschide pagina Google în browserul vostru implicit.
Ne dorim să definim aliasul go
pentru comanda xdg-open
. Adăugați linia alias go='xdg-open'
în fișierul ~/.bash_aliases
și salvați modificările.
Dacă încercăm să folosim aliasul proaspăt definit, vom primi o eroare similară cu cea de mai jos:
student@uso:~$ go https://www.google.com Command 'go' not found, but can be installed with: sudo snap install go # version 1.18.7, or sudo apt install golang-go # version 2:1.18~0ubuntu2 sudo apt install gccgo-go # version 2:1.18~0ubuntu2 See 'snap info go' for additional versions.
Acest lucru se întâmplă din cauză că fișierul ~/.bashrc
este citit atunci când pornim o instanță de Bash (când deschidem un terminal). Ca să recitim fișierul, și să aplicăm modificările, folosim comanda source
, ca în exemplul de mai jos:
student@uso:~$ source ~/.bashrc student@uso:~$ go https://www.google.com
Comanda source ~/.bashrc
a avut ca efect recitirea și aplicarea modificărilor definite în fișierul .bashrc
și fișierele pe care acesta le include.
Atunci când rulăm o comandă, aceasta își poate încheia execuția în două moduri: cu succes sau cu eșec. Atunci când își încheie execuția, orice proces întoarce un cod de ieșire (exit code), care este un număr:
0
, atunci procesul și-a încheiat execuția cu succes.man
a utilitarului ls
este specificat fiecare cod de eroare și ce înseamnă.
Pentru a vedea codul cu care și-a încheiat execuția o comandă folosim sintaxa $?
. Urmărim exemplul de mai jos:
student@uso:~$ ls Desktop/ todos.txt student@uso:~$ echo $? 0 student@uso:~$ ls non-existent ls: cannot access 'non-existent': No such file or directory student@uso:~$ echo $? 2
Observăm că în cazul fișierului inexistent, comanda ls non-existent
a întors valoarea 2
, așa cum era specificat și în pagina de manual.
De multe ori vrem să executăm o succesiune de comenzi pentru a realiza o sarcină. De exemplu, atunci când vrem să instalăm o aplicație rulăm trei comenzi:
apt update
apt install
Preferăm să înlănțuim cele trei comenzi într-una singură pentru că astfel putem să pornim tot acest proces, să plecăm de la calculator, iar când ne întoarcem să avem tot sistemul pregătit.
Pentru a înlănțui comenzi în shellul Bash avem trei operatori disponibili:
Operatorul ;
- este folosit pentru separarea comenzilor. Urmăm exemplul de mai jos:
student@uso:~$ mkdir demo; cd demo; touch Hello; ls Hello
În exemplul de mai sus am creat directorul demo
, am navigat în interiorul său, am creat fișierul Hello
și am afișat conținutul directorului. Am făcut toate acestea înlănțuind comenzile mkdir
, cd
, touch
și ls
cu ajutorul operatorului ;
.
Operatorul ;
este folosit pentru separarea comenzilor, dar nu ține cont dacă comenzile anterioare au fost executate cu succes sau nu. Urmăm exemplul de mai jos:
student@uso:~$ mkdir operators/demo; cd operators/demo mkdir: cannot create directory ‘operators/demo’: No such file or directory -bash: cd: operators/demo: No such file or directory
În exemplul de mai sus, comanda mkdir
a eșuat deoarece nu a găsit directorul operators
în care să creeze directorul demo
. Cu toate acestea, operatorul ;
doar separă comenzile între ele, așa că și comanda cd operators/demo
a fost executată, și și aceasta a eșuat, deoarece nu există calea operators/demo
.
Folosim operatorul ;
pentru a înlănțui comenzi care sunt independente unele de altele, deci execuția lor nu depinde de succesul unei comenzi precedente.
Operatorul binar &&
(și logic) - execută a doua comandă doar dacă precedenta s-a executat cu succes. Exemplul anterior devine:
student@uso:~$ mkdir operators/demo && cd operators/demo mkdir: cannot create directory ‘operators/demo’: No such file or directory
Observăm că din moment ce comanda mkdir
a eșuat, comanda cd
nu a mai fost executată.
Operatorul binar ||
(sau logic) - execută a doua comandă doar dacă prima s-a terminat cu eșec. Urmărim exemplul de mai jos:
student@uso:~$ (ls -d operators || mkdir operators) && ls -d operators ls: cannot access 'operators': No such file or directory operators student@uso:~$ (ls -d operators || mkdir operators) && ls -d operators operators operators
În exemplul de mai sus, prima comandă ls
a eșuat, așa că a fost executată comanda mkdir
și apoi a fost executată ultima comandă ls
. La cea de-a doua rulare, a fost executată cu succes prima comandă ls
, așa că comanda mkdir
nu a mai fost executată, și apoi a fost executată ultima comandă ls
.
Pentru a rezolva scenariul de la care am plecat inițial, putem rula:
sudo apt update && sudo apt install -y cowsay && cowsay "Howdy"
Comanda de mai sus va actualiza indexul pachetelor sursă, va instala pachetul cowsay
și va rula comanda cowsay
pentru a valida instalarea. O astfel de înlănțuire de comenzi este numită oneliner.
Așa cum am descoperit în secțiunile și capitolele anterioare, în mediul Linux avem multe utilitare care rezolvă o nevoie specifică: ls
afișează informații despre fișiere, ps
despre procese, grep
filtrează, etc. Toate acestea au la bază filozofia mediului Linux: “do one thing and do it well”. Ca întodeauna, frumusețea stă în simplitate: avem o suită de unelte la dispoziție, fiecare capabilă să rezolve rapid o sarcină dată; pentru a rezolva o problemă mai complexă trebuie doar să îmbinăm uneltele.
Operatorul |
(pipe) ne ajută să facem acest lucru. Atunci când folosim operatorul |
preluăm rezultatul comenzii din stânga operatorului și îl oferim ca intrare comenzii aflate în dreapta operatorului.
Am folosit de mai multe ori operatorul |
până acum:
student@uso:~$ ps -e | grep firefox 14912 pts/0 00:00:19 firefox
student@uso:~$ ps -e -ouser,uid,pid,%mem,%cpu,rss,cmd --sort=-%mem | head -11 USER UID PID %MEM %CPU RSS CMD student 1000 7938 18.0 0.1 367952 /usr/bin/gnome-shell student 1000 8437 8.4 0.0 171916 /usr/bin/gnome-software --gapplication-service student 1000 7782 3.9 0.0 81312 /usr/lib/xorg/Xorg vt1 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -background none -noreset -keeptty -verbose 3 root 0 1338 3.8 0.0 78880 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock student 1000 8307 3.1 0.0 64628 /usr/lib/evolution/evolution-calendar-factory student 1000 8338 3.0 0.0 61860 /usr/lib/evolution/evolution-calendar-factory-subprocess --factory all --bus-name org.gnome.evolution.dataserver.Subprocess.Backend.Calendarx8307x2 --own-path /org/gnome/evolution/dataserver/Subprocess/Backend/Calendar/8307/2 root 0 336 2.6 0.0 53612 /lib/systemd/systemd-journald student 1000 8274 2.3 0.0 48296 nautilus-desktop root 0 1074 2.2 0.0 45460 /usr/bin/containerd student 1000 12966 1.8 0.0 38216 /usr/lib/gnome-terminal/gnome-terminal-server
Până acum, am efectuat procesări text pe rezultatul unor comenzi. Folosind operatorul |
și utilitarul xargs
putem să folosim rezultatul pe post de argument pentru altă comandă, ca în exemplul de mai jos:
student@uso:~$ find . -maxdepth 1 -type f | xargs ls -l -rw------- 1 student student 10992 nov 6 14:56 ./.ICEauthority -rw-r--r-- 1 student student 297 nov 7 00:18 ./.bash_aliases -rw------- 1 student student 43604 nov 5 02:34 ./.bash_history -rw-r--r-- 1 student student 220 aug 6 2018 ./.bash_logout -rw-r--r-- 1 student student 3824 aug 13 19:04 ./.bashrc -rw-r--r-- 1 student student 3159 aug 20 2018 ./.emacs -rw-r--r-- 1 student student 87 aug 21 2018 ./.gitconfig -rw------- 1 student student 361 nov 7 02:40 ./.lesshst
Comanda din exemplul de mai sus afișează informații în format lung despre toate fișierele din directorul curent, excluzând directoarele.
Dacă folosim opțiunea -p
a utilitarului xargs
, acesta o să ne afișeze ce comandă urmează să execute și așteaptă confirmarea noastră prin apăsarea tastei y
(yes) sau n
(no). Este recomandat să folosiți opțiunea -p
atunci când vă scrieți onelinerul, pentru a verifica dacă comanda pe care urmează să o executați este corectă. În exemplul următor ne dorim să mutăm toate fisierele .c
în directorul părinte
:
student@uso:~/uso-lab/labs/06-scripting/support/02-one-liners/rename/src$ ls *.c | xargs -p mv .. mv .. casts.c endian.c ptr.c signed-unsigned-representation.c signed-unsigned.c test-hard-link.c use-__thread.c?...
Cu ajutorul opțiunii -p
am putut să observăm că comanda nu are sintaxa dorită și am anulat execuția ei. Problema este că avem destinația (..
) înaintea fișierelor care trebuie mutate, iar destinația pentru mv
trebuie să fie la final.
Pentru a rezolva această problemă folosim opțiunea -I str
, ca mai jos:
student@uso:~/uso-lab/labs/06-scripting/support/02-one-liners/rename/src$ ls *.c | xargs -I str -p mv str .. mv casts.c ..?...n mv endian.c ..?...n mv ptr.c ..?...n mv signed-unsigned-representation.c ..?...n mv signed-unsigned.c ..?...n mv test-hard-link.c ..?...n mv use-__thread.c ..?...n
Opțiunea -I
va înlocui șirul de caractere str
cu numele arhivelor primite din pipe, așa cum observăm mai sus. Șirul de caractere placeholder poate să fie orice, nu neapărat str
; comanda ls *.c | xargs -I {} -p mv {} ..
produce aceelași rezultat.
|
(pipe). Acesta conectează fluxul de ieșire (STDOUT) al comenzii din stânga sa cu fluxul de intrare (STDIN) al comenzii din dreapta.
Majoritatea utilitarelor pe care le folosim afișează rezultatele operațiilor pe care le aplică la ieșirea standard, adică pe ecran. În continuare vom aprofunda ceea ce am discutat despre redirectări în laboratorul Lucrul cu Fișiere. Anterior am mai menționat și termenul de intrare standard; în această secțiune ne vom clarifica ce înseamnă, ce rol îndeplinesc și cum ne folosim de aceste cunoștințe.
Orice proces folosește implicit trei fluxuri (streams) de date:
În linia de comandă, atât STDOUT cât și STDERR vor apărea pe ecran. Datorită faptului că informațiile sunt scrise în două fluxuri distincte, utilizatorul are posibilitatea de a separa rezultatele de erori. Utilizatorul face aceasta folosind redirectări.
Cum spuneam mai sus, majoritatea programelor pe care le folosim vor afișa rezultatele pe ecran. Acest comportament este bun atunci când ne scriem onelinerul care ne extrage informațiile căutate, dar cel mai probabil o să vrem să salvăm rezultatul procesării într-un fișier.
Folosim operatorul >
pentru a redirecta STDOUT sau STDERR într-un fișier. Pentru fiecare flux de date avem un număr, numit descriptor de fișier, asociat:
Pentru a redirecta ieșirea standard folosim sintaxa cmd 1> nume-fișier
. Pentru a redirecta ieșirea standard a erorilor folosim sintaxa cmd 2> nume-fișier
.
>
îl va crea. Dacă fișierul destinație există, operatorul >
va șterge conținutul acestuia.
Urmăm exemplul de mai jos:
student@uso:~$ ps -e -ouser,uid,pid,%mem,%cpu,rss,cmd --sort=-%mem | head -11 USER UID PID %MEM %CPU RSS CMD student 1000 7938 18.0 0.1 367952 /usr/bin/gnome-shell student 1000 8437 8.4 0.0 171916 /usr/bin/gnome-software --gapplication-service student 1000 7782 3.9 0.0 81312 /usr/lib/xorg/Xorg vt1 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -background none -noreset -keeptty -verbose 3 root 0 1338 3.8 0.0 78880 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock student 1000 8307 3.1 0.0 64628 /usr/lib/evolution/evolution-calendar-factory student 1000 8338 3.0 0.0 61860 /usr/lib/evolution/evolution-calendar-factory-subprocess --factory all --bus-name org.gnome.evolution.dataserver.Subprocess.Backend.Calendarx8307x2 --own-path /org/gnome/evolution/dataserver/Subprocess/Backend/Calendar/8307/2 root 0 336 2.6 0.0 53612 /lib/systemd/systemd-journald student 1000 8274 2.3 0.0 48296 nautilus-desktop root 0 1074 2.2 0.0 45460 /usr/bin/containerd student 1000 12966 1.8 0.0 38216 /usr/lib/gnome-terminal/gnome-terminal-server student@uso:~$ ps -e -ouser,uid,pid,%mem,%cpu,rss,cmd --sort=-%mem | head -11 1> top10-consumers student@uso:~$ less top10-consumers
Am scris, prin încercări succesive, onelinerul care ne afișează primele zece procese care consumă cea mai multă memorie. Apoi am folosit sintaxa 1> top10-consumers
pentru a redirecta rezultatul în fișierul top10-consumers.
Urmăm exemplul de mai jos pentru a redirecta erorile:
student@uso:~$ ls D* F* > out 2> errs student@uso:~$ cat out Desktop: Documents: snippets.git Downloads: student@uso:~$ cat errs ls: cannot access 'F*': No such file or directory
Observăm că am folosit sintaxa 2> errs
pentru a redirecta erorile în fișierul errs. Observăm că pentru a redirecta ieșirea standard putem omite descriptorul de fișier, așa cum am făcut cu > out
.
Atunci când rulăm o comandă, redirectăm erorile într-un fișier pentru că vrem să verificăm că totul s-a executat cu succes. De cele mai multe ori suntem în următorul scenariu:
out
și err
.out
și err
pentru a vedea dacă au existat erori și le rezolvăm.
Implicit, operatorul >
șterge (trunchează) conținutul fișierului destinație. Dacă vrem să păstrăm conținutul fișierului și să adăugăm rezultatul redirectării în continuarea acestuia, folosim operatorul >>
.
Exercitiu: Rulați din nou exemplele de mai sus folosind operatorul >>
în locul operatorului >
. Folosiți less pentru a inspecta fișierele de ieșire și de erori.
Motivul pentru care redirectăm erorile într-un fișier este pentru că vrem să analizăm mesajele de eroare. Avem și scenarii în care rulăm un program care afișează mesaje, la STDOUT și STDERR, de care nu suntem interesați.
Un astfel de scenariu întâlnim atunci când pornim browserul firefox
în linia de comandă: acesta afișează din când în când mesaje de care nu suntem interesați. Ne dorim să pornim procesul firefox
în background și să redirectăm STDOUT și STDERR a.î. să nu ne polueze inutil consola. Urmăm exemplul de mai jos:
student@uso:~$ firefox &> firefox-ignore & [1] 10349 student@uso:~$ firefox > firefox-ignore 2>&1 & [2] 10595
Cele două comenzi de mai sus produc aceelași efect: redirectează atât STDOUT, cât și STDERR în fișierul firefox-ignore. Efectul este produs prin două metode diferite:
&> cale/către/nume-fișier
- operatorul &>
va unifica fluxul STDERR cu STDOUT și va redirecta către fișierul primit ca argument.> cale/către/nume-fișier 2>&1
- operatorul 2>&1
folosește descriptori de fișier și redirectează STDERR (descriptorul 2) în STDOUT (descriptorul 1). Această sintaxă trebuie precedată de > cale/către/nume-fișier
, pe care o citim: ceea ce se găsește pe fluxul de ieșire STDOUT va fi scris în fișierul cale/către/nume-fișier.Pe sistemele Linux găsim un număr de fișiere speciale pe care le putem folosi în diferite scopuri:
Fișierul /dev/null
este un fișier care ignoră orice este scris în el. Este echivalentul unei găuri negre în sistemul nostru. Cu ajutorul său, putem rescrie exemplul de mai sus în modul următor:
student@uso:~$ firefox &> /dev/null & [1] 10349 student@uso:~$ firefox > /dev/null 2>&1 & [2] 10595
Acum orice va genera firefox
va fi scris în /dev/null
, care va consuma textul primit fără a ocupa spațiu pe disc.
Fișierul /dev/zero
este un generator de octeți. Acesta generează atâția octeți cu valoarea zero (0)3) cât îi sunt ceruți. Urmăm exemplul:
student@uso:~$ cat /dev/zero | xxd 00000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................ [...] ^C
Deoarece citim din generator, comanda cat
va afișa o infinitate de octeți cu valoarea zero. Utilitarul xxd
afișează în hexazecimal textul primit la STDIN. Trecem rezultatul lui cat
prin xxd
deoarece valoarea 0 nu este un caracter printabil. Cu alte cuvinte nu este un caracter obișnuit, ca cele de pe tastatură, deoarece nu are un echivalent grafic. Folosim Ctrl+c
pentru a opri execția.
Exercițiu: Rulați comanda cat /dev/zero
pentru a înțelege nevoia utilitarului xxd
din exemplul de mai sus.
/dev/urandom
este un alt generator de octeți. Acesta generează atâția octeți cu valoare random cât îi sunt ceruți.
Exercițiu: Rulați comenzile din exemplul anterior, dar acum citiți din /dev/urandom
.
Generatoarele de octeți sunt utile pentru a testa aplicațiile pe care le dezvoltăm. Majoritatea aplicațiilor pe care le vom scrie, ca și cele pe care le utilizăm, citesc și prelucrează informații. Testăm o aplicație pentru că vrem să verificăm că nu avem buguri. Pentru aceasta putem să folosim seturi de date de intrare cât mai variate și mai aleatoare, adică inputuri random. Folosim utilitarul dd
pentru a genera un fișier de 100 MB cu octeți random, ca în exemplul de mai jos:
student@uso:~$ dd if=/dev/urandom of=rand-100mb count=100 bs=1M 100+0 records in 100+0 records out 104857600 bytes (105 MB, 100 MiB) copied, 1,11416 s, 94,1 MB/s student@uso:~$ ls -lh rand-100mb -rw-rw-r-- 1 student student 100M nov 8 17:49 rand-100mb
Am folosit următoarele opțiuni ale utilitarului dd
:
if
- input file - calea către fișierul de unde citimof
- output file - calea către fișierul unde scriembs
- block size - dimensiunea unui block citit din ifcount
- block count - numărul de block-uri citite
Exercițiu: Folosiți fișierul generat și utilitarul tar
pentru a testa diferite metode de compresie a arhivelor. Creați câte o arhivă pentru fiecare din următoarele opțiuni de compresie: Z (compress), z (gzip) și j (bzip2). Comparați dimensiunile arhivelor obținute.
dd
este suprascrierea unui disc cu informații aleatoare. Această metodă este utilizată ca o formă de securitate atunci când vrem să ștergem informații de pe un disc. Astfel suprascriem datele șterse pentru a preveni posibilitatea recuperării datelor de pe disc. Mai multe informații găsiți aici.
Funcția de tab completion este probabil una dintre cele mai utile funcții expuse de către terminal. Prin simpla apăsare a tastei Tab
în timp ce scriem numele unei comenzi, al unei opțiuni a unei comenzi sau calea către un director sau fișier, terminalul va completa în mod automat textul. În cazul în care există mai multe opțiuni pentru auto-complete, prin dubla apăsare a tastei Tab
ne va sugera opțiunile de auto-complete.
În imaginea de mai jos putem observa că pentru comanda cd D
funcția de Tab
completion a găsit mai multe opțiuni valide pentru auto-complete. În astfel de scenarii, cu mai multe opțiuni valide, apăsarea tastei Tab
o singură dată nu produce niciun rezultat; trebuie să apăsăm tasta Tab
de două ori consecutiv pentru a genera afișarea opțiunilor de auto-complete.
student@uso:~$ cd D Desktop/ Documents/ Downloads/ student@uso:~$ cd D
Funcția de auto-complete este extrem de utilă și îmbunătățește semnificativ viteza cu care realizăm acțiuni în terminal.
Funcția este extrem de utilă atunci când lucrăm cu nume de fișiere, directoare și căi din sistem. În loc să scriem manual o cale către un nume foarte lung, lăsăm tasta Tab
să facă asta pentru noi.
Atunci când avem o eroare în comandă (am scris greșit o anumită parte din numele comenzii sau al fișierului, fișierul nu există, etc.), tasta Tab
nu produce nici un rezultat. Acesta este un alt motiv pentru care să folosim tasta Tab
.
Folosiți funcția de Tab
completion cât mai des cu putință4).
Shellul implementează funcția de a reține istoricul comenzilor pe care le-am executat. Pentru a vedea istoricul curent putem rula comanda history
. Vom obține un rezultat asemănător cu cel de mai jos:
student@uso:~$ history [...] 21 ls 22 cd sorting/ 23 ls -l 24 ls 25 cd 26 ls ~/Desktop ~/Documents ~/Downloads 27 ls ~/Desktop 28 ls Desktop/todos.txt 29 cp Desktop/todos.txt cp 30 ls 31 rm cp [...]
Ciclăm prin comenzile date anterior folosind combinația de taste Arrow Up
sau Ctrl+p
, respectiv Arrow Down
sau Ctrl+n
.
Exercițiu: Ciclați prin istoricul de comenzi folosind combinația de taste Ctrl+p
, respectiv Ctrl+n
.
Terminalul ne pune la dispoziție un mod mai inteligent de a căuta în istoricul comenzilor prin combinația de taste Ctrl+r
, ordinea căutării fiind de la cea mai recentă comandă la cea mai veche. Funcția este cunoscută sub numele de history search.
Pentru a porni căutarea, apăsați combinația de taste Ctrl+r
și începeți să scrieți o parte din textul comenzii pe care o căutați, de exemplu cd
.
(reverse-i-search)`cd': cd workspace
De aici, avem următoarele opțiuni:
cd
, folosind combinația de taste Ctrl+r
Ctrl+o
Ctrl+g
Căutarea este incrementală. Adică se rafinează pe măsură ce tastăm un caracter. Orice caracter apăsat rafinează căutarea.
Textul căutat se poate afla oriunde în interiorul comenzii; nu trebuie să fie primele litere din comandă. Pentru exemplul de mai sus, căutarea folosind textul work
ar fi produs același rezultat:
(reverse-i-search)`work': cd workspace
Ne găsim des în situația în care căutăm o comandă în istoric folosind funcția de history search, modificăm un argument al comenzii și apoi o executăm. Pentru navigarea în cadrul textului comenzii putem folosi Arrow Keys
, iar pentru ștergeri putem folosi tasta Backspace
sau Del
.
Terminalul ne pune la dispoziție și o serie de scurtături cu ajutorul cărora putem face realiza aceeași acțiune mai rapid. Dacă vreți să vă impresionați prietenii, acesta este un mod simplu, dar eficient, de a o face.
Pentru a naviga în corpul textului putem folosi combinațiile de taste:
Ctrl+a
- mută cursorul la începutul linieiCtrl+e
- mută cursorul la sfârșitul linieiCtrl+f
- mută cursorul cu un caracter înainteCtrl+b
- mută cursorul cu un caracter înapoiAlt+f
- mută cursorul cu un cuvânt înainteAlt+b
- mută cursorul cu un cuvânt înapoiPentru a efectua ștergeri în corpul textului putem folosi combinațiile de taste:
Ctrl+k
- șterge tot textul de la cursor până la sfârșitul linieiCtrl+u
- șterge tot textul de la cursor până la începutul linieiAlt+d
- șterge tot textul de la cursor până la sfârșitul cuvântului
Ctrl+y
. Funcționalitatea este similară cu procesul de Cut (Ctrl+k
, Ctrl+u
sau Alt+d
) și Paste (Ctrl+y
).
Rulați comanda ls Documents/ Downloads/ Desktop/ Pictures/ Music/
înainte de a vă apuca de exerciții.
Ctrl+p
pentru a accesa comanda rulată anterior.Ctrl+a
.Ctrl+e
.Alt+b
.Alt+f
.ls Docuents/ Downlads/ Dektop/ Pitures/ Muic/
.Ctrl+p
pentru a accesa comanda rulată anterior. Corectați typourile (greșelile de scriere) din comanda anterioară. Folosiți combinațiile de taste Ctrl+f
, Ctrl+b
pentru a deplasa cursorul în cadrul comenzii.Ctrl+p
pentru a accesa comanda rulată anterior (comanda corectată). Avansați până la începutul cuvântului Desktop/
. Ștergeți tot până la final folosind combinația de taste Ctrl+k
. Acum anulați comanda curentă apăsând combinația de taste Ctrl+c
. În acest moment, textul pe care l-ați șters folosind Ctrl+k
(Desktop/ Pictures/ Music/) se află într-un buffer. O să rulați comanda ls
pe textul din buffer. Scrieți comanda ls
și apoi apăsați combinația de taste Ctrl+y
. Textul a fost scris din buffer în continuarea comenzii ls
(scrisă de voi).ls Documents/ Downloads/ Desktop/ Pictures/ Music/
. Apăsați tasta Ctrl+p
pentru a accesa comanda rulată anterior (comanda corectată). Avansați până la începutul cuvântului Pictures/
. Ștergeți cuvântul folosind combinația de taste Alt+d
. Acum anulați comanda curentă apăsând combinația de taste Ctrl+c
. În acest moment, textul pe care l-ați șters folosind Alt+d
(Pictures) se află într-un buffer. O să rulați comanda ls
pe textul din buffer. Scrieți comanda ls
și apoi apăsați combinația de taste Ctrl+y
. Textul a fost scris din buffer în continuarea comenzii ls
(scrisă de voi).Tab
și pt a confirma că respectiva comandă este validă.history
.Arrow Keys
, Ctrl+r
, expandarea comenzilor anterioare sau chiar expandarea argumentelor comenzii anterioare.Ctrl+a
, Ctrl+e
, Alt+f
, Alt+b
), putem efectua modificări (Ctrl+k
, Ctrl+u
, Alt+d
) și putem insera textul șters (Ctrl+y
).Ctrl+n
/Ctrl+p
sau j
/k
pentru a naviga, cu câte o linie, în jos, respectiv în sus; recomandăm utlizarea tastelor j
/k
pentru a fi mai eficienți/
, ?
, n
, N
)g
), go down (G
)h
) pentru a afla mai multe despre cum putem folosi mai bine sesiunea interactivăq
) pentru a ieși din sesiunea interactivăman printf
vs man 3 printf
.tldr
- poate fi util pentru cazurile uzuale, dar nu trebuie să ne fie frică să căutăm în man
pentru detaliiFolosim globbing pentru a selecta mai multe fișiere al căror nume corespunde unui tipar:
*
poate fi înlocuit cu orice caracter de oricâte ori, sau poate lipsi cu totul.?
înlocuiește exact un caracter, oricare ar fi acela.[]
pentru a defini o listă de caractere care pot fi folosite în înlocuire.{}
pentru a defini o listă de cuvinte (grupuri de caractere) care pot fi folosite în înlocuire."
(ghilimele).
Folosim find
pentru a căuta după criterii mai complexe decât numele fișierului, cum ar fi tipul fișierului, data ultimei modificări, etc.
De cele mai multe ori vom folosi find
în conjuncție cu opțiunea -exec
pentru a rula o comandă asupra fișierelor găsite.
Pentru a vedea rapid conținutul unui fișier folosim utlitarul less
. În cadrul unei sesiuni less
putem folosi aceeleași taste ca în cadrul sesiunii interactive man
pentru navigarea în pagină:
Ctrl+n
/Ctrl+p
sau j
/k
pentru a naviga, cu câte o linie, în jos, respectiv în sus; recomandăm utlizarea tastelor j
/k
pentru a fi mai eficiențiCtrl+f
/Ctrl+b
pentru a naviga, cu câte o pagină de terminal, în jos, respectiv în sus/
, ?
, n
, N
)g
), go down (G
)h
) pentru a afla mai multe despre cum putem folosi mai bine sesiunea interactivăq
) pentru a ieși din sesiunea interactivă
Pentru a afișa pe ecran conținutul unui fișier folosim utlitarul cat
.
Pentru a afișa parțial conținutul unui fișier sau a extrage rezultatul unei comenzi folosim utilitarele head
și tail
.
Folosim comanda grep
pentru a căuta un pattern într-un fișier sau în rezultatul unei comenzi (cum ar fi cat
). Modul de folosire este grep PATTERN cale/către/fișier
sau cmd | grep PATTERN
.
Putem folosi funcția de căutare în sesiunea interactivă less
pentru a găsi linia și fișierul care ne interesează.
Folosim opțiunea -n
pentru a afișa și numărul liniei care conține patternul căutat.
Implicit, grep caută în mod case-sensitive patternul. Folosim opțiunea -i
pentru a căuta patternul în mod case-insensitive.
Pentru a afișa toate liniile, mai puțin pe cele care conțin pattern, folosim opțiunea -v
.
Pentru a efectua o căutare recursivă folosim opțiunea -r
.
De cele mai multe ori vom folosi opțiunile -n
, -i
și -r
în același timp.
Extra: Dacă dorim să căutăm cuvântul search folosim opțiunea -w
(word) pentru a trata patternul ca un cuvânt, ca în exemplul următor: grep -nri -w "search" workspace/C/ | less
.
Fișierul de configurare al shellului BASH este ~/.bashrc
. Atunci când un utilizator pornește un shell bash, conținutul fișierului ~/.bashrc
este citit și sunt aplicate configurările specifice utilizatorului.
Un alias este un nume (placeholder) care înlocuiește un șir de caractere. Pentru o organizare mai bună, este recomandat ca utilizatorul să-și definească aliasurile în fișierul ~/.bash_aliases
.
Atunci când își încheie execuția, orice proces întoarce un cod de eroare, care este un număr: valoarea 0
semnifică că acesta și-a încheiat execuția cu succes, iar orice ală valoare indică o eroare.
Pentru a înlănțui comenzi în terminalul bash avem trei operatori disponibili:
;
- este folosit pentru separarea comenzilor, indiferent de cum s-au executat acestea.&&
(și logic) - execută a doua comandă doar dacă precedenta s-a executat cu succes.||
(sau logic) - execută a doua comandă doar dacă prima s-a terminat cu eșec.
Atunci când folosim operatorul |
preluăm rezultatul comenzii din stânga operatorului și îl oferim ca intrare comenzii aflate în dreapta operatorului. Operatorul |
ne permite să prelucrăm datele de interes, trecându-le prin mai multe utilitare, fiecare cu un scop bine definit.
Folosim operatorul >
pentru a redirecta STDOUT sau STDERR într-un fișier. Pentru a redirecta ieșirea standard folosim sintaxa cmd > nume-fișier
. Pentru a redirecta ieșirea standard a erorilor folosim sintaxa cmd 2> nume-fișier
.
Implicit, operatorul >
șterge (trunchează) conținutul fișierului destinație. Dacă vrem să păstrăm conținutul fișierului și să adăugăm rezultatul redirectării în continuarea acestuia, folosim operatorul >>
.
Folosim sintaxa &> cale/către/nume-fișier
pentru a redirecta atât STDERR, cât și STDOUT, în fișierul primit ca argument.
/dev/null
este un fișier care ignoră orice este scris în el./dev/zero
este un generator de octeți. Acesta generează atâția octeți cu valoarea zero (0) cât îi sunt ceruți./dev/urandom
este un alt generator de octeți. Acesta generează atâția octeți cu valoare random cât îi sunt ceruți.-I
în sesiunea interactivă, înainte de a porni căutarea. Dacă doriți să aflați mai multe despre opțiunile pe care le putem introduce apăsați tasta h
într-o sesiune interactivă și căutați textul “OPTIONS”.
?
pentru a porni o căutare de la poziția curentă către începutul paginii. Alternativ, putem naviga la începutul paginii prin apăsarea unei singure taste (g
) și apoi pornim căutarea /
de acolo.
Valoarea 0 nu înseamnă cifra 0. Valoarea 0 înseamnă caracterul (null) din tabelul ASCII. Caracterul 0 are valoarea 48 în tabelul ASCII.
clear
. O alternativă mai rapidă este să folosim combinația de taste Ctrl+l
. Aceasta va produce același rezultat (va curăța ecranul) și are avantajul că poate fi folosită în timp ce scriem deja o comandă.