| suma logiczna (OR, +, |) | a OR 1 = 1 | a OR 0 = a | a OR a = a |
| iloczyn logiczny (AND, *, &) | a AND 1 = a | a AND 0 = 0 | a AND a = a |
| negacja (NOT, ~, ^, !) | NOT 1 = 0 | NOT 0 = 1 | NOT (NOT a) = a |
| łączność | (a OR b) OR c = a OR (b OR c) | (a AND b) AND c = a AND (b AND c) |
| przemienność | a OR b = b OR a | a AND b = b AND a |
| rozdzielność | a AND (b OR c) = (a AND b) OR (a AND c) | a OR (b AND c) = (a OR b) AND (a OR c) |
| absorpcja | a AND (b OR a) = a | a OR (b AND a) = a |
| negacja sumy i iloczynu | NOT (a OR b) = (NOT a) AND (NOT b) | NOT (a AND b) = (NOT a) OR (NOT b) |
| pochłanianie | a OR (NOT a) = 1 | a AND (NOT a) = 0 |
| alternatywa wykluczająca (XOR) | a XOR b = (a AND (NOT b)) OR (b AND (NOT a)) | a XOR 0 = a | a XOR a = 0 |
| a NAND b = NOT (a AND b) | (a NAND b) NAND (c NAND d) = (a AND b) OR (c AND d) | ||
| a NOR b = NOT (a OR b) | (a NOR b) NOR (c NOR d) = (a OR b) AND (c OR d) | ||
| a XNOR b = NOT (a XOR b) | |||
| a | b | a OR b | a AND b | a NOR b | a NAND b | a XOR b | a XNOR b | NOT a |
| 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
| 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | |
| 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
| 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |
a3 a2 a1 a0 reprezentuje liczbę an-1 ... a3 a2 a1 a0 będzie miała wartość 0b101 = 101b reprezentuje liczbę 5 w systemie dziesiętnym (0xc7 = #c7 = c7h reprezentuje liczbę 199 w systemie dziesiątkowym (0xc = 0b1100 oraz 0x7 = 0b0111 to 0xc7 = 0b 1100 0111.
| J | K | Qn+1 | Opis |
| 0 | 0 | Qn | brak zmiany |
| 0 | 1 | 0 | reset |
| 1 | 0 | 1 | set |
| 1 | 1 | NOT Qn | zmiana wyjścia na przeciwny |
clang++ lub g++). Kompilacja C i C++ przebiega kilku etapowo. W pierwszej kolejności wywoływany jest preprocesor, który jest odpowiedzialny za włączanie plików określonych poprzez #include (jest to literalne włączenie zawartości wskazanego pliku w danym miejscu, obsługę rozwijania stałych makr preprocesora (definiowanych z użyciem #define) oraz kompilację warunkową z wykorzystaniem poleceń takich jak #ifdef czy #if. Kompilatory pozwalają na uzyskanie nie tylko wynikowego pliku binarnego, ale także plików po przetworzeniu przez preprocesor czy też po konwersji na assembler.-a (jej podanie zakłada że podawany kod jest tylko częścią ujętą w <?php i ?>, bez tej opcji kod zostanie zinterpretowany i wykonany dopiero po wczytaniu znaku końca pliku, który można wprowadzić Ctrl+D).cc -o plik_wykonywalny plik_zrodlowy.ctcc -o plik_wykonywalny plik_zrodlowy.cgcc -o plik_wykonywalny plik_zrodlowy.cclang -o plik_wykonywalny plik_zrodlowy.c-o polecenia (plik_wykonywalny), który uruchomić można poleceniem: ./plik_wykonywalnycc -o plik_wykonywalny plik_zrodlowy_1.c plik_zrodlowy_2.c plik_zrodlowy_3.c), jednak lepszym (szybszym i dużo częściej stosowanym) podejściem jest rozdzielenie kompilacji poszczególnych plików i konsolidacji całego programu na osobne etapy (korzystając z opcji -c) np.:cc -o plik_wynikowy_1.o -c plik_zrodlowy_1.c cc -o plik_wynikowy_2.o -c plik_zrodlowy_2.c cc -o plik_wynikowy_3.o -c plik_zrodlowy_3.c cc -o plik_wykonywalny plik_wynikowy_1.o plik_wynikowy_2.o plik_wynikowy_3.o
main. Zakończenie tej funkcji oznacza zakończenie programu, a wartość przez nią zwracana odpowiedzialna jest za kod powrotu przekazany procesowi wywołującemu program.#include <stdio.h> /* plik nagłówkowy zawierający deklarację funkcji puts */ int main() { puts("Hello from C"); // wywołanie funkcji puts wypisującej tekst na standardowe wyjście return 0; // kod powrotu ustawiony na zero = sukces }
/* a */ oraz teksty od // do końca linii stanowi komentarz, czyli informacje dla programisty (np. opis działania algorytmu) ignorowane przez kompilatormain jest to int, czyli liczba całkowita), po którym (po spacji) następuje nazwa funkcji, następnie nawiasy okrągłe zawierające listę argumentów (w tym przykładzie puste) a następnie kod funkcji{ i });return kończy działanie funkcji i ustawia wartość zwracaną#include służy do dołączania treści innego pliku, w tym wypadku dołączany jest plik nagłówkowy biblioteki standardowej języka C zawierający deklaracje funkcji związanych z obsługą standardowego wejścia i wyjściaman 3 printf zapoznamy się z dokumentacją funkcji printf.#if 0 oraz #endif)// poniższe polecenie każe zastąpić w kodzie programu każde wystąpienie _NAPIS_ // poprzez "Hello World" #define _NAPIS_ "Hello World" // w podobny sposób możemy definiować pseudo funkcje #define _SUMA_(a,b) (a + b) // poprzedzenie argumentu # powoduje ujęcie go w "" #define _WYPISZ_(a) printf(#a) // ## powoduje sklejenie ... // ze względu na kontrolę błędów na etapie kompilacji // zaleca się ograniczanie #define (w powyższych zastosowaniach) // na rzecz stałych i funkcji inline // możemy też warunkować włączanie fragmentów kodu #define PL #ifdef PL // ten fragment wykona się tylko gdy zdefiniowana PL #undef _NAPIS_ // oddefiniowalismy _NAPIS_ #define _NAPIS_ "Witaj World" // i zdefiniowaliśmy go inaczej #endif #if 0 to jest trzeci rodzaj komentarza w C #endif #include <stdio.h> int main() { printf("%s\n", _NAPIS_); printf("%d\n", _SUMA_(5,3)); _WYPISZ_ (witaj świecie !!! \n); }
./nazwa_pliku pod warunkiem że ma prawo wykonalności (powinien także zawierać w pierwszą linii komentarz określający program używany do interpretacji tekstowego pliku wykonywalnego, w postaci: #!/usr/bin/python3 lub #!/usr/bin/env python3). Może też być wykonany za pomocą wywołania: python3 nazwa_pliku.python3 -i nazwa_pliku (opcja -i_ pozwala na uzyskanie interpretera w stanie po wykonaniu poleceń z pliku nazwa_pliku.sys.argv.#!/usr/bin/env python3 import sys print("Hello World") print(sys.argv) exit(0)
# do końca linii stanowi komentarz, czyli informacje dla programisty (np. opis działania algorytmu) ignorowane przez kompilator! jest komentarzem sterującym określającym program który ma być użyty do interpretacji kodu z pliku tekstowegoimport służy do zaimportowania modułu pythonowego (są nimi fragmenty biblioteki standardowej oraz innych bibliotek także tworzonych przez nas), w tym przypadku importowany jest fragment biblioteki standardowejprint wypisuje tekst określony w argumentach na standardowe wyjścieexit kończy działanie skryptu ustawiając podaną w argumencie wartość jako kod powrotu, stosowanie jej na końcu skryptu (jak w tym przykładzie) nie jest obowiązkowehelp od funkcji, typu, etc z dokumentacją którego chcemy się zapoznać, np. help(print) wypisze dokumentację funkcji print, help(sys) (po import sys) wypisze dokumentację modułu sys.auto w C++).#include <stdio.h> int main() { // liczba całkowita ze znakiem int calkowita = -34; // liczba rzeczywista (pojedynczej precyzji) float zmiennoprzecinkowa = 673.1357; // 8 bitowa liczba całkowita bez znaku uint8_t bajt = 0xf3; printf("%d %x %d %.2f\n", bajt, bajt, calkowita, zmiennoprzecinkowa); // zmienna napisowa "C NULL-end string" char* napisA = "q we"; // zmienna napisowa typu "C++ string" std::string napisB = "a bc"; }
# dynamiczne typowanie - typ określany jest # na podstawie wartości zapisywanej do zmiennej zmiennaA = -91.7 zmiennaB = "qa z"
# dynamiczne typowanie # typ określany jest na podstawie wartości znajdującej się w zmiennej # # zasadniczo wszystkie zmienne są napisami, a interpretacja # ma miejsce przy ich użyciu a nie przy tworzeniu # obsługiwane liczby całkowite oraz napisy # brak obsługi liczb zmiennoprzecinkowych zmiennaA=-91 zmiennaB="qa z" zmiennaC=98.6234 # to będzie traktowane jako napis a nie liczba # Brak spacji pomiędzy nazwą zmiennej a znakiem równości jest wymogiem składniowym. # Wynika to ze znaczenia spacji w składni powłoki. # Spacja oddziela nazwy poleceń i argumenty od siebie, czyli pełni istotną funkcję # składniową, odpowiadającą nawiasom okrągłym i przecinkom, używanym do oddzielania # nazwy funkcji i argumentów od siebie, z wielu innych języków programowania. # Odwołanie do zmiennej odbywa się z użyciem znaku dolara ($), po którym występuje # nazwa zmiennej. Nazwa może być ujęta w klamry. Rozwijaniu ulegają nazwy zmiennych # znajdujące się w napisach umieszczonych w podwójnych cudzysłowach. # Umieszczenie odwołania do zmiennej w cudzysłowach zabezpiecza białe znaki # (spacje nowe linie) przy przekazywaniu do funkcji i programów (w tym przy # przekazywaniu do echo, celem wypisywania). # Użycie apostrofów wyłącza interpretację dolara jako # znaku specjalnego (odwołania do zmiennej) echo $zmiennaA ${zmiennaA}AA echo "$zmiennaA ${zmiennaA}AA" echo '$zmiennaA ${zmiennaA}AA' # # Zmienna nie zdefiniowana # # Odwołaniu do nie zdefiniowanej zmiennej nie zgłaszane jako błąd, # taka zmienna ma wartość napisu pustego. echo "AAA $niezdefiniowana BBB" # Należy jednak pamiętać że taki napis pusty będzie inaczej traktowany gdy znajduje się # wewnątrz cudzysłowów a inaczej gdy nie (wtedy jest pomijany jako argument poleceń). printf "> %s < %s\n" $niezdefiniowana BBB printf "> %s < %s\n" "$niezdefiniowana" BBB # Została tu użyta komenda `printf` będące odpowiednikiem funkcji języka C o tej samej nazwie: printf "%.2f\n" $zmiennaC # # Zmienne środowiskowe # # Jeżeli chcemy aby zmienna była widoczna przez programy # uruchamiane z naszej powłoki należy ją wyeksportować za # pomocą polecenia: export zmiennaA # Do polecenia przekazujemy nazwę zmiennej a nie jej wartość, # więc nie używamy znaku dolara. # Taka zmienna jest dostępna jako zmienna środowiskowa # dla wszystkich potomków tej powłoki. # Zmienne środowiskowe mogą być także ustawiane bez użycia # `export` dla pojedynczego nowego programu poprzez podanie # ich nazw i wartości przed nazwą polecenia: ABCD=678 bash -c 'echo $ABCD' ABCD=135 EFG=098 bash -c 'echo $ABCD $EFG' echo $ABCD # Są one jedynie widoczne w tak uruchomionym procesie potomnym # (nie mogą być użyte w bieżącej powłoce, czy jako jako argumenty w linii poleceń). # Dlatego w powyższych przykładach wywoływana jest nowa powłoka, # która korzysta z tak ustawionych zmiennych. # Takie ustawianie zmiennych jest szczególnie przydatne gdy # chcemy uruchomić pojedyncze polecenie w zmienionym # środowisku - np. polecenie date w innej strefie czasowej: TZ=America/New_York date
<?php # dynamiczne typowanie - typ określany jest # na podstawie wartości zapisywanej do zmiennej $zmiennaA = -91.7 $zmiennaB = "qa z" ?>
var a = 13.3 var b = "qa z"
#include <stdio.h> int main() { double a = 12.7, b = 3, c, d, e; int x = 5, y = 6, z; // dodawanie, mnożenie, odejmowanie zapisuje się // i działają one tak jak w normalnej matematyce: e = (a + b) * 4 - y; // dzielenie zależy od typów argumentów d = a / b; // będzie dzieleniem zmiennoprzecinkowym bo a i b są typu float c = x / y; // będzie dzieleniem całkowitym bo z i y są zmiennymi typu int b = (int)a / (int)b; // będzie dzieleniem całkowitym a = (double)x / (double)y; // będzie dzieleniem zmiennoprzecinkowym // reszta z dzielenia (tylko dla argumentów całkowitych) z = x % y; // wypisanie wyników printf("%d %f %f %f %f %f\n", z, e, d, c, b, a); // operacje logiczne: // ((a większe równe od 0) AND (b mniejsze od 2)) OR (z równe 5) z = (a>=0 && b<2) || z == 5; // negacja logiczna z x = !z; printf("%d %d\n", z, x); // operacje binarne: // bitowy OR 0x0f z 0x11 i przesunięcie wyniku o 1 w lewo x = (0x0f | 0x11) << 1; // bitowy XOR 0x0f z 0x11 y = (0x0f ^ 0x11); // negacja bitowa wyniku bitowego AND 0xfff i 0x0f0 z = ~(0xfff & 0x0f0); printf("%x %x %x\n", x, y, z); // uwaga: powyższy program może nie wykonywać obliczeń w czasie działania // ze względu na optymalizację i fakt iż wyniki wszystkich operacji // są znane w momencie kompilacji programu }
+ np. int a = 3 + 2; (zmienna a będzie miała wartość 5)- np. int a = 3 - 2; (zmienna a będzie miała wartość 1)* np. int a = 3 * 2; (zmienna a będzie miała wartość 6)- przed nazwą zmiennej celem odwrócenia jej znaku (pomnożenia przez -1), np. int b = -a;/ np. int a = 3 / 2; float b = 3 / 2; float c = 3.0 / 2;a i zmienna b będą miały wartość 1 (dzielenie całkowite)c będzie miało 1.5 (dzielenie zmiennoprzecinkowe, bo jeden z argumentów jest zmiennoprzecinkowy)% np. int a = 3 % 2; (zmienna a będzie miała wartość 1)( i ), np. int a = 3 + 2 * 2 ustawi wartość zmiennej a na 7, natomiast int a = (3 + 2) * 2 ustawi wartość zmiennej a na 10.a = 12.7 b = 3 x = 5 y = 6 # dodawanie, mnożenie, odejmowanie zapisuje się i działają # tak jak w normalnej matematyce: e = (a + b) * 4 - y # dzielenie zmiennoprzecinkowe c = x / y # dzielenie całkowite b = a // b # reszta z dzielenia z = x % y; # wypisanie wyników print(e, c, b, z) # operacje logiczne: # ((a większe równe od 0) AND (b mniejsze od 2)) OR (z równe 5) z = (a>=0 and b<2) or z == 5; # negacja logiczna z x = not z; print(z, x); # operacje binarne: # bitowy OR 0x0f z 0x11 i przesunięcie wyniku o 1 w lewo x = (0x0f | 0x11) << 1; # bitowy XOR 0x0f z 0x11 y = (0x0f ^ 0x11); # negacja bitowa wyniku bitowego AND 0xfff i 0x0f0 z = ~(0xfff & 0x0f0); print(hex(x), hex(y), hex(z & 0xffff)); # wypisując z musimy określić jego bitowość # wieloargumentowa operacja przypisania a, b, c = 1, 2, 3 # Najpierw oblicza wartości wyrażeń po prawej, a dopiero potem przypisuje. # Pozwala to np. na: a, b = b, a # celem zamiany wartości zmiennych bez jawnego używania zmiennej tymczasowej print(a, b)
a=12; b=3; x=5; y=6 # aby wykonać działania arytmetyczne należy umieścić je wewnątrz $(( i )) # dodawanie, mnożenie, odejmowanie zapisuje się i działają # tak jak w normalnej matematyce: e=$(( ($a + $b) * 4 - $y )) # dzielenie całkowite c=$(( $x / $y )) # wypisanie wyników echo $e $c # Do operacji arytmetycznych może być też jest wykorzystywane polecenie let. # Najczęściej jest stosowane do inkrementacji podanej zmiennej, np: echo $a let a++ echo $a # Zarówno operator podwójnych nawiasów okrągłych jak i komenda `let` # mogą obsługiwać wyrażenia logiczne. Mimo to operacje logiczne # najczęściej obsługiwane są komendą `test` lub operatorem `[ ]`, # w których wynik zwracany jest jako kod powrotu. # Należy zwrócić uwagę na escapowanie odwrotnym ukośnikiem # nawiasów i na to że spacje mają znaczenie. # [ jest w istocie komendą, działającą jak test, # tyle że wymaga ] jako ostatniego argumentu. # ((a większe równe od zera) AND (b mniejsze od dwóch)) OR (c równe 5) [ \( $a -ge 0 -a $b -lt 2 \) -o $c -eq 5 ]; z=$? # negację realizuje !, ale wynikiem negacją dowolnej liczby jest FALSE # więc nie da się zanegować z jak w pozostałych przykładach echo $z # bash stosuje logikę odwróconą 0 == TRUE, coś nie zerowego to FALSE # # wykonywanie innych programów # # Jako operacje podstawowe powinniśmy patrzyć także na wykonanie innych # programów i pobieranie ich standardowego wyjścia i/lub kodu powrotu. # Pobieranie standardowego wyjścia możemy realizować za pomocą ujęcia # polecenia w backquotes (`) lub operatora $( ) (pozwala on na # zagnieżdżanie takich operacji). # Natomiast kod powrotu ostatniej komendy znajduje się w zmiennej $? # (używaliśmy tego już przy obliczaniu wyrażeń logicznych). a=`cat /etc/issuse` b=$(cat /etc/issuse; cat /etc/resolv.conf) echo $a echo $b echo "$b" # Zwróć uwagę na różnicę w wypisaniu zmiennej zawierającej znaki nowej # linii objętej cudzysłowami i nie objętej nimi. # Bash nie obsługuje liczb zmiennoprzecinkowych ani operacji bitowych # nieobsługiwane operacje można wykonać za pomocą innego programu np: a=`echo 'print(3/2)' | python3` b=$(echo '3/2' | bc -l) echo $a $b
var a = 12.7, b = 3, c = 13, d, e; // dodawanie, mnożenie, odejmowanie zapisuje się // i działają one tak jak w normalnej matematyce: e = (a + b) * 4 - c; // dzielenie zawsze jest zmiennoprzecinkowe d = a / b; c = b / c; // wypisanie wyników console.log( "e=" + e + "\n" + "d=" + d + "\n" + "c=" + c + "\n" ); // operacje logiczne: // ((a większe równe od 0) AND (b mniejsze od 2)) OR (c równe 5) var z = (a>=0 && b<2) || c == 5; // negacja logiczna z a = !z; console.log( "z=" + z + "\n" + "a=" + a + "\n" ); // operacje binarne: // bitowy OR 0x0f z 0x11 i przesunięcie wyniku o 1 w lewo a = (0x0f | 0x11) << 1; // bitowy XOR 0x0f z 0x11 b = (0x0f ^ 0x11); // negacja bitowa wyniku bitowego AND 0xfff i 0x0f0 c = ~(0xfff & 0x0f0); console.log( "a=0x" + a.toString(16) + "\n" + "b=0x" + b.toString(16) + "\n" + "c=0x" + (c & 0xffff).toString(16) + "\n" );
goto (realizująca skok bezwarunkowy) jest pełnoprawną instrukcją skoku, jedyną wadą jej stosowania jest to że przy niewłaściwym / zbyt częstym wykorzystywaniu (zamiast wywołań funkcji, warunków i pętli) kod programu staje się mniej czytelny.#include <stdio.h> #include <setjmp.h> // potrzebne dla długich skoków jmp_buf long_jump_buffer; void jakas_funkcja(); int main() { int i, j, k; // instrukcja waunkowa if - else if (i<j) { puts("i<j"); } else if (j<k) { puts("i>=j AND j<k"); } else { puts("i>=j AND j>=k"); } // podstawowe operatory logiczne if (i<j || j<k) puts("i<j OR j<k"); // innymi operatorami logicznymi są && (AND), ! (NOT) // pętla for for (i=2; i<=9; ++i) { if (i==3) { // pominięcie tego kroku pętli continue; } if (i==7) { // wyjście z pętli break; } printf(" a: %d\n", i); } // pętla while while (i>0) { printf(" b: %d\n", --i); } // pętla do - while do { printf(" c: %d\n", ++i); } while (i<2); // instrukcja wyboru switch switch(i) { case 1: puts("i==1"); break; default: puts("i!=1"); break; } // // Niskopoziomowe mechanizmy skoków // // Instrukcje `goto` i funkcje `setjmp`/`longjmp` służą do bezpośredniego // zarządzania przepływem sterowania w programie, ale zazwyczaj unika się // tych mechanizmów, ponieważ mogą zmniejszać czytelność kodu. goto ETYKIETA; puts("to się nigdy nie wykona"); puts("bo wcześniej robimy bezwarunkowe goto"); ETYKIETA: puts("a to się wykona"); // Najczęstszym przypadkiem użycia `goto` jest obsługa błędów w złożonych funkcjach // (tzw. "cleanup" na końcu funkcji). Skok `goto` do kodu wykonujące sprzątanie // i wyjście z funkcji używany jest zamiast return // w warunkach sprawdzających wystąpienie błędu. // Długi skok realizowany przez funkcje `setjmp` i `longjmp` pozwala na wykonywanie // skoków pomiędzy różnymi funkcjami. // Funkcja `setjmp` zapisuje kontekst programu (m.in. stan stosu) i zwraca zero, // a `longjmp` pozwala do niego wrócić (w tym przypadku `setjmp` zwróci wartość // określoną w argumencie `longjmp`). int x = setjmp(long_jump_buffer); if (x == 0) { // setjump zapisało kontekst programu jakas_funkcja(); // wywołanie funkcji } else { // wróciliśmy do miejsca wywołania `setjump` na skutek wywołania `longjmp` printf("Powrót z longjmp, longjmp zostało wywołane z argumentem = %d\n", x); } } void jakas_funkcja() { longjmp(long_jump_buffer, 3); // skok z powrotem do miejsca wywołania setjmp puts("to się nigdy nie wykona"); }
i, k, j = 0, 0, 0 # instrukcja warunkowa if - else if i<j : print("i<j") elif j<k : print("i>=j AND j<k") else: print("i>=j AND j>=k") ''' Uwaga: * Bloki kodu w Pythonie rozpoczynają się dwukropkiem i wydzielane są z użyciem wcięć. * Blok kodu trwa do momentu zmniejszenia wcięcia do poziomu na którym była instrukcja go rozpoczynająca. * Zagnieżdżony blok kodu wiąże się z zwiększeniem wcięcia. * Wcięcia mogą być wykonywane spacjami albo tabulatorami (ale nie należy ich mieszać). ''' # podstawowe operatory logiczne if i<j or j<k: print("i<j OR j<k") # innymi operatorami logicznymi są `and` oraz `not` # pętla for for i in range(2, 9): if i==3: # pominięcie tego kroku pętli continue; if i==7: # wyjście z pętli break; print(" a:", i) else: # nie wejdzie tutaj bo pętla kończy # się z użyciem break print("else w pętli") # pętla while while i>0 : i = i - 1; print(" b:", i) else: # wejdzie tu gdy warunek i>0 # nie będzie spełniony # zarówno przy pierwszym # jak i kolejnych sprawdzeniach print("else w pętli")
# pętla for for (( i=0 ; $i<=20 ; i++ )) ; do echo $i; done # w bardziej "shellowym" stylu: for i in `seq 0 20`; do echo $i; done # pętla for po liście rozdzielanej spacjami # wypisanie nazw wszystkich plików z /tmp for nazwa in /tmp/* ; do echo $nazwa; done # pętla while z read (po liniach pliku) cat /etc/fstab | while read slowo reszta; do echo $reszta; done # powyższa pętla wypisze po kolei wszystkie # wiersze pliku przekazanego przez stdin # (cat nazwa_pliku |) z pominięciem # pierwszego słowa (które wczytywane było do # zmiennej slowo) # domyślnym separatorem pól dla komendy read # jest dowolny ciąg białych znaków (spacji i # tabulatorów) można go zmienić przy pomocy # zmiennej IFS: IFS=: while read a b c d; do echo $c done < /etc/passwd unset IFS # Należy mieć na uwadze, że w konstrukcjach typu # while read, pętla while uruchamiana może być w # procesie potomnym obecnej powłoki. # Efektem tego jest iż w niektórych przypadkach # wykonywane modyfikacje zmiennych wewnątrz # takiej pętli nie będą widoczne poza nią. # instruikcja if - else if [ "$xx" = "kot" -o "$xx" = "pies" ]; then echo "kot lub pies"; elif [ "$xx" = "ryba" ]; then echo "ryba" else echo "coś innego" fi # spacje wokół i wewnątrz nawiasów kwadratowych # przy warunku są istotne składniowo, zawartość # nawiasów kwadratowych to tak naprawdę # argumenty dla komendy test, zatem wywołanie: # if [ "$xx" = "ryba" ]; then # jest równoważne: # if test "$xx" = "ryba"; then # a więcej na temat warunków można znaleźć w: # man test # jako warunek może wystąpić dowolne polecenie # wtedy sprawdzany jest jego kod powrotu # 0 oznacza prawdę / zakończenie sukcesem # wartość nie zerowa fałsz / błąd if grep '^root:' /etc/passwd > /dev/null; then echo /etc/passwd zawiera użytkownika root; fi # istnieje możliwość skróconego zapisu warunków # z użyciem łączenia instrukcji przy pomocy: # && wykonaj instrukcję występująca po prawej # gdy poprzednia zwróciła zero (true) # || (wykonaj instrukcję występująca po prawej # gdy poprzednia zwróciła nie zero -- false [ "$xx" = "ryba" ] && echo '$xx = to ryba' grep '^root:' /etc/passwd > /dev/null && \ echo /etc/passwd zawiera użytkownika root; # instrukcja case # (w odróżnieniu od switch z C obsługuje napisy) case $xx in kot | pies) echo "kot lub pies" ;; ryba) echo "ryba" ;; *) echo "cos innego" ;; esac
<?php $i=$j=$k=0; // instrukcja waunkowa if - else if ($i<$j) { echo("i<j\n"); } else if ($j<$k) { echo("i>=j AND j<k\n"); } else { echo("i>=j AND j>=k\n"); } // pętla for for ($i=2; $i<7; ++$i) { if ($i==$j) { // pominięcie tego kroku pętli continue; } printf(" a: %d\n", $i); } // pętla while while ($i>0) { if ($i==$k) { // wyjście z pętli break; } printf(" b: %d\n", --$i); } // pętla do - while do { printf(" c: %d\n", ++$i); } while ($i<2); // instrukcja wyboru switch switch($i) { case 1: echo("i==1\n"); break; default: echo("i!=1\n"); break; } ?>
var i, j, k; // instrukcja waunkowa if - else if (i<j) { console.log("i<j\n"); } else if (j<k) { console.log("i>=j AND j<k\n"); } else { console.log("i>=j AND j>=k\n"); } // podstawowe operatory logiczne if (i<j || j<k) console.log("i<j OR j<k\n"); // innymi operatorami logicznymi są && (AND), ! (NOT) // pętla for for (i=2; i<=9; ++i) { if (i==3) { // pominięcie tego kroku pętli continue; } if (i==7) { // wyjście z pętli break; } console.log(" a: ", + i + "\n"); } // pętla while while (i>0) { console.log(" b: " + --i + "\n"); } // pętla do - while do { console.log(" c: " + ++i + "\n"); } while (i<2); // instrukcja wyboru switch switch(i) { case 1: console.log("i==1\n"); break; default: console.log("i!=1\n"); break; }
#include <stdio.h> #include <stdarg.h> // potrzebne dla obsługi dowolnej ilości argumentów // funkcja bezargumentowa niezwracająca wartości void f1() { puts("ABC"); } // funkcja dwuargumentowa zwracająca wartość int f2(int a, int b) { return a*2.5 + b; } // funkcja z jednym argumentem obowiązkowym i jednym opcjonalnym float f3(int a, int b=1) { puts("F3"); return a*2.5 + b; } // funkcja z dwoma argumentami wymaganymi i dowolną ilością argumentów opcjonalnych float f4(int a, int b, ...) { float ret; va_list vl; va_start(vl, b); // w tym miejscu potrzebujemy znać ilość oraz typy argumentów for (int i=0; i<a; i++) { ret += b * va_arg(vl,double); } va_end(vl); return ret; } int main() { f1(); int a = f2(3, 6); // zwracaną wartość można wykorzystać (jak wyżej) lub zignorować: f3(0); float b = f4(2, 1, 2.8, 3.5); printf("%d %f\n", a , b); }
# funkcja bezargumentowa, zwracająca wartość def f1(): print("AA") return 5 a = f1() print(a) # funkcja przyjmująca jeden obowiązkowy argument oraz dwa opcjonalne def f2(a, b=2, c=0): print(a**b+c) f2(3) f2(3, 3) # można pominąć dowolne z argumentów z wartością # domyślną odwołując się do pozostałych nazwami f2(2, c=1) # można podawać argumenty w dowolnej kolejności # odwołując się do nich nazwami f2(b=3, a=2) # nieokreślona ilość argumentów pozycyjnych def f(*a): for aa in a: print(aa) f(1, "y", 6) # ale nie: f(1, "y", u="p") # nieokreślona ilość argumentów nazwanych def f(**a): for aa in a: print(aa, "=", a[aa]) f(a="y", u="p") # ale nie: f(1, u="p") # nieokreślona ilość argumentów pozycyjnych i nazwanych def f(*a1, **a2): print(a1) print(a2) f(1, "y", 6) f(a="y", u="p") f(1, "y", u="p") # można też wymusić ileś argumentów jawnych def f(x, *a1, y="8", **a2): print(x, y) print(a1) print(a2) f(1, "y", 6) f(1, "y", u="p") f(1, "z", y="y", u="p") # ale nie: f(a="y", u="p")
# w bashu każda funkcja może przyjmować # dowolną ilość parametrów pozycyjnych # (w identyczny sposób obsługiwane są # argumenty linii poleceń dla całego skryptu) f1() { echo "wywołano z $# parametrami" echo "parametry to: $@" [ $# -lt 2 ] && return; # można odwoływać się do pojedynczych parametrów echo "drugi: $2" echo "pierwszy: $1" # albo kolejnych w pętli for a in "$@"; do echo $a; done # lub z użyciem polecenia shift for i in `seq 1 $#`; do echo $1 shift # powoduje zapomnienie $1 # i przenumerowanie pozostałych # argumentów pozycyjnych o 1 # wpływa na wartości $@ $# itp done # funkcja może zwracać tylko wartość numeryczną # tzw kod powrotu return 83 } # wywołanie - tak jak komendy, czyli bez nawiasów, # a argumenty rozdzielane białym znakiem (spacją) f1 aaa 3 t 56 # kod powrotu ostatnio wywołanej komendy lub funkcji # uzyskuje się poprzez $?: echo $? # często funkcje (tak jak wiele komend) wynik swojego # działania zwracają poprzez standardowe wyjście f2() { echo "Uwolnić mrożone truskawki$1"; } # pozyskać go można poprzez `` lub $() # zapis z użyciem $() może być zagnieżdżony a=`f2 '!!!'` echo $a b=$(f2 '!') echo $b # w jednolinijkowym zapisie definicji funkcji (jak # miało to miejsce dla f2) spacje po { i przed } są # obowiązkowe, podobnie jak średniki po instukcjach
<?php # funkcja bezargumentowa, zwracająca wartość function f1() { print("AA\n"); return 5; } $a = f1(); print($a); print("\n"); # funkcja przyjmująca jeden obowiązkowy # argument oraz dwa opcjonalne function f2($a, $b=2, $c=0) { print($a**$b+$c); print("\n"); } f2(3); f2(3, 3); # nieokreślona ilość argumentów pozycyjnych function f3() { $num = func_num_args(); print("ilość argumentów:\n"); print( $num ); print("\n"); for($i=0; $i<$num; ++$i) { print("argument $i: "); print(func_get_arg($i)); print("\n"); } } f3(); f3("a", 1, "c", 5); # jeden wymagany, jeden opcjonalny # i nieokreślona ilość dodatkowych function f4($a, $b="16") { # tablica z wszystkimi argumentami # jest alternatywną metodą dostępu # do wszystkich argumentów w stosunku # co do pokazanej w f3() $args = func_get_args(); print_r($args); } f4("a", 1, "c", 5); ?>
function f1() { console.log("ABC\n"); } // funkcja dwuargumentowa zwracająca wartość function f2(a, b) { return a*2.5 + b; } // funkcja z jednym argumentem obowiązkowym // i jednym opcjonalnym function f3(a, b=1) { console.log("F3\n"); return a*2.5 + b; } // funkcja z dowolną ilością argumentów function f4() { var ret = 0; for(var i=0; i<arguments.length; i++) { ret += arguments[i]; } return ret; } f1(); // zwracaną wartość można wykorzystać: var a = f2(3, 6); // lub zignorować: f3(0); var b = f4(2, 1, 2.8, 3.5); console.log("a=" + a +" b=" + b + "\n");
#include <iostream> int main() { int x = 10; // lambda bez argumentów i listy przechwytywania auto lambda_function1 = []() { std::cout << "Hello\n"; }; lambda_function1(); // lambda z argumentami auto lambda_function2 = [](int y) { std::cout << "y = " << y << "\n"; }; lambda_function2(11); // lambda z listą przechwytywania (przez wartość) // x jest zamrożone jako 10 auto lambda_function3 = [x](int y) { return x + y; }; std::cout << lambda_function3(3) << "\n"; x = 0; std::cout << lambda_function3(3) << "\n"; // lambda z listą przechwytywania (przez referencje) // odwołujemy się do zewnętrznego x auto lambda_function4 = [&x](int y) { return x + y; }; std::cout << lambda_function4(3) << "\n"; x = 10; std::cout << lambda_function4(3) << "\n"; // w C++ lambda może korzystać tylko ze zmiennych określonych // w jej listach przechwytywania i argumentów, czyli: // auto lambda_function5 = [](int y) { return x + y; }; // nie będzie poprawne }
# Python pozwala na definiowanie funkcji wewnątrz funkcji oraz łatwe przechowywanie # funkcji w zmiennych, także kolekcjach takich jak listy i słowniki. # Warto zwrócić uwagę na sposób obsługi zmiennych globalnych, # czy też zewnętrznych w takich przypadkach. # W poniższym przykładzie widzimy, że funkcje tak zdefiniowane nie korzystają # z wartości zmiennej x z miejsca definicji, tylko z miejsca wywołania. # Jeżeli potrzebujemy aby funkcja używała wartości z chwili definicji to możemy # użyć x jako wartości domyślnej któregoś z argumentów tej funkcji. tablicaA = [None] * 4 tablicaB = [None] * 4 for x in [0, 1, 2, 3]: def tmpA(a): return x+a def tmpB(a, x=x): # x=x nakazuje przechwycić obecną wartość x return x+a tablicaA[x]=tmpA tablicaB[x]=tmpB print (x, " + 3 =", tablicaA[1](3), " 1 + 3 = ", tablicaB[1](3)) x = 0 print (x, " + 3 =", tablicaA[1](3), " 1 + 3 = ", tablicaB[1](3)) # Lambda jest innym sposobem lokalnego definiowania funkcji (często dostępnym także # w językach nie pozwalających na tak swobodne definiowanie zwykłych funkcji) # W tym przypadku definicja jest jeszcze bardziej lokalna, # funkcja nawet nie posiada swojej nazwy. # Lambda może być przypisana do jakiejś zmiennej lub przekazana w argumencie. # Sposób traktowania zmiennych zewnętrznych jest analogiczny. tablicaA = [None] * 4 tablicaB = [None] * 4 for x in [0, 1, 2, 3]: tablicaA[x] = lambda a: x+a tablicaB[x] = lambda a,x=x: x+a # x=x nakazuje przechwycić obecną wartość x print (x, " + 3 =", tablicaA[1](3), " 1 + 3 = ", tablicaB[1](3)) x = 0 print (x, " + 3 =", tablicaA[1](3), " 1 + 3 = ", tablicaB[1](3))
#!/ścieżka/do/programu) stanowiący informację dla programu uruchamiającego skrypt jakiego interpretera ma użyć w celu wykonania kodu zawartego w pliku (niekiedy zawiera też opcje z jakimi należy uruchomić interpreter).main. Zakończenie tej funkcji oznacza zakończenie programu, a wartość przez nią zwracana odpowiedzialna jest za tzw. kod powrotu przekazany procesowi wywołującemu program.
#include<iostream> /// generowanie wyjątków void wyj(int i) { switch(i) { // rzucamy wyjątek w zależności od i case 0: throw "Ala ma kota"; case 1: std::cout << "wypisze sie (0) ?" << std::endl; throw "Kot ma Ale"; case 2: throw 13; } std::cout << "wypisze sie (1) ?" << std::endl; } int main() { for (int j=0; j<4; j++) { std::cout << std::endl << "j = " << j << std::endl; try { wyj(j); std::cout << "wypisze sie (2) ?" << std::endl; wyj(1); } catch (char const* opis) { std::cout << "WYJATEK: " << opis << std::endl; } catch (...) { std::cout << "inny wyjątek" << std::endl; } // aby zaoszczędzić jednego kopiowania należy przechwytywać wyjątek przez referencję // ale należy pamiętać iż ze względu na mechanizm działania wyjątków // wartość ta i tak będzie kopiowana } }
# Prawie wszystkie błędy w Pythonie mają postać wyjątków, # które mogą zostać obsłużone blokiem try/except. try: a = 5 / 0 except ZeroDivisionError: print("dzielenie przez zero") except: print("inny błąd") # Przy obsłudze błędów może przydać się instrukcja pusta "pass", # która w tym przypadku pozwala na zignorowanie obsługi danego błędu. try: slownik["a"] += 1 except: pass # Powyższy kod zwiększy wartość związaną z kluczem "a" w słowniku "slownik", # jednak gdy napotka błąd (np. słownik nie zawiera klucza "a") zignoruje go. # Możemy także generować wyjątki z naszego kodu, służy do tego instrukcja raise, # której należy przekazać obiektem dziedziczącym po \python{BaseException} np: \begin{CodeFrame*}[python]{} raise BaseException("jakiś błąd")
# obliczanie silni z użyciem rekurencji def silnia(n): # każda rekurencja musi mieć warunek końca if n == 1: return 1 else: return n*silnia(n-1) silnia(20)
# iteracyjne obliczanie silni def silnia(n): s = 1 for i in range(2,n+1): s = s*i return s silnia(20)
# fragment kodu asemblerowego wygenerowanego poleceniem "gcc -S" z kodu C: # if (argc == 1) # puts("A"); # else # puts("B"); # puts("C"); # operacja porównania --- warunek if cmpl $1, -4(%rbp) # skok jeżeli nie równe do bloku else jne .L2 # odłożenie argumentu "A" na stos i wywołanie funkcji puts --- blok if leaq .LC0(%rip), %rdi call puts@PLT # skok bezwarunkowy za blok if - else jmp .L3 .L2: # odłożenie argumentu "B" na stos i wywołanie funkcji puts --- blok else leaq .LC1(%rip), %rdi call puts@PLT .L3: # odłożenie argumentu "C" na stos i wywołanie funkcji puts --- kod po if-else leaq .LC2(%rip), %rdi call puts@PLT
#include <iostream> #include <stdint.h> struct NazwaStruktury { // pola składowe int a; std::string d; // zmienna statyczna // wspólna dla wszystkich obiektów tej klasy static int x; // stała static const int y = 7; // pola binarne (jedno i trzy bitowe) uint8_t mA :1; uint8_t mB :3; // metody składowe void wypisz() { std::cout << " a=" << a << " d=" << d << "\n"; } // deklaracja metody // definicja musi być podana gdzieś indziej int getSum(int b) ; /// metody statyczna static void info() { std::cout << "INFO\n"; } // konstruktor i destruktor NazwaStruktury(int aa = 0) { std::cout << "konstruktor\n"; a = aa; d = "abc ..."; } ~NazwaStruktury() { // potrzebny gdy klasa tworzy jakieś // obiekty które nalezy usuwać, itp std::cout << "destruktor\n"; } }; // definicja zmiennej statycznej z nadaniem jej wartości // jest to niezbędne aby była ona widoczna ... int NazwaStruktury::x = 13; // wcześniej zdeklarowane metody // możemy definiować także poza deklaracją klasy int NazwaStruktury::getSum(int b) { return a + b; } int main() { // korzystanie ze struktur NazwaStruktury s; s.a = 45; s.wypisz(); // korzystanie z metod statycznych NazwaStruktury::info(); // a także poprzez obiekt danej klasy s.info(); }
class NazwaKlasy: # pola składowe a=0 d="ala ma kota" # metody składowe def wypisz(self): print(self.a + self.b) # warto zauważyć jawny argument w postaci obiektu tej klasy # w innych językach (np. C++) także on występuje, # ale nie jest jawnie deklarowany, ani nie trzeba się nim jawnie posługiwać # metody statyczna @staticmethod def info(): print("INFO") # konstruktor (z jednym argumentem) def __init__(self, x = 1): print("konstruktor", self.a , self.d) # i kolejny sposób na utworzenie pola składowego klasy self.b = 13 * x # tworzenie obiektu klasy i używanie go k = NazwaKlasy() k.a = 67 k.wypisz() # do metod można odwoływać się także tak: # (jawne użycie argumentu w postaci obiektu klasy) NazwaKlasy.wypisz(k) # korzystanie z metod statycznych NazwaKlasy.info() print("k jest typu:", type(k)) print("natomiast k.a jest typu:", type(k.a)) # obiekty można rozszerzać o nowe składowe i funkcje: k.b = k.a + 10 print(k.b) # w ten sposób można też tworzyć całe struktury: class Pusta(): pass x = Pusta() x.a = 3 x.b = 4 # Od strony implementacyjnej są one trzymane w słowniku # związanym z danym obiektem o nazwie `__dict__`: print(k.__dict__, x.__dict__, sep='\n') # w `k.__dict__` nie znajduje się wartość pola `d` jest tak dlatego, # że nie uległa ona modyfikacji w stosunku co do domyślnej wartości dla klasy # więc ma wartość wspólną dla wszystkich obiektów tej klasy # może ona być zmieniona dla pojedynczego obiektu (jak było z `a`) lub dla wszystkich: NazwaKlasy.d='abc' print(k.d) # lae nie będzie miało to efektu jeżeli w danym obiekcie została nadpisana: NazwaKlasy.a = 13 print(k.a) # Obiekty klas są obiektami modyfikowalnymi, # zatem jak wiemy zwykłe przypisanie tworzy tylko inną referencję na ten sam obiekt. # Celem utworzenia kopii naszego obiektu możemy zaimplementować własną metodę copy # lub skorzystać z funckji copy dostarczanej przez moduł copy.
<?php class NazwaKlasy { public $a; public $d = "tekst"; # metody składowe public function wypisz() { echo "a=" . $this->a . " d=" . $this->d . "\n"; } # warto zauważyć jawne odwołania do składowych # poprzez zmienną $this # metody statyczna public static function info() { echo "INFO\n"; } } // korzystanie z klasy $k = new NazwaKlasy; $k->a = 87; $k->wypisz(); // korzystanie z metod statycznych NazwaKlasy::info(); // a także poprzez zmienną przechowującą nazwę klasy $t="NazwaKlasy"; $t::info(); ?>
// klasa abstrakcyjna - definiująca interfejs dla jakiejś grupy obiektów // fakt bycia klasą abstrakcyjną wynika z niezdefiniowanej metody wirtualnej // z faktu tego wynika niemożność utworzenia obiektów będących bezpośrednimi // instancjami takiej klasy class Ksztalt { public: virtual float objetosc() = 0; // użycie virtual wymusza wywoływanie metody z klasy potomnej // kiedy odwołujemy się przy użyciu klasy bazowej }; // klasy dziedzicząca po klasie Ksztalt class Kula : public Ksztalt { float promien; public: Kula(float r) { promien = r; } float objetosc() { return 1.25 * 3.14 * promien * promien * promien; } }; class Szescian : public Ksztalt { float bok; public: Szescian(float a) { bok = a; } float objetosc() { return bok*bok*bok; } }; struct Kolor { char r, g, b; }; class Cena { public: Cena(float c); // taki zapis oznacza że definicja funkcji znajduje się poza definicją klasy // (typowo w pliku .cpp o tej samej nazie co .h zawierający definicję klasy) }; class Material { }; // klasa dziedzicząca po kilku klasach bazowych struct Opakowanie : public Ksztalt, public Kolor, public Cena { Ksztalt *ksztalt; Material *material; public: Opakowanie(Ksztalt *k, const Kolor& kolor, float c) : // wywołanie konstruktorów klasy bazowej // jawnego dla Cena i kopiującego dla Kolor Cena(c), Kolor(kolor) { // inicjalizacja zmiennej wskaźnikowej ksztalt // dzięki rozwiązaniu z zmienną przechowującą informację o kształcie // nie ma potrzeby tworzenia różnych klas Opakowanie dla różnych kształtów ksztalt = k; material = 0; } Opakowanie(Ksztalt *k, float c) : // wywołanie konstruktora klasy bazowej Cena // oraz inicjalizacja zmiennych ksztalt i material Cena(c), ksztalt(k), material(0) { // inicjalizacja pól odziedziczonych po strukturze Kolor r = 0x96; g = 0xFF; b = 0x03; } float objetosc() { return ksztalt->objetosc(); } // jest to alternatywna w stosunku co do dziedziczenia // metoda rozszerzania interfejsu jakiejś klasy // dziedziczenie (w przypadku dziedziczenia public) dodaje metody // klasy bazowej bezpośrednio do interfejsu klasy dziedziczącej // natomiast to rozwiązanie pozwala na pobranie poszczególnych klas // opisujących złożony obiekt niezależnie i operowaniu na ich interfejsie Material* getMaterial() { return material; } void setMaterial(Material *m) { material = m; } };
# klasa abstracyjna from abc import ABC, abstractmethod class Kształt(ABC): @abstractmethod def objetosc(self): return None @staticmethod @abstractmethod def typKształtu(): return None # jednak ze względu na dynamiczne typowanie klasy abstrakcyjne # w Pythonie odgrywają mniejszą rolę niż w C++ # klasy dziedzicząca po klasie Kształt class Kula(Kształt): def __init__(self, r): self.promien = r def objetosc(self): return 1.25 * 3.14 * self.promien ** 3 @staticmethod def typKształtu(): return 'Kula' class Prostopadłościan(Kształt): def __init__(self, a, b, c): self.a = a self.b = b self.c = c def objetosc(self): return self.a * self.b * self.c @staticmethod def typKształtu(): return 'Prostopadłościan' # można dziedziczyć po klasach nie abstrakcyjnych # oraz po klasach, które już dziedziczą po jakiejś class Szescian(Prostopadłościan): def __init__(self, a): self.bok = a; def objetosc(self): return self.bok ** 3 k = Kula(1) print(k.typKształtu(), k.objetosc()) s = Szescian(2) print(s.typKształtu(), s.objetosc()) # tu wykonała się funkcja `typKształtu` z klasy `Prostopadłościan` # bo nie została nadpisana w klasie `Szescian` class Kolor: def __init__(self, r, g, b): self.r = r self.g = g self.b = b # Możliwe jest także dziedziczenie po jednej lub kilku klasach bazowych: class Opakowanie(Szescian, Kolor): def __init__(self, a): Szescian.__init__(self, a) Kolor.__init__(self, .5, .5, .5) o = Opakowanie(1) print(o.objetosc(), o.b)
#include <stdio.h> int main() { int tablica[3]; // tablica 3 elementowa liczb całkowitych tablica[0] = 13; // do elementów tablicy odwołujemy się z użyciem [], // elementy tablicy indeksujemy od zera tablica[2] = 17; printf("%d %d\n", tablica[0], tablica[1]); // nie zainicializowane elementy (tak samo jak nie zainicjalizowane zmienne) // będą mieć przypadkową wartość (nie jest to jednak dobre źródło losowości) } void xxx(int n) { // Język C od wersji C99 pozwala na korzystanie z tablic zmiennej długości (*VLA*), // czyli tablic których rozmiar nie jest stałą czasu kompilowania a zmienną - np.: float vals[n]; v[0] = 21; /* ... */ }
#include <iostream> #include <vector> int main() { // klasyczna tablica z C // wartości elementów może zainicjalizować od razu przy deklaracji tablicy: int t[4] = {1, 8, 3, 2}; std::cout << t[2] << " -> "; t[2] = 55; std::cout << t[2] << " = " << *(t+2) << "\n"; // jest ona podobnie jak struktura ciągłym obszarem pamięci, // możliwe jest zatem traktowanie takiej tablicy jako struktury struct Struktura { int a, b, c, d; }; Struktura *tt = (Struktura*)t; std::cout << tt->a << " " << tt->c << "\n"; // dynamicznie alokowana tablica C++ STL std::vector<int> v(4); v[3] = 21; std::cout << v[3] << "\n"; }
a[0]="a b c" a[1]=123 a[2]=zz a[3]=qq x=1 echo "wybrane elementy tablicy: " ${a[$x]} ${a[0]} echo "cała tablica: " ${a[@]} echo "ilość elementów w tablicy tablica: " ${#a[@]}
var t = ['a b c', 8, 3, 2]; console.log( "pierwszy element: " + t[0] + "\n" + "długość tablicy: " + t.length + "\n" ); // usuwamy 2 elementy od pozycji 1 t.splice(1, 2) // dodajemy nowe elementy t[5] = "pp"; t[7] = "ww"; // wypisanie wszystkich elementów for (var i=0; i<t.length; ++i) { console.log("t["+ i +"]="+ t[i] +"\n"); } // za pomocą for..in for (var ii in t) { console.log("t["+ ii +"]="+ t[ii] +"\n"); }
#include <iostream> #include <list> int main() { std::list<int> l; // dodanie elementu na końcu l.push_back(17); l.push_back(13); l.push_back(3); l.push_back(27); l.push_back(21); // dodanie elementu na początku l.push_front(8); // wypisanie liczby elementów std::cout << "size=" << l.size()<< "\n"; // wypisanie pierwszego i ostatniego elementu std::cout << "first=" << l.front() << " last=" << l.back() << "\n"; // usuniecie ostatniego elementu l.pop_back(); // posortowanie listy l.sort(); // odwrócenie kolejności elementów l.reverse(); // usuniecie pierwszego elementu l.pop_front(); for (std::list<int>::iterator i = l.begin(); i != l.end(); ++i) { // wypisanie wszystkich elementów std::cout << *i << "\n"; // możliwe jest także: // - usuwanie elementu wskazanego przez iterator // - wstawianie elementu przed wskazanym przez iterator } }
l = [ 3, 5, 8 ] # wstawienie elementu na koniec l.append(1) # wstawienie elementu na pozycje 2 l.insert(2, 13) print("liczba elementów =", len(l)) print("pierwszy =", l[0]) print("dwa kolejne =", l[1:3]) # wypisanie wszystkich elementów for e in l: # możemy modyfikować zmienną "e", # ale nie będzie maiło to wpływu na listę print(e) # alternatywne iterowanie po elementach # (pozwala na ich modyfikowanie) for i in range(len(l)): l[i] = l[i] + 1 print(l[i]) # możemy też uzyskać listę w oparciu o wykonanie jakiś # operacji na danej liście w formie jednolinijkowca: l = [a * 2 for a in l] # listę taką możemy przypisać do innej # lub (jak wyżej) do tej samej zmiennej # pobranie i usuniecie ostatniego elementu print("ostatnim był:", l.pop()) print("ostatnim był:", l.pop()) # pobranie i usuniecie elementu na wskazanej pozycji print("drugim elementem był:", l.pop(1)) # wypisanie całej listy print(l)
#include <iostream> #include <map> int main() { std::map<std::string, int> m; m["a"] = 6; m["cd"] = 9; std::cout << m["a"] << " " << m["ab"] << "\n"; // wyszukanie elementu po kluczu std::map<std::string, int>::iterator iter = m.find("cd"); // sprawdzenie czy istnieje if (iter != m.end()) { // wypisanie pary - klucz wartość std::cout << iter->first << " => " << iter->second << "\n"; // usunięcie elementu m.erase(iter); } m["a"] = 45; // wypisanie całej mapy for (iter = m.begin(); iter != m.end(); ++iter) std::cout << iter->first << " => " << iter->second << "\n"; // jak widać mapa jest wewnętrznie posortowana }
m = { "ab" : 11, "cd" : "xx" } x = "e" m[x] = True; # pobranie samych kluczy for k in m: print (k, "=>", m[k]) # sprawdzenie istnienia if "ab" in m: print ("jest ab") # usunięcie elementu del m['ab'] # modyfikacja wartości m["cd"] = "oi" # pobranie par klucz wartość for k,v in m.items(): print (k, "=>", v)
<?php $a = array('ab' => True, 'cd' => 'xx'); $a['efg'] = 15; $a['cd']="yu"; # sprawdzenie istnienia if (isset($a['ab'])) { // uwaga: gdy $a['ab'] = null // to isset zwróci FALSE // patrz array_key_exists() echo "jest ab"; # usunięcie elementu unset($a['ab']); } while ( list($k, $v) = each($a) ) { echo "a[$k] = $v\n"; } ?>
// pseudo odpowiednik // tablicy asocjacyjnej var o = {a: 1, b: 2, c: 3}; // dodanie elementu o.ab = "pp"; // usunięcie elementu delete o.b; for (var k in o) { console.log( `o.${k} = ${o[k]} \n` ); }
std::multimap<>.
std::set<> i std::multiset<> z C++ oraz set([k1, k2, ...]) z Pythona). Różnią się one od map tym że nie przechowują one wartości a jedynie same klucze.
// UWAGA: przykład ten jest kontynuacją przykładu z języka C #include <stdio.h> #include <iostream> void f3(int& a) { // & oznacza że argument będzie pobrany jako referencja a = 13; } void ff(int (*f)(const char *s)) { f("Uwolnić mrożone truskawki !!!"); } struct Struktura { int a; int b; int getSum(int c) { return a + c; } }; int main() { int x = 5678; Struktura obiekt = {2, 4}; // // wskaźniki na obiekty i ich składowe // (identycznie będzie działało to w czystym C) // // wskaźnik na strukturę Struktura *wsk = &obiekt; // dostęp do składowych poprzez wskaźnik na strukturę (*wsk).a = 7; wsk->b = 8; // `x->y` jest skróconym, ładniejszym zapisem dla `(*x).y` std::cout << obiekt.a << " " << obiekt.b << "\n"; // wskaźniki na składowe int *aa = &(wsk->a); int *bb = &(obiekt.b); *aa = 13; *bb = 17; std::cout << obiekt.a << " " << obiekt.b << "\n"; // // referencja // (to nie występuje w C) // // Referencje są zasadniczo wskaźnikami, których używa się jak zwykłych zmiennych // (bez stosowania operatora `*` w celu operowania na wartości wskaźnika). // W odróżnieniu od wskaźników nie możemy bezpośrednio operować na wskazywanym // przez nią adresie (np. spowodować aby wskazywała na inną zmienną). int &y = x; y = 6543; // brak operatora odwołania się do wskazywanej wartości std::cout << x << " = " << y << "\n"; // przekazując referncję do funkcji uzyskamy takie samo zachowanie // jak w przypadku wskaźnika - funkcja będzie mogła modyfikować wartość argumentu // (inny jest sposób zapisu - zarówno w definicji funkcji jak i jej wywołaniu) f3(y); // w wywołaniu funkcji pobierającej argument jako referencja // podajemy po prostu zmienną na którą chcemy uzyskać referencję // nie ma tu operatora pobrania adresu jak przy wskaźniku std::cout << y << "\n"; // // wskaźniki na funkcje // (identycznie będzie działało to w czystym C) // // wskaźnik "fun" na funkcje przyjmującą // wskaźnik const char i zwracającą int int (*fun)(const char *s); // przypisanie adresu funkcji puts do zmiennej fun fun = &puts; // użycie wskaźnika na funkcję jako funkcji fun("aaa"); // wskaźnik na funkcję może być przekazywany // do innych funkcji jako argument ff(fun); // // wskaźniki na metodę // (to nie występuje w C) // // wskaźnik na metodę składową jakiejś klasy (z wyjątkiem metod statycznych) // wymaga określenia typu tej klasy // (gdyż jest on typem niejawnego argumentu jej metod) int (Struktura::*fun2)(int) = &Struktura::getSum; // aby skorzystać trzeba mieć obiekt danej klasy (lub wskaźnik do niego) std::cout << (obiekt.*fun2)(2) << " + 1 = " << (wsk->*fun2)(3) << "\n"; } // Z wskaźników lub referencji stanowiących argumenty funkcji korzysta się także // aby uniknąć kopiowania dużych struktur, // w tym przypadku dobrym zwyczajem jest dodanie `const`, // aby funkcja nie mogła modyfikować tego na co wskazuje ten wskaźnik void f4(const struct Struktura *s) { // s->a = 15; // to spowodowałoby błąd kompilacji w tym miejscu, // z powodu const w linii wyżej std::cout << s->a << "\n"; } // Podobnie często używa się wskaźników na duże obiekty przy umieszczaniu ich // w różnych strukturach danych (jak tablice, listy, itp) aby uniknąć ich kopiowania
a, b = 5, [1, 2, 3] def pinfo(x, xx): print( "id(x) =", hex(id(x)), " == " if id(xb) == id(xx) else " != ", "id(xx) =", hex(id(xx)) ) # utworzenie kopii zmiennej oznacza utworzenie # nowej referencji wskazującej nadal na ten sam # obiekt w pamięci aa = a pinfo(id(a), id(aa) bb = b pinfo(id(b), id(bb) # przypisanie nowego obiektu pod zmienną # powoduje zmianę (adresu) obiektu który # wskazuje: aa = 5 pinfo(id(a), id(aa) bb = [9, 11, 13] pinfo(id(b), id(bb) # ale modyfikacja obiektów "immutable" # (takich jak liczby, napisy) nie jest możliwa # zatem zawsze tworzony jest nowy obiekt aa=a aa+=1 pinfo(id(a), id(aa) # jeżeli do starego obiektu nie ma innych # referencji może on zostać usunięty # a nowy może być umieszczony w jego miejscu # (w takim przypadku wynik id się nie zmieni) # natomiast modyfikacja obiektów "mutable" # (takich jak listy i słowniki) # nie tworzy nowego obiektu tylko modyfikuje # istniejący (wynik id się nie zmieni) zatem # wszystkie referencje wskazują na zmodyfikowany # obiekt: bb = b bb[0] = 17 print ("bb =", bb, "b=", b) pinfo(id(b), id(bb) # aby uzyskać kopię należy skorzystać # z odpowiedniej metody bb = b.copy() print ("bb =", bb, "b=", b) bb[0] = 99 print ("bb =", bb, "b=", b) pinfo(id(b), id(bb) # uwaga kopiowanie takie jest płytkie: jeżeli w # liście mamy obiekty "mutable" obie (niezależne # pod względem zbioru elementów) kopie listy # będą wskazywać na te same obiekty # usuwanie zmiennej b = None # mapowanie zmiennej "b" zostało zmienione # nie wskazuje już na listę tylko na # obiekt typu NoneType bbb = bb del bb # nazwa "bb" została usunięta # ale do danych możemy dostawać się przez "bbb" # dopiero po usunięciu wszystkich referencji # na dany obiekt to Python może go usunąć (ale # nie musi wykonać tego natychmiast) # python także umożliwia przekazywanie funkcji # jako argumentu do innej funkcji def a(z): print(z*3) def b(x, y): x(y+2) b(a, 1) # ponadto funkcja może także zwracać funkcję: def aa(y): def t(x): return y*x return t b = aa(3) b(2) # można też: aa(3)(4)
# w Bashu nie ma operacji na wskaźnikach # ale podobną funkcję w pewnych wypadkach # może pełnić zmienna zawierająca nazwę # innej zmiennej A='tekst do wypisania, $HOME, `ls`'; B="A"; # proste podejście typu echo ${$B} # (działające np. w PHP) nie zadziała, # ale można to zrobić na kilka innych sposobów: C=${!B}; # ta metoda nie działa w czystym sh echo $C export A C=$(echo "\$$B" | envsubst) # ta metoda wymaga zewnętrznego polecenia # envsubst i wyeksporotwania zmiennych echo $C C=$( eval "echo \$$B" ) echo $C # z użyciem większej liczby poleceń eval możemy # zapewnić także podstawienie kolejnego poziomu # zmiennych lub wykonanie wpisanych w nich poleceń C=$( eval eval "echo \$$B" ) echo $C # zmienna może przechowywać komendę / nazwę # funkcji do wykonania x=ls $x /tmp
<?php // w PHP korzystanie z nazwy zmiennej // przechowywanej w innej zmiennej // jest jeszcze prostsze: $a="12"; $b="a"; $c="b"; echo "$a $b $c\n" echo "${$b} ${$c}\n" echo "${${$c}}\n" // możliwe jest także używanie zmiennych // zawierających nazwy funkcji oraz // przekazywanie ich do innych funkcji function fa($z) { print($z*3); print("\n"); } $c="fa"; $c(2); function fb($x, $y) { $x($y+2); } fb("fa", 1); ?>
malloc) lub tworzenia obiektów poprzez new limitowana jest widoczność i istnienie otrzymanego wskaźnika, ale nie zaalokowanego bloku pamięci. Zatem ograniczona jest widoczność takich zmiennych ale nie czas ich istnienia, dlatego też przed utratą wskaźnika na nie należy je usunąć (zwolnić zaalokowaną pamięć).
#include <iostream> int funA(int a) { a = a*2; return a; } int funB(int &a) { a = a*2; return a; } int main() { int a = 57, b = 23; std::cout << ++a << " " << ++b << "\n"; // wypisze 58 24 { int a = b; std::cout << ++a << " " << ++b << "\n"; // wypisze 25 25 // bo a z tego bloku (==24) przesłoniło wcześniejsza zmienną a (==58) } std::cout << ++a << " " << ++b << "\n"; // wypisze 59 26 // bo a z wcześniejszego bloku już nie istnieje i nie przesłania naszego a (==58) std::cout << funA(a) << " " << funB(b) << "\n"; // wypisze wartości zwracane przez funkcje czyli 118 (59*2) i 52 (26*2) std::cout << a << " " << b << "\n"; // wypisze aktualne wartości argumentu: // a=59 bo przekazanie przez wartość i funkcja operowała na własnej kopii // b=52 bo przekazane przez referencję i funkcja operowała na tej samej kopii } /* * Podsumowanie: * * Zasięg zmiennych (widoczność i istnienie) jest limitowany do bloku * (wydzielanego nawiasami klamrowymi) w którym zostały zadeklarowane, * zmienne z bloków wewnętrznych mogą przesłaniać zmienne zadeklarowane wcześniej. * * Wywołanie funkcji powoduje rozpoczęcie nowego kontekstu, w którym zmienne * z bloku wywołującego funkcję nie są widoczne (ale nadal istnieją). * * Argumenty do funkcji przekazywane są przez kopiowanie, więc funkcja nie ma * możliwości modyfikacji zmiennych z bloku ją wywołującego nawet do niej * przekazanych (wyjątkiem jest przekazanie przez referencję lub wskaźnik). */
a, b, d = 5, 12, [1, 2, 3] def f1(c): return c+b def f2(c): b=2 return c+b # f1 korzysta z zmiennej globalnej b # f2 przysłania sobie zmienną globalną b poprzez swoją zmienną lokalną print("f1(a) =", f1(a), "f2(a) =", f2(a), "ale b nadaj wynosi:", b) def f3(c): global b b=16 return c+b # f3 dzięki temu że jawnie deklaruje iż używa globalnego b # może modyfikować zmienną globalną print("f3(a) =", f3(a), "teraz b wynosi:", b) def f4(c): c = 2*c return c # argumenty funkcji są zmiennymi lokalnymi print("f4(a) =", f4(a), "ale a nadaj wynosi:", a) def f5(c): c[0]="xx" return c print("f5(d) =", f5(d), "tym razem d uległo modyfikacji:", d) if d[1] == 2: x = "ABC" else: x = 22 print("zmienna x jest widoczna poza blokiem w którym została utworzona", x)
// definicja szablonu template <typename T> T wyrazenie(T a, T b) { // T jest parametrem szablonu (typem zastępczym) i będzie zastąpione typem argumentu return a + 2*b*a + 3*b; } // przykłady użycia: // 1. argumenty są typu int i wynik jest typu int int fun1(int a) { return wyrazenie(a, 13); } // 2. argumenty są typu double i wynik jest typu double double fun2(double a) { return wyrazenie(a, 1.3); } // 3. argumenty są różnych typów (double i int), // jawnie wymuszamy działanie funkcji `wyrazenie` typu double (jako T), // wartość `a` będzie rzutowana z int na double, wynik jest typu double double fun3(int a) { return wyrazenie<double>(a, 1.3); }
#include <inttypes.h> #include <stdio.h> int main() { // dane jako tablica liczb 16 bitowych uint16_t aa[4] = {0x1234, 0x5678, 0x9abc, 0xdeff}; // wypisujemy ją printf("A0: %x %x %x %x\n", aa[0], aa[1], aa[2], aa[3]); // chyba nikogo nie zaskoczy wynik powyższego printf: A0: 1234 5678 9abc deff // wypisujemy dwie pierwsze liczby rozłożone na części 8 bitowe (poszczególne bajty) printf("A1: %x %x %x %x\n", (aa[0] >> 8) & 0xff, aa[0] & 0xff, (aa[0] >> 8) & 0xff, aa[0] & 0xff); // efekt też jest oczywisty: A1: 12 34 12 34 // każemy na te same dane patrzeć jako na liczby 8 bitowe (poszczególne bajty) uint8_t* bb = (uint8_t*) aa; printf("B0: %x %x %x %x\n", bb[0], bb[1], bb[2], bb[3]); // czego się teraz spodziewamy? // - wypisze nam tylko połowę oryginalnej tablicy // - ale dokładny wynik zależy od architektury na której uruchamiamy program: // * na little endian (np. x86) będzie to: B0: 34 12 78 56 // * na big endian (np. sparc) będzie to (bardziej naturalne dla człowieka): B0: 12 34 56 78 }
. - dowolny znak
[a-z] - znak z zakresu
[^a-z] - znak z poza zakresu (aby mieć zakres z ^ należy dać go nie na początku)
^ - początek napisu/linii
$ - koniec napisu/linii
* - dowolna ilość powtórzeń
? - 0 lub jedno powtórzenie
+ - jedno lub więcej powtórzeń
{n,m} - od n do m powtórzeń
() - pod-wyrażenie (może być używane dla operatorów powtórzeń, a także dla referencji wstecznych)
| - alternatywa: wystąpienie wyrażenia podanego po lewej stronie albo wyrażenia podanego prawej stronie
/* w C++ można korzystać także z napisów w stylu C * jednak C++ oferuje własny typ obudowujący w sposób obiektowy * tablicę znaków znaną z języka C * oraz kilka innych udogodnień (jak np. strumienie) */ #include <stdio.h> #include <iostream> #include <string> #include <bitset> #include <regex> #include <sstream> int main() { // napisy w stylu C++ std::string xx(x); std::string y = "aa bb cc bb dd bb ee"; // wypisanie długości napisu std::cout << xx.size() << "\n"; // .size() to to samo co .length() // uzyskanie napisy w stylu C puts(xx.c_str()); // wypisanie pod-napisu od 2 do końca std::cout << xx.substr(2) << "\n"; std::cout << xx.substr(2, std::string::npos) << "\n"; // i od 0 (początku)do 3 std::cout << xx.substr(0, 3) << "\n"; // wyszukiwanie pod-napisu "bb" w y od pozycji 5 std::cout << y.find("bb", 5) << "\n"; // porównywanie if (xx == "a") std::cout << "x == \"a\"\n"; if (xx.compare(0, 1, "a") == 0) puts("pierwsze 1 znaków x to \"a\""); if ( std::regex_match(xx, std::regex(".*[dz].*")) ) puts("x zawiera d lub z"); // regex_match dopasowuje całość napisu do wyrażenia regularnego // dopasowanie częściowe wraz z opcjonalnym uzyskaniem // pasującej części umożliwia: std::regex_search() // modyfikowanie std::string xx = "Ala ma psa"; // wstawianie - insert(pozycja, co) xx.insert(6, " kota i"); std::cout << xx << std::endl; // zastępowanie - replace(pozycja, ile, czym); xx.replace(4, 2, "miała samochód", 0, 6); // mogłoby też być xx.replace(4, 2, "miała"); i parę innych wariantów ... std::cout << xx << std::endl; // usuwanie - erase(pozycja, ile); xx.erase(9, 1); // 9 zamiast 8 bo UTF-8 i ł ma dwa znaki std::cout << xx << std::endl; // zastępowanie z użyciem wyrażeń regularnych std::cout << std::regex_replace (y, std::regex("[bc]+"), "XX") << "\n"; // zastępowanie z użyciem podstawienia // $2 zostanie zastąpione wartością drugie pod-wyrażenia, // czyli fragmentu ujętego w nawiasach std::cout << std::regex_replace ( y, std::regex("([bc]+) ([bc]+)"), "X-$2-X" ) << "\n"; // konwersja liczb na napis w systemach: // dwójkowym, ósemkowym, dziesiętnym i szesnastkowym std::cout << std::bitset<8>(7) << " "; std::cout << std::oct << 0xf << " "; std::cout << std::dec << 010 << " "; std::cout << std::hex << 0b11 << "\n"; // liczby podawane do wypisywania są w odpowiednio systemach: // dziesiętnym, szesnastkowym, ósemkowym i dwójkowym // wskazane jest to przez brak prefixu i prefixy "0x" "0" "0b" // alternatywnie w stylu printf, ale bez dwójkowego printf("0o%o %d 0x%x\n", 0xf, 010, 0b11); // wypisywanie znaków unicodu puts("\u21c4 = ⇄"); // strumienie napisowe std::ostringstream zzz; zzz << xx << " cpp\n"; zzz << 34.6 << " " << std::oct << 0xf << " "; // konwersja do std::string xx = zzz.str(); std::cout << xx << "\n"; }
import re # napisem w Pythonie jest ciąg znaków ujęty w cudzysłowa lub apostrofy # (nie ma różnicy którego zapisu użyjemy). x = "abcdefg" y = "aa bb cc bb dd bb ee" z = "qw=rt" # Potrójne cudzysłowa / apostrofy pozwalają na definiowanie napisów wieloliniowych # (a także zawierających pojedyncze cudzysłowa / apostrofy w tekście). q = '''abc def''' '''rozpoczynający się z początkiem linii nieprzypisany do zmiennej napis jest ignorowany i może być użyty jako komentarz, typowo komentarz dokumentacyjny''' # wypisanie długości napisu print(len(x)) # wypisanie pod-napisu od 2 do końca # i od 0 (początku)do 3 print (x[2:], x[0:3]) # wypisanie ostatniego i 3 ostatnich znaków print (x[-1], x[-3:]) # wypisanie co 3ciego znaku z napisu oraz napisu od tyłu print (y[::3], x[::-1]) # wyszukiwanie # pod-napisu "bb" w y od pozycji 5 print (y.find("bb", 5)) # porównywanie if x == "a": print("x == \"a\"") # sprawdzanie czy zawiera pod-napis (z listy) for podnapis in ["ab", "bc"]: if podnapis in x: print (podnapis, "jest pod-napisem:", x) # # Wyrażenia regularne # # sprawdzanie czy pasuje do wyrażenia regularnego if re.search("[dz]", x): print(x, "zawiera d lub z") # zastępowanie (dowolny niepusty ciąg złożony z liter b oraz c na XX) print (re.sub('[bc]+', "XX", y)) # czwarty (opcjonalny) argument określa ile razy ma być wykonane zastępowanie print (re.sub('[bc]+', "XX", y, 2)) # zastępowanie z użyciem podstawienia # \\2 zostanie zastąpione wartością drugie pod-wyrażenia, # czyli fragmentu ujętego w nawiasach print (re.sub('([bc]+) ([bc]+)', "X-\\2-X", y)) # mamy też wpływ na zachłanność wyrażeń regularnych: print (re.sub('bb (.*) bb', "X \\1 X", y)) # "bb (.*) bb" dopasowało najdłuższy możliwy fragment, czyli: cc bb dd print (re.sub('.*bb (.*) bb.*', "\\1", y)) # "bb (.*) bb" dopasowało jedynie "dd", bo najdłuższy możliwy # fragment został dopasowany przez poprzedzające ".*" print (re.sub('.*?bb (.*) bb.*', "\\1", y)) # "bb (.*) bb" mogło i dopasowało najdłuższy możliwy fragment, # gdyż było poprzedzone niezachłanną odmianą dopasowania # dowolnego napisu, czyli: .*? # Po każdym z operatorów powtórzeń (. ? + {n,m}) możemy dodać # pytajnik (.? ?? +? {n,m}?) aby wskazać że ma on dopasowywać # najmniejszy możliwy fragment, czyli ma działać nie zachłannie. # # Modyfikowanie napisów # # w Pythonie nie da się modyfikować napisu z użyciem odwołań x[numer] np. # x[2]="X" # nie zadziała # można (gdy dużo tego typu modyfikacji) przepisać do listy: l=list(x) # alternatywnie można manualnie: # l=[] # for c in x: # l.append(c) # albo tak: # l=[c for c in x] l[1]="X" l[3]="qqq" del l[5] print("".join(l)) # albo tak (gdy mniej modyfikacji) print(x[:2] + "XXX" + x[3:]) # można także modyfikować po kolei i dodawać do nowego napisu s = "" for c in x: if c == "a": s += "AA" else: s += c print(s) # przy pomocy metody split() napis możemy podzielić # na listę napisów przy pomocy dowolnego separatora print(y.split(" ")) print(y.split(" cc ")) # # Konwersja liczba - napis # # konwersja liczb na napis w systemach: # dwójkowym, ósemkowym, dziesiętnym i szesnastkowym print( bin(7), oct(0xf), str(0o10), hex(0b11) ) # liczby podawane do wypisywania są w odpowiednio systemach: # dziesiętnym, szesnastkowym, ósemkowym i dwójkowym # wskazane jest to przez brak prefixu i prefixy "0x" "0o" "0b" # alternatywnie w stylu printf, ale bez dwójkowego s = "0o%o %d 0x%x" % (0xf, 0o10, 0b11) print(s) # # Konwersja znak - numer znaku i kodowania znaków # # uzyskiwanie znaków z użyciem ich numeru w unikodzie # - funkcja chr() zwraca napis złożony ze znaku o podanym numerze # w ramach napisów można też użyć \uNNNN gdzie NNNN jest numerem znaku # lub po prostu umieścić dany znak w pliku kodowanym UTF8 print(chr(0x21c4) + " == \u21c4 == ⇄") # funkcja ord() umożliwia konwersję napis złożonego # z pojedynczego znaku na numer unicodowy print(hex(ord("⇄")), hex(ord("\u21c4")), hex(ord(chr(0x21c4))) ) # Python używa Unicode dla obsługi napisów, jednak przed # przekazaniem napisu do świata zewnętrznego konieczne # może być zastosowanie konwersji do określonej postaci # bytowej (zastosowanie odpowiedniego kodowania) # służy do tego metoda encode() np. a = "aąbcć ... ⇄" inUTF7 = a.encode('utf7') inUTF8 = a.encode() # lub a.encode('utf8') print("'" + a + "' w UTF7 to: " + str(inUTF7)) print(" i jest typu: " + str(type(inUTF7))) # obiekty typu 'bytes' mogą zostać zdekodowane do napisu print("zdekodowany UTF7: " + inUTF7.decode('utf7')) # lub zostać poddane dalszej konwersji np. kodowaniu base64: import codecs b64 = codecs.encode(inUTF8, 'base64') print("napis w UTF8 po zakodowaniu base64 to: " + str(b64))
<?php $x = "abcdefg"; $y = "aa bb cc bb dd bb ee"; $z = "qw=rt"; # wypisanie długości napisu echo strlen($x) . "\n"; # wypisanie pod-napisu od 2 do końca echo substr($x, 2); # i od 0 (początku) 3 kolejne znaki echo substr($x, 0, 3) . "\n"; # wyszukiwanie # pod-napisu "bb" w $y od pozycji 5 echo strpos($y, "bb", 5) . "\n"; # porównywanie if ($x == "a") echo "$x == a\n"; if (substr_compare($x, "de", 3, 2)) { echo "2 znakowy pod-napis od"; echo "pozycji 3 w $x to \"de\"\n"; } if (preg_match("/[dz]/", $x)) { echo "$x zawiera d lub z\n"; } # zastępowanie echo str_replace("bb", "BB", $y) . "\n"; echo preg_replace('/[bc]+/', "XX", $y, 2) . "\n"; echo preg_replace('/[bc]+/', "XX", $y) . "\n"; # zastępowanie z użyciem podstawienia # $2 zostanie zastąpione wartością pierwszego pod-wyrażenia, # czyli fragmentu ujętego w nawiasach echo preg_replace('/^([^=]*)=.*$/', '$1', $z); echo " = "; echo preg_replace('/^[^=]*=(.*)$/', '$1', $z); echo "\n"; # wypisywanie w różnych systemach liczbowych printf("0b%b 0o%o %d 0x%x\n", 7, 0xf, 010, 0b11); ?>
# ${zmienna:-"napis"} zwróci napis gdy # zmienna nie jest zdefiniowana lub jest pusta # ${zmienna:="napis"} zwróci napis # oraz wykona podstawienie zmienna="napis" gdy # zmienna nie jest zdefiniowana lub jest pusta # ${zmienna:+"napis"} zwróci napis gdy # zmienna jest zdefiniowana i nie pusta a=""; b=""; c="" echo ${a:-"aa"} ${b:="bb"} ${c:+"cc"} echo $a $b $c a="x"; b="y"; c="z" echo ${a:-"aa"} ${b:="bb"} ${c:+"cc"} echo $a $b $c # ${#str} zwróci długość napisu w zmiennej str # ${str:n} zwróci pod-napis $str od n do końca # ${str:n:m} zwróci pod-napis $str od n o długości m x=abcdefg echo ${#x} ${x:2} ${x:0:3} ${x:0:$((${#x}-2))} # ${str#"ab"} zwróci $str z obciętym "ab" z początku # ${str%"fg"} zwróci $str z obciętym "fg" z końca echo ${x#"abc"} ${x%"efg"} echo ${x#"ac"} ${x%"eg"} # w napisach do obcięcia możliwe jest stosowanie shellowych # znaków uogólniających, czyli *, ?, [abc], itd # operator # i % dopasowują minimalny napis do usunięcia # operatory ## i %% dopasowują maksymalny napis do usunięcia x=abcd.e.fg echo ${x#*.} ${x##*.} ${x%.*} ${x%%.*} # ${str/"n1"/"n2"} zwróci $str z zastąpionym # pierwszym wystąpieniem n1 przez n2 # ${str//"n1"/"n2"} zwróci $str z zastąpionymi # wszystkimi wystąpieniami n1 przez n2 y="aa bb cc bb dd bb ee" echo ${y/"bb"/"XX"} echo ${y//"bb"/"XX"} # polecenie expr match $x 'wr1\(wr2\)wr3' # zwróci część $x pasującą do wyrażenia regularnego wr2 # wyrażenia regularne wr1 i wr2 pozwalają na # określanie części napisu do odrzucenia # alternatywną składnią jest expr $x : 'wr1\(wr2\)wr3' z="ab=cd" expr match $z '^\([^=]*\)=' expr $z : '^[^=]*=\(.*\)$' # możliwe jest też sprawdzanie dopasowań wyrażeń # regularnych poprzez (uwaga na brak cytowania): [[ "$z" =~ ^([^=]*)= ]] && echo "OK" # wypisywanie w różnych systemach liczbowych printf "0o%o %d 0x%x\n" 0xf 010 3 # do bardziej zaawansowanych operacji # mogą być przydatne także polecenia: # grep diff sed awk # join comm paste # należy też pamiętać o różnicy w działaniu echo "$a" i # echo $a w przypadku gdy zmienna a zawiera znaki nowej linii: # - w pierwszym wypadku będą traktowane jako znaki nowej linii # - w drugim jako spacje
# obsługa napisów w bash'u przy pomocy standardowych komend POSIXa # jako że większość operacji bashowych wiąże się z # uruchamianiem zewnętrznych programów to także # przetwarzanie napisów może być realizowane w ten sposób a="aąbcć 123" # obliczanie długości napisu w znakach, w bajtach i ilości słów w napisie echo -n $a | wc -m echo -n $a | wc -c echo -n $a | wc -w # obliczanie ilości linii (dokładniej ilości znaków nowej linii) wc -l < /etc/passwd # wypisanie 5 pola (rozdzielanego :) z pliku /etc/passwd z eliminacją # pustych linii oraz linii złożonych tylko ze spacji i przecinków cut -f5 -d: /etc/passwd | grep -v '^[ ,]*$' # komenda cut wybiera wskazane pola, opcja -d określa separator # alternatywne podejście z użyciem AWK awk -F: '$5 !~ "^[ ,]*$" {print $5}' /etc/passwd # awk daje duże możliwości przy przetwarzaniu tego typu tekstowych baz # danych ... możemy np. wypisywać wypisywać pierwsze pole w oparciu # o warunki nałożone na inne: awk -F: '$5 !~ "^[ ,]*$" && $3 >= 1000 {print $1}' /etc/passwd # jak widać w powyższych przykładach do poszczególnych pól odwołujemy # się poprzez $n, gdzie n jest numerem pola, $0 oznacza cały rekord # program dla każdego rekordu przetwarza kolejne instrukcje postaci # "warunek { komendy }", instrukcji takich może być wiele w programie # (przetwarzane są kolejno) komenda "next" kończy przetwarzanie danego rekordu # separator pola ustawiamy opcją -F (lub zmienną FS) domyślnym separatorem # pola jest dowolny ciąg spacji i tabulatorów (w odróżnieniu od cut # separator może być wieloznakowym napisem lub wyrażeniem regularnym) # domyślnym separatorem rekordu jest znak nowej linii # (można go zmienić zmienną RS) # awk jest prostym językiem programowania obsługującym podstawowe pętle # i instrukcje warunkowe oraz funkcje wyszukujące i modyfikujące napisy echo "aba aab bab baa bba bba" | awk '{ for (i=1; i<=NF; ++i) { # dla każdego pola w rekordzie if(i%2==0) # jeżeli jego numer jest parzysty gsub("b+", "B", $i); # zastąp wszystkie ciągi b pojedynczym B ii = index($i, "B") # wyszukaj pozycję pod-napisu B if (ii) # jeżeli znalazł to wypisz pozycję i pod-napis printf("# %d %s\n", ii, substr($i, ii)) # od tej pozycji do końca } print $0 }' # AWK obsługuje także tablice asocjacyjne pozwala to np. policzyć # powtórzenia słów echo "aa bb aa ee dd aa dd" | awk ' BEGIN {RS="[ \t\n]+"; FS=""} {slowa[$0]++} {printf("rekord: %d done\n", NR)} END {for (s in slowa) printf("%s: %s\n", s, slowa[s])} ' # podobny efekt możemy uzyskać stosując "uniq -c" (który wypisuje unikalne # wiersze wraz z ich ilością) na odpowiednio przygotowanym napisie # (spacje zastąpione nową linią, a linie posortowane) echo "aa bb aa ee dd aa dd" | tr ' ' '\n' | sort | uniq -c # jednak rozwiązanie awk można łatwo zmodyfikować aby wypisywało pierwsze # wystąpienie linii bez sortowania pliku # inną bardzo przydatną komendą jest sed pozwala ona m.in na zastępowanie # wyszukiwanego na podstawie wyrażenia regularnego tekstu innym echo "aa bb cc bb dd bb ee" | sed -e 's#\([bc]\+\) \([bc]\+\)#X-\2-X#g' # sedowe polecenie s przyjmuje 3 argumenty (oddzielane mogą być dowolnym # znakiem który wystąpi za s), pierwszy to wyszukiwane wyrażenie, drugi # tekst którym ma zostać zastąpione, a trzeci gdy jest g to powoduje # zastępowanie wszystkich wystąpień a nie tylko pierwszego # należy zwrócić uwagę na różnicę w składni wyrażenia regularnego polegającą # na poprzedzaniu (, ) i + odwrotnym ukośnikiem aby MIAŁY znaczenie specjalne # sed z opcją -i i wskazaniem pliku modyfikuje zawartości tego pliku # pozwala to na łatwe stworzenie funkcji rekurencyjnego zastępowania: rreplace() { if [ $# -ne 2 ]; then echo USAGE: $1 str1 str2 return fi grep -R "$1" . | cut -f 1 -d: | uniq | while read f; do [ -L $f ] || sed -e "s#$1#$2#g" -i $f; done; }
var x = "abcdefg"; var y = "aa bb cc bb dd bb ee"; var z = "qw=rt"; // podstawianie wartości zmiennych w napisie console.log(`x=${x} y=${y} z=${z}\n`) function echo(a) { // łączenie napisów (lub napisu i czegoś // co można skonwertować na napis) // z użyciem operatora + console.log(a + "\n"); } // wypisanie długości napisu echo(x.length); // wypisanie pod-napisu od 2 do końca echo(x.substr(2)); // i od 0 (początku) 3 kolejne znaki echo(x.substr(0, 3)); // wyszukiwanie // pod-napisu "bb" w $y od pozycji 5 echo(y.indexOf("bb", 5)); // porównywanie if (x == "abcdefg") echo("x == abcdefg"); if (x.search(/[dz]/) > 0) { echo("x zawiera d lub z"); } // zastępowanie echo(y.replace("bb", "BB")); echo(y.replace(/[bc]+/g, "XX")); // zastępowanie z użyciem podstawienia // $2 zostanie zastąpione wartością // pierwszego pod-wyrażenia, // czyli fragmentu ujętego w nawiasach console.log( z.replace(/^([^=]*)=.*$/, '$1') + " = " + z.replace(/^[^=]*=(.*)$/, '$1') + "\n" ); var a = 7, b = 0xf, c = 010, d = 0b11; echo("0b" + a.toString(2)) echo("0o" + b.toString(8)) echo(c.toString()) echo("0x" + d.toString(16))
<b>aa</b>), znaczniki bez wartości mogą być samo-zamykające (np. <g />).
Dokumenty HTML mogą być zgodne z wymogami formalnymi XML tym samym stanowiąc dokumenty XML.// wymaga pobrania biblioteki nagłówkowej rapidxml (http://rapidxml.sourceforge.net/) #include "rapidxml.hpp" namespace rapidxml { namespace internal { // fix bug in rapidxml https://sourceforge.net/p/rapidxml/bugs/16/ template<class V, class C> V print_children(V, const xml_node<C>*, int, int); template<class V, class C> V print_element_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_data_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_cdata_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_declaration_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_comment_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_doctype_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_pi_node(V, const xml_node<C>*, int, int); }} #include "rapidxml_print.hpp" #include <iostream> #include <string.h> int main() { char xmlString[1024]; strncpy(xmlString, "<a>" "<b>A<h>qwe ... rty</h></b>" "ABCD...HIJ..." "<c x=\"q\" w=\"p p\">EE F</c>" "<g y=\"zz\" />" "<c x=\"pp\">123 <d rr=\"oo\">456</d> 78 90.</c>" "</a>", 1024 ); // utworzenie obiektu drzewa XMLowego w oparciu o napis rapidxml::xml_document<> xmlDoc; xmlDoc.parse<0>(xmlString); // pobranie głównego węzła rapidxml::xml_node<>* xmlRoot = xmlDoc.first_node(); std::cout << "nazwa głównego elementu to: " << xmlRoot->name() << "\n"; std::cout << "jego zawartość tekstowa to: " << xmlRoot->value() << "\n"; std::cout << "jego wartość to: {{{{" << *xmlRoot << "}}}}\n"; std::cout << "jego potomkowie to: \n"; rapidxml::xml_node<>* xmlNode = xmlRoot->first_node(); while(xmlNode) { std::cout << " " << xmlNode->name() << " : "; rapidxml::print( std::cout, *xmlNode, rapidxml::print_no_indenting ); // rapidxml::print() może zapisywać także do napisu std::cout << "\n"; xmlNode = xmlNode->next_sibling(); } std::cout << "pierwszy węzeł c ma atrybuty:\n"; xmlNode = xmlRoot->first_node("c"); if(xmlNode) { rapidxml::xml_attribute<>* xmlAtrib = xmlNode->first_attribute(); while(xmlAtrib) { std::cout << xmlAtrib->name() << " = " << xmlAtrib->value() << "\n"; xmlAtrib = xmlAtrib->next_attribute(); } } // modyfikacje dokumentu: // zmiana nazwy i zawartości elementu xmlNode = xmlRoot->first_node("g"); if (xmlNode) { xmlNode->name("noweGG"); xmlNode->value("!@#$"); } // zmiana nazwy i wartości atrybutu xmlNode = xmlRoot->first_node("c"); if(xmlNode) { rapidxml::xml_attribute<>* xmlAtrib = xmlNode->first_attribute(); while(xmlAtrib) { if (xmlAtrib->name() == std::string("w")) { xmlAtrib->name("uu"); xmlAtrib->value("1 2 3"); break; } xmlAtrib = xmlAtrib->next_attribute(); } } // usuwanie wszystkich potomków ostatniego <c> xmlNode = xmlRoot->last_node("c"); xmlNode->remove_all_nodes(); xmlNode->value(""); // usuwanie wszystkich atrybutów ... xmlNode->remove_all_attributes(); // są też funkcje usuwające wskazanego potomka lub atrybut: // remove_node() i remove_attribute() // dodawanie atrybutów xmlNode->append_attribute( xmlDoc.allocate_attribute("abc", "098") ); xmlNode->append_attribute( xmlDoc.allocate_attribute("qwe", "...") ); // powyższa metoda dodaje na koniec, można także dodawać na początek: // prepend_attribute() lub na wskazaną pozycję insert_attribute() // dodawanie potomków rapidxml::xml_node<>* xmlNode2; xmlNode2 = xmlDoc.allocate_node(rapidxml::node_data, NULL); xmlNode2->value("ert"); xmlNode->append_node(xmlNode2); xmlNode2 = xmlDoc.allocate_node(rapidxml::node_element, "kk"); xmlNode2->value("uio"); xmlNode->append_node(xmlNode2); xmlNode2 = xmlDoc.allocate_node(rapidxml::node_data, NULL); xmlNode2->value("bnm"); xmlNode->append_node(xmlNode2); // podobnie jak atrybuty nody też możemy dodawać na początku // prepend_node() lub na dowolnej pozycji insert_node() // wypisanie zmienionego dokumentu std::cout << xmlDoc; }
import xml.etree.ElementTree as xml # Python oferuje także inne niz ElementTree moduły do obsługi XML # także wspierające DOM d = """<a> <b>A<h>qwe ... rty</h></b> ABCD... &' HIJ... <c x="q" w="p p">EE FĄ</c> <g y="zz" /> <c x="pp">123 <d rr="oo">456</d> 78 90.</c> </a>""" rootNode = xml.fromstring(d) # pobieranie informacji z dokumentu print("nazwa głównego elementu to:", rootNode.tag) print("jego zawartość tekstowa to:", rootNode.text) print("jego pełna zawartość tekstowa to:", "".join(rootNode.itertext())) print("jego wartość to: {{{{", xml.tostring(rootNode, encoding="unicode") ,"}}}}") print("jego potomkowie to:") for c in rootNode: print(" ", c.tag, ":", xml.tostring(c, encoding="unicode")) print("pierwszy węzeł c ma atrybuty:") try: ci = rootNode.iter("c") print(next(ci).attrib) except StopIteration: print(" [brak takiego węzła]") # modyfikacje dokumentu # zmiana nazwy i zawartości elementu try: ci = rootNode.iter("g") cc = next(ci); cc.tag = "noweGG" cc.text = "!@#$" except StopIteration: pass # zmiana nazwy i wartości atrybutu try: ci = rootNode.iter("c") cc = next(ci); del cc.attrib["w"] cc.attrib["uu"] = "1 2 3" except StopIteration: pass try: cc = next(ci); # usuwanie wszystkich potomków drugiego <c> cc.clear() # jest też remove() które usuwa wskazany element # usuwanie wszystkich atrybutów ... cc.attrib.clear() se = xml.SubElement(cc, "kk", attrib={"aa":str(45)}) se.text = "uio" se = xml.SubElement(cc, None) se.text = "bnm" # możliwe jest też wstawienie w podanym miejscu se = xml.Element(None) se.text = "ert" cc.insert(0, se) except StopIteration: pass print(xml.tostring(rootNode, encoding="unicode"))
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl"> <head> <meta http-equiv="ContentType" content="application/xhtml+xml; charset=utf-8" /> </head><body> <div id="xyz"> <p> <b id="abc">Lorem <i>ipsum</i></b> dolor sit amet, <i>consectetur</i> adipiscing elit. <ul class="typA typX"> <li>Sed congue, eros quis ultricies ornare,</li> <li>massa sem auctor arcu, et semper ex arcu id augue.</li> <li>Fusce pretium <i><b>turpis</b></i> massa, maximus <i>dapibus</i> sit amet.</li> <li>Nulla fermentum molestie finibus.</li> </ul>Etiam accumsan <i>tempus</i> ante at congue.</p> <p data-abc="123" data-zz="0" class="az" id="q23y"> Sed feugiat vestibulum sapien eget iaculis.<br /> Integer quis magna nec lacus tempor sagittis. </p> </div> <script type="text/javascript">//<![CDATA[ function fff() { console.log("wszystkie elementy <i> w dokumencie:"); var a = document.getElementsByTagName("i"); for (var j = 0; j < a.length; j++) { console.log(a[j].innerHTML + "\n"); } // modyfikacja zawartości elementu o id="abc" var b = document.querySelector("#abc"); b.innerHTML = "ABC <u>ABC</u> ABC"; // potomkowie rodzica wskazanego elementu var child = b.parentNode.firstElementChild while (child) { console.log("potomek jest węzłem: " + child.nodeName + "\n"); if (child.nodeName == "ul") { // jeżeli jest to lista <ul> // która posiada (wśród klass podanych w atrybucie class) klasę "typA" // to ją usuń i dodaj klasę "typB" if (child.classList.contains('typA')) { child.classList.remove('typA'); child.classList.add('typB'); } } child = child.nextElementSibling; } var c = document.querySelector("#xyz"); // drugi potomek typu <p> elementu określonego w zmiennej c var d = c.querySelector("p:nth-of-type(2)"); console.log("ma atrybuty:") for (var i = 0; i < d.attributes.length; ++i) { console.log(" " + d.attributes[i].name + " = " + d.attributes[i].value + "\n"); } // można też pytać o konkretny atrybut console.log("abc => " + d.getAttribute("data-abc")); // oraz ustawić (zarówno dodać nowy jak i zmienić istniejący) d.setAttribute("data-def", "tyui"); } addEventListener('DOMContentLoaded', fff, false); //]]></script> </body></html>
import json from pprint import pprint a='''{ "info": "bbb", "ver": 31, "d": [ {"a": 21, "b": {"x": 1, "y": 2}, "c": [9, 8, 7]}, {"a": 17, "b": {"x": 6, "y": 7}, "c": [6, 5, 4]} ] }''' # interpretacja napisu jako zbioru danych w formacie json d = json.loads(a) # wypisanie zbioru danych pprint(d) # pprint ładnie formatuje złożone zbiory danych # jak widać jest to zagnieżdżona struktura list i słowników # odpowiadająca 1 do 1 temu co było w napisie # dostęp do poszczególnych elementów: "po pythonowemu" print(d["d"][1]["b"]) print(d["d"][1]["b"]["x"]) print(d["d"][1]["c"][1]) # przygotowaniem nowego zbioru danych b={'aa': 'pp', 'b': [3,4,5], 'c':d} b['e']="test" b['f']={'a':1, 'b':2} # wygenerowanie json-owego tekstu w oparciu o niego c=json.dumps(b,indent=1,ensure_ascii=False) print(c) # dla porównania: pprint(b)
# dla kodu pythonowego a="""print("ppp") b=13-17 """ exec(a) print(b) # dla wyrażeń a="9+7" b = eval(a) print(b) # lub exec("c="+a) print(c) # oczywiście napis może pochodzić z # dowolnego źródła, może być też # treścią całego skryptu pythonowego: # exec(open("plik.py").read())
# dla kodu bashowego a='echo "ppp"; b="ooo"' eval $a echo $b # dla obliczeń arytmetycznych a="13+17" b=$(( $a )) echo $b # alternatywnie przez # zewnętrzny kalkulator np.: b=`echo $a | bc` echo $b
<?php $a='echo "aaa"; $b="cc";'; eval($a); echo "\n" . $b . "\n"; $a='2+6'; eval('$b='. $a .';'); echo $b . "\n"; ?>
// dla kodu javascript a="console.log('AAA'); b='www';" eval(a); console.log("b to: " + b + "\n"); // dla wyrażeń a="5+7" b=eval(a) console.log("b to: " + b + "\n");
// Już w powyższych przykładach użycia list i map w C++ // wykorzystane były iteratory, pozwalające na // pobieranie kolejnych wartości z tych kontenerów: void wypiszListe1(std::list<int> l) { for (std::list<int>::iterator i = l.begin(); i != l.end(); ++i) { std::cout << *i << "\n"; } } // Iterator zwracają niektóre z metod tych kontenerów, // np. .begin() zwraca iterator na pierwszy element. // Zwiększanie iteratora odbywa się z użyciem operatora ++ // Wyjście poza zakres (zwiększenie iteratora wskazującego na // ostatni element kolekcji) nie powoduje rzucenia wyjątku, // za to iterator przyjmuje specjalną wartość oznaczającą koniec. // Iterator o tej wartości zwracany jest przez metodę .end() // (lub \cpp{.rend()} przy iterowaniu w przeciwną stronę). // Przy używaniu iteratorów w C++ wygodne jest korzystanie // z typu auto. Typ ten zwalnia programistę z konieczności // jawnego definiowania typu zmiennej do której przypisywana // jest od razu jakaś wartość z określonym typem. // Można napisać np. `auto x = 5;`, ale nie możemy napisać: // `auto x; x = 5;` void wypiszListe2(std::list<int> l) { for (auto i = l.begin(); i != l.end(); ++i) { std::cout << *i << "\n"; } } // C++ udostępnia także inną składnię pętli for pozwalającą // na iterowanie po wszystkich elementach kolekcji takich jak // listy, mapy, itp. I upraszczającą powyższy zapis do postaci: void wypiszListe3(std::list<int> l) { for (auto i : l) { std::cout << i << "\n"; } } // Zamiast `auto i` można napisać `auto& i` aby otrzymać dostęp // przez referencję (wtedy wykonanie przypisania wartości do i, // np. `i = 0`, spowoduje modyfikację elementu listy). // Warto zauważyć także, że w odróżnieniu od wcześniejszej pętli // zmienna reprezentuje wyłuskany iterator (jest to wartość / // referencja do wartości elementu a nie sam iterator).
# niekiedy zamiast tworzenia listy lepsze może być # uzyskiwanie jej kolejnych elementów "na żywo" # funkcjonalność taką w pythonie zapewniają generatory: def f(l): a, b = 0, 1 for i in range(l): r, a, b = a, b, a + b yield r # użycie generatora w pętli for for i in f(16): print(i) # można także tworzyć generatory nieskończone def ff(): a, b = 0, 1 while True: r, a, b = a, b, a + b yield r # pobieranie kolejnych elementów a = iter( ff() ) print( next(a) ) print( next(a) )
grep in.txt | sort > out.txt (grep wybiera linie spełniające kryteria, a sort sortuje wynik)#include <stdio.h> int main() { // // wyjście / wyjście w stylu C // // wykorzystywane już wcześniej funkcje // puts i printf korzystają z stdout puts("Hello world"); printf("0x%x == %d == %.3f\n", 13, 13, 13.0); // jeżeli chcemy korzystać z innego strumienia // należy użyć wariantu pozwalającego na podanie // pliku do którego ma się odbywać zapis: // stdout - standardowe wyjście // stderr - standardowe wyjście błędu fputs("Hello world 2", stderr); fprintf(stderr, "0x%x == %d == %.3f\n", 13, 13, 13.0); // wczytanie napisu z standardowego wejścia: char napis[10]; fgets( napis, 10, stdin ); // wczytany napis będzie miał 9 znaków + NULL-end // zapominamy o reszcie inputu jeżeli był dłuższy niż 9 znaków if (napis[8] !='\n') { int c; while((c = getchar()) != '\n' && c != EOF); } // wczytanie liczby z standardowego wejścia: int d; fscanf (stdin, "%i", &d); fprintf(stdout, "liczba: %d napis: \"%s\"\n", d, napis); }
#include <iostream> #include <limits> #include <iomanip> int main() { // // wyjście / wyjście w stylu C++ // // standardowe wyjście i standardowe // wyjście błędu jako strumienie C++ std::cout << "Hello C++ world\n"; std::cerr << "Hello C++ world 2\n"; // strumienie pozwalają na proste wypisywanie zmiennych, // ale jeżeli chcemy formatować wyjście to // pisania jest więcej niż w printf int d = 1363; std::cout << "d=" << d << " d/3=" << d/3.0 << "\n"; std::cout << std::setprecision(3) << 1.2342; std::cout << " " << 23.567 << "\n"; // wczytanie napisu z standardowego wejścia: char napis[10]; std::cin.getline(napis, 10); if (std::cin.fail()) { // zapominamy o reszcie jeżeli była std::cin.clear(); std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' ); } // wczytanie liczby z standardowego wejścia: std::cin >> d; std::cout << "liczba to: " << d; std::cout << " napis to: \"" << napis << "\"\n"; }
import sys # wypisywanie na standardowe wyjście print("ABC: " + str(12) + " = " + hex(12)) # bez nowej linii na końcu print("abc", end='') # wypisywanie na standardowe wyjście błędu print("QWE", file=sys.stderr) # odczyt z wejścia li = iter(sys.stdin) liniaA = next(li) liniaB = next(li) print("l1: " + liniaA + "l2: " + liniaB) # można też z użyciem metody czytającej jedną linię liniaA = sys.stdin.readline() liniaB = sys.stdin.readline() print("l1: " + liniaA + "l2: " + liniaB) # należy zauważyć że dane wczytywane są # liniami i zawierają znak końca linii # można także w ramach: # for l in sys.stdin: # # lub skorzystać z metody wczytującej cały plik # jako listę poszczególnych jego linii: # ll = sys.stdin.readlines() # bądź po prostu jako jeden napis # napis = sys.stdin.read() # jest też: info = input("Wpisz coś: ") print(info)
f1() { # wypisywanie na standardowe wyjście echo "Hello World" printf "%d %.3f\n" 123 13.15686 # wypisywanie na standardowe wyjście błędu echo "Hello World 2" > /dev/stderr echo "ABC" > /dev/stderr } f2() { # odczyt ze standardowego wejścia # read wczytuje dane z stdin do podanych zamiennych # w taki sposób że do kolejnych zmiennych trafiają # kolejne słowa (napisy rozdzielane spacją lub # tabulatorem), a do ostatniej zmiennej reszta napisu # (do końca linii bez znaku końca linii) while read a b; do echo $(($a+$b)) done } # przekierowania strumieni standardowych: # wyjście z echo przekierowywane jest na wejście f2 echo -e "2 3\n1 6" | f2 # wyjścia f1 do odpowiednich plików f1 > /tmp/out.txt 2> /tmp/err.txt # użycie >> zamiast > spowoduje dopisywanie do pliku # zamiast nadpisywania jego zawartości # połączonych wyjść (normalnego i błędu) f1 do grep f1 |& grep -v Hello # standardowe wyjście może zostać przechwycone i # podstawione w danym miejscu poprzez użycie # `polecenie` lub $(polecenie) echo XXX `ls -ld /tmp` echo XXX $(ls -ld /tmp) # często też chcemy zignorować standardowe wyjście i/lub # standardowe wejście - możemy to uzyskać przekierowując # je do /dev/null np: grep '^root:' /etc/passwd > /dev/null # przekierowanie wyjścia cat do pliku /tmp/liczby # << EOF powoduje że bash podaje na standardowe # wejście komendy (w tym wypadku "cat") dane czytane # z skryptu (lub swojego wejścia) dopóki nie wystąpi # w nowej linii słowo podane po << (w tym # wypadku EOF), jeżeli słowo to jest ujęte w '' to # w przekazywanym tekście nie są dokonywane podstawienia # shellowe (np. rozwijane zmienne) cat << EOF > /tmp/liczby 1 3 13 9 9 4 7 10 EOF # przekierowanie pliku /tmp/liczby # na standardowe wejście f2 f2 < /tmp/liczby
ze względu na specyfikę tego języka wszystko co jest poza znacznikami php będzie wypisywane na standardowe wyjście <?php echo "są także funkcje "; print("realizujące wypisywanie\n"); printf("%d %.3f\n", 13, 45.33221); ?>
#include <stdio.h> int main() { // // obsługa plików w stylu C // // otwieramy plik określony w pierwszym argumencie, // w trybie określonym w drugim argumencie: // r - odczyt, w - zapis, a - dopisywanie, // + - dwukierunkowy (używane po r, w albo a) FILE *plik = fopen("/tmp/plik1.txt", "w+"); // zapisujemy do pliku fputs("Hello World !!!\n", plik); fprintf(plik, "%.3f\n", 13.13131); // jako że są to operacje buforowane to aby mieć // pewność że to jest już w pliku należy wykonać // fflush(), nie jest to konieczne gdy zamykamy // plik (wtedy wykonywane jest z automatu) fflush(plik); int poz = ftell(plik); printf("aktualna pozycja w pliku to %d\n", poz); // przewijamy do początku // jest to równoważne rewind(plik); fseek(plik, 0, SEEK_SET); // wczytywanie z pliku char napis[10]; fgets(napis, 10, plik); // wczytany napis będzie miał 9 znaków + NULL-end puts(napis); // powrót do poprzedniej pozycji fseek(plik, poz, SEEK_SET); // operacje binarne - w ten sposób możemy zapisywać // i odczytywać całe bufory z pamięci, czyli także napisy // zapis do pliku double x = 731.54112, y = 12.2; fwrite(&x, 1, sizeof(double), plik); fflush(plik); // przesuniecie do pozycji na której zapisywaliśmy fseek(plik, poz, SEEK_SET); // i odczyt z pliku ... fread(&y, 1, sizeof(double), plik); printf("zapisano: %f, odczytano: %f\n", x, y); // dostępne są także funkcje read() i write() działające w oparciu o // numeryczny deskryptor pliku uzyskiwany np. z funkcji open() // a nie obiekt FILE uzyskiwany z fopen() fclose(plik); }
#include <iostream> #include <fstream> int main() { // // obsługa plików w stylu C++ // // tworzymy strumień związany z plikiem std::fstream plik_w; // otwieramy plik w trybie dopisywania // ale pozwalającym na modyfikowanie obecnej treści // aby to działało musi być to strumień in/out plik_w.open( "/tmp/plik2.txt", std::fstream::ate|std::fstream::out|std::fstream::in ); // tworzymy strumień (typu in) związany z plikiem ... std::ifstream plik_r; // otwieramy plik do czytania plik_r.open("/tmp/plik1.txt"); // tryby możemy budować z następujących opcji: // std::ios::in - odczyt (domyślna dla ifstream) // std::ios::out - zapis (domyślna dla ofstream) // std::ios::ate - ustawie pozycji na koniec pliku // std::ios::app - dopisywanie bez możliwości zmiany zawartości // std::ios::trunc - nadpisuje plik std::cout << "Pozycja w pliku OUT: " << plik_w.tellp() << std::endl; std::cout << "Pozycja w pliku IN: " << plik_r.tellg() << std::endl; // pisanie i czytanie dokładnie jak dla cin cout ... plik_w << "Hello World !!!"; // przesunięcie na 16 bajt bliku wejściowego plik_r.seekg(16); // i wczytanie czegoś double x; plik_r >> x; std::cout << "wczytano: " << x << "\n"; // przesunięcie o 2 bajty do tyłu i wczytanie plik_r.seekg(-2, std::ios::cur); // inne tryby przesunięć to: // ios::beg - od początku pliku (domyślny) // ios::end - od końca pliku plik_r >> x; std::cout << "wczytano: " << x << "\n"; plik_w.seekp(-3, std::ios::end); plik_w << 123 << "\n"; // nma koniec zamykamy pliki plik_w.close(); plik_r.close(); }
import os.path # otwarcie pliku do odczytu f=open("/etc/passwd", "r") # funkcja pozwala na określenie kodowania pliku poprzez argument nazwany "encoding" # (np. encoding='utf8'), domyślne kodowanie zależne jest od ustawień systemowych # i można je sprawdzić poprzez locale.getpreferredencoding() # jeżeli plik ma być otwarty w trybie binarnym a nie tekstowym # konieczne jest podanie flagi b w ramach drugiego argumentu # odczyt po linii l1 = f.readline() l2 = f.readline() # można także czytać z jawnym użyciem iteratorów: li = iter(f) l3 = next(li) l4 = next(li) print("l1: " + l1 + "l2: " + l2 + "l3: " + l3 + "l4: " + l4) # albo w ramach pętli i = 5 for l in f: print(str(i) + ": " + l) i += 1 # powrót na początek pliku f.seek(0) # odczyt jako tablica linii ll = f.readlines() print(ll) # i kolejny raz ... jako jednolity tekst f.seek(0) print( f.read() ) f.close() # # tworzenie pliku oraz dopisywanie # # jeżeli plik istnieje to: if os.path.isfile("/tmp/plik3.txt"): # otwieramy w trybie do zapisu i odczytu # i ustawiamy się na końcu pliku celem dopisywania f=open("/tmp/plik3.txt", "r+") f.seek(0, 2) else: f=open("/tmp/plik3.txt", "w") # pobieramy aktualną pozycje w pliku # (która w tym wypadku jest równa długości pliku) pos = f.tell() # jeżeli plik ma więcej niż 5 bajtów if pos > 5: # to cofamy się o 3 f.seek(pos-3) f.write("0123456789") f.close() # # obsługa plików binarnych # # wymagane jest dodanie flagi b w flagach funkcji open(): f=open("/tmp/plik1.txt", "rb") # czytanie baj po bajcie while True: b = f.read(1) if b == b"": break print(b) f.close()
import sys, os, select rdfd, _, _ = select.select([sys.stdin], [], [], 3.0) # select() przyjmuje 3 listy deskryptorów plików # (czyli numerycznego identyfikatora otwartego pliku, # zwracanego np. przez funkcję open()) oraz ilość sekund, # którą ma czekać na początek danych. # Pierwsza lista związana jest z plikami z których chcemy # czytać, druga pisać, a trzecia z plikami na których # czekamy na wyjątkowe warunki. # Funkcja ta kończy działanie gdy pojawią się jakiekolwiek dane # (nie czeka na koniec danych – EOF) i zwraca również 3 takie listy, # ale zawierające jedynie deskryptory plików na których pożądana # operacja jest możliwa (np. są dane do wczytania, można zapisać dane). if not rdfd: print("czas minął") for fd in rdfd: print("czytam z:", fd) a = os.read(fd.fileno(), 1024) # Do odczytu zastosowana została funkcja os.read() a nie metoda fd.read(), # wynika to z faktu, iż fd.read() czeka na EOF lub podaną ilość bajtów, # a os.read() wczytuje to co jest dostępne i ogranicza jedynie maksymalną # ilość wczytywanych danych (resztę możemy doczytać kolejnym wywołaniem). print("wczytałem:", a)
// program korzysta z mechanizmów systemów POSIX // i może nie działać na niekompatybilnych platformach #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <stdio.h> #define _MMAP_MODE_ MAP_PRIVATE // MAP_SHARED // MAP_PRIVATE powoduje bez widoczności zmian w mapowanym pliku dla innych procesów // MAP_SHARED pozwala współdzielić zamapowany obszar z innymi procesami, // jest wymagane aby móc zmodyfikować plik z użyciem msync() int main() { const char* fname = "/tmp/aaa.txt"; int fsize; // pobieramy rozmiar pliku struct stat st; if (stat(fname, &st)) { if (errno == ENOENT) { printf("plik \"%s\" nie istnieje\n", fname); fsize = 0; } else { fprintf(stderr, "Error in stat(): %s\n", strerror(errno)); // powyższe zadziała jak perror() return -1; } } else { fsize = st.st_size; } // otwieramy plik w trybie zapis-odczyt uzyskując deskryptor // (jeżeli plik nie istnieje zostanie utworzony z prawami 600) int fd = open(fname, O_RDWR|O_CREAT, (mode_t)0600); int bufSize = fsize + 20; // jeżeli chcemy operować na większym buforze niż rozmiar pliku musimy powiększyć plik if (ftruncate(fd, bufSize)) perror("Error in ftruncate()"); // operacja ta nie ma wpływu na zajmowane miejsce na dysku: // int fd = open("/tmp/x", O_RDWR|O_CREAT); ftruncate(fd, 100000000); close(fd); // utworzy plik /tmp/x którego rozmiar wg `ls` będzie 98MB a wg `du` będzie 0 // alternatywnie można: // lseek(fd, bufSize-1, SEEK_SET); write(fd, "", 1); lseek(fd, 0, SEEK_SET) // mapujemy plik do pamięci ... w trybie odczytu i zapisu char *buf = (char*)mmap(NULL, bufSize, PROT_READ | PROT_WRITE, _MMAP_MODE_, fd, 0); if (buf == MAP_FAILED) { perror("Error in mmap()"); return -1; } buf[fsize]=0; printf("Długość pliku: %d\n", fsize); printf("Zawartość pliku: %s\n", buf); // modyfikujemy przedostatni bajt w pliku // (jeżeli trafimy na fragment wielo-bajtowego znaku to go popsujemy) if (fsize > 2) buf[fsize - 2]='X'; fsize += snprintf(buf+fsize, bufSize-fsize, "Ala ma kota\n\n"); fsize += snprintf(buf+fsize, bufSize-fsize, "Kot ma Alę\n\n"); fsize += snprintf(buf+fsize, bufSize-fsize, "czyżby ...\n\n"); // do pliku chcemy zapisać "fsize" bajtów z "buf", czyli: buf[0] ... buf[fsize-1] // nie chcemy zapisać kończącego napis znaku NULL (dodawanego przez snprintf), // jeżeli nie doszło do przepełnienia bufora nie jest on wliczany w długość "fsize" // i znajduje się w buf[fsize], zatem nie ma konieczności jawnego pomijania go // // jeżeli natomiast doszło do przepełnienia bufora jest on ostatnim znakiem w buforze, // czyli buf[bufSize-1] ... zatem aby go pominąć należy ustawić fsize = bufSize -1 if (fsize >= bufSize) { fprintf(stderr, "Error: przepełnienie bufora\n"); fsize = bufSize -1; } printf("Nowa długość pliku: %d\n", fsize); printf("Nowa zawartość pliku: %s\n", buf); // ustawiamy prawidłowy rozmiar pliku if (ftruncate(fd, fsize)) perror("Error in ftruncate()"); #if _MMAP_MODE_ == MAP_SHARED // synchronizujemy zawartość bufora pamięci do pliku if (msync(buf, fsize, MS_SYNC)) perror("Error in msync()"); #else // zapisujemy zmiany do pliku if (write(fd, buf, fsize) < 0) perror("Error in write()"); #endif // odmapowanie pliku z pamięci if (munmap(buf, fsize)) perror("Error in munmap()"); // zamknięcie pliku close(fd); }
// przy kompilacji konieczne jest dodanie: // -lboost_filesystem -lboost_system #include <iostream> #include <boost/filesystem.hpp> int main() { // sprawdzenie czy plik istnieje boost::filesystem::path pFile( "/tmp/abc.txt" ); if (boost::filesystem::is_regular_file(pFile)) std::cout << "istnieje\n"; else std::cout << "nie istnieje\n"; // listowanie katalogu i sprawdzanie typów boost::filesystem::path pDir( "/tmp/" ); boost::filesystem::directory_iterator end_iter; boost::filesystem::directory_iterator iter(pDir); for(; iter != end_iter; ++iter) { if (boost::filesystem::is_regular_file(iter->path())) { std::cout << iter->path().filename().generic_string() << " jest plikiem\n"; // wypisujemy tylko nazwę pliku } else if (boost::filesystem::is_directory(iter->path())) { std::cout << iter->path().generic_string() << " jest katalogiem\n"; // wypisuje,y pełną ścieżkę } else { std::cout << iter->path().generic_string() << " jest czymś innym\n"; } } boost::filesystem::rename("/tmp/a.txt", "/tmp/b.txt"); boost::filesystem::remove("/tmp/b.txt"); }
import os # sprawdzenie czy plik istnieje if os.path.isfile("/tmp/abc.txt"): print("istnieje") else: print("nie istnieje") # listowanie katalogu i sprawdzanie typów dl = os.listdir("/tmp/") print (dl) for f in dl: if os.path.isfile(f): print("\"" + f + "\" jest plikiem") elif os.path.isdir(f): print("\"" + f + "\" jest katalogiem") else: print("\"" + f + "\" jest czymś innym") # można także rekurencyjnie wraz z podkatalogami for currDir, dirs, files in os.walk('/tmp'): print("podkatalogi \"" + currDir + "\":") for d in dirs: print(" " + os.path.join(currDir, d)) print("pliki w \"" + currDir + "\":") for f in files: print(" " + os.path.join(currDir, f)) # zmiana nazwy os.rename("/tmp/a.txt" "/tmp/b.txt") # usuwanie os.remove("/tmp/b.txt")
# sprawdzenie czy plik istnieje if [ -f "/tmp/abc.txt" ]; then echo "istnieje" else echo "nie istnieje" fi # listowanie katalogu i sprawdzanie typów for f in /tmp/*; do if [ -f "$f" ]; then echo "\"$f\" jest plikiem" elif [ -d "$f" ]; then echo "\"$f\" jest katalogiem" else echo "\"$f\" jest czymś innym" # np. kolejką lub urządzeniem ... fi done # alternatywne listowanie katalogu ls /tmp | while read f; do echo "/tmp zawiera: $f" done # zmiana nazwy mv /tmp/a.txt /tmp/b.txt # usuwanie rm /tmp/b.txt
ls -la to to samo co ls -l -a), a opcji długich po dwóch myślnikach. Ewentualne argumenty opcji podawane są zaraz po opcji i w przypadku opcji krótkich oddzielone od niej spacją, a w przypadku opcji długich po znakiem równości (np. ls --color=auto). Po opcjach występują argumenty pozycyjne (np. lista plików które ma wyświetlić ls).
/* Argumenty linii poleceń (w tym nazwa pod którą został uruchomiony program) przekazywne są do funkcji main jako dwa argumenty: 1) liczba całkowita określająca ilość argumentów, typowo nazywany `argc` 2) tablica napisów, typowo nazywany `argv` */ #include <stdio.h> int main(int argc, char *argv[]) { printf("%d %s\n", argc, argv[0]); return 0; }
// przy kompilacji konieczne jest dodanie: // -lboost_program_options #include <boost/program_options.hpp> #include <iostream> // pełna deklaracja funkcji main dla systemów POSIX // funkcja main otrzymuje: // - liczbę elementów tablicy argumentów // - tablicę argumentów (jest to tablica napisów typu C) // - tablicę zmiennych środowiskowych int main(int argc, char *argv[], char *envp[]) { std::cout << "program wywołano z " << argc << " argumentami\n"; for (int i=0; i<argc; ++i) std::cout << "argument " << i << " to: " << argv[i] << "\n"; // deklaracja opcji linii poleceń boost::program_options::options_description desc("Program options"); desc.add_options() ("help,h", "show help message") ("load", boost::program_options::value<std::string>(), "load from \"arg\" file") ; // parsowanie opcji linii poleceń wraz z obsługą wyjątków boost::program_options::variables_map vm; try { boost::program_options::store( boost::program_options::parse_command_line(argc, argv, desc), vm ); boost::program_options::notify(vm); } catch(boost::program_options::error& e) { std::cerr << "Cmdline args error: " << e.what() << "\n\n"; std::cerr << "Use --help to see full options description\n"; return 2; } // przetwarzanie otrzymanych opcji if (vm.count("help")) { std::cout << desc << "\n"; } else if (vm.count("load")) { std::cout << "Load from: " << vm["load"].as<std::string>() << "\n"; } else { std::cout << "Wywołano bez opcji\n"; } // jest też funkcja getopt_long() z biblioteki standardowej C // funkcja main powinna zwracać kod powrotu // typowo: gdy program zakończy się powodzeniem zero // a gdy zakończy się błędem (nie zerowy) kod błędu return 0; }
import argparse # parser agumentów linii poleceń # do argumentów można się też dostawać bezpośrednio poprzez sys.argv: # len(sys.argv) - ilość elementów tej tablicy # str(sys.argv[0]) - napis odpowiadający nazwie wywołania programu # str(sys.argv[1]) - napis odpowiadający pierwszemu argumentowi programu parser = argparse.ArgumentParser(description='Opis programu') parser.add_argument( 'ARG', default='ABC', nargs='?', help='argument pozycyjny (opcjonalny z wartością domyślną)' ) parser.add_argument( '-v', "--verbose", action="store_true", help='opcja typu przełącznik' ) parser.add_argument( "--input", action="store", help='opcja z argumentem' ) parser.add_argument( "--vector", action="store", metavar='V', nargs='*', help='opcja z wieloma argumentami' ) args = parser.parse_args() print(args)
# przetworzenie argumentów określonych w wywołaniu getopt # dalsze argumenty dostępne są w $1, $2, itd po zakończeniu pętli # : -> poprzedzająca opcja wymaga argumentu # :: -> poprzedzająca opcja może mieć argument # uwaga o ile wymagane argumenty mogą być rozdzielane od opcji spacją # o tyle opcjonalne nie (krótkie należy podawać zaraz po opcji, # długie rozdzielając =) eval set -- "`getopt -o xy:z:: -l opcja-x,opcja-y:,opcja-z:: -- "$@"`" while true; do case $1 in -x|--opcja-x) echo "X"; shift 1;; -y|--opcja-y) echo "Y, argument \`$2'"; shift 2;; -z|--opcja-z) echo "Z, argument \`$2'"; shift 2;; # tuataj $2 może być pusty (gdy nie podano), ale jest zdefiniowany --) shift; break;; esac done # inną komendą mogącą służyć przetwarzaniu opcji skryptu jest getopts
<?php /* możliwe jest używanie php w trybie command-line jednak ze względu na specyfikę i podstawowe zastosowania tego języka jest to przypadek dość egzotyczny dlatego też zamiast obsługi opcji przekazywanych z linii poleceń w przypadku skryptów php większy sens ma pokazanie obsługi opcji przekazywanych w żądaniu HTTP */ // wysłanie ciasteczka // musi być przed wysłaniem // jakiejkolwiek treści $val = time(); $val = "tmp $val"; setcookie("Ciasteczko", $val); echo '<pre>'; echo "wysłałem ciasteczko: $val\n"; // tablica zawierajaca zmienne // przekazane w adresie URL echo '$_GET: '; print_r($_GET); // tablica zawierajaca zmienne // przekazane wi treści żądania echo '$_POST: '; print_r($_POST); // tablica zawierajaca zmienne // przekazane w ciasteczkach echo '$_COOKIE: '; print_r($_COOKIE); echo '</pre>'; ?>
#include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <fcntl.h> #include <stdio.h> void getInput(int fd, int pid); int main() { // najprostszą metodą uruchomienia innego programu jest funkcja system int retcode = system("ls -l"); printf("Program uruchomiony przez system() zwrócił kod powrotu: %d\n", WEXITSTATUS(retcode) ); // jeżeli chcemy odebrać standardowe wyjście // (albo zapisać standardowe wejście) można skorzystać z popen FILE* p = popen("uname -a", "r"); puts("Proces wypisał na swój stdout:"); char buf[256]; size_t s; while ((s = fread(buf, sizeof(char), sizeof(buf), p))) { fwrite(buf, sizeof(char), s, stdout); } // tworzymy łącza nie nazwane - rury ... int toSys[2]; if ( pipe(toSys) == -1 ) { perror("Error in pipe(toSys)"); // perror wypisuje podany komunikat na stderr dodając do niego opis // błędu określonego przez errno, można też pobrać identyfikator błędu // lub jego opis - szczegóły w man 3 errno return 1; } // toSys[0] - odbiór z rury // toSys[1] - wysyłanie przez rurę int fromSys[2]; if ( pipe(fromSys) == -1 ) { perror("Error in pipe(fromSys)"); return 1; } // rozgałęziamy proces int pid; switch (pid = fork()) { case -1: { // funkcja zwróciła -1 co oznacza błąd perror("Error in fork"); return 1; } case 0: { // funkcja zwróciła zero co oznacza że jesteśmy w procesie potomnym // podmieniamy stdin i stdout potomka na odpowiednie // koniec łącz nienazwanych dup2(toSys[0], 0); dup2(fromSys[1], 1); // zamykamy końce łącz nienazwanych (te które używamy są już dostępne // jako stdin, stdout, innych nie potrzebujemy) close(toSys[0]); close(toSys[1]); close(fromSys[0]); close(fromSys[1]); // zastępujemy się innym procesem (w tym wypadku gerp dopasowujący napisy // zawierające znak z zakresu A-C) - w taki sposób w jaki robi to system() execl("/bin/sh", "sh", "-c", "grep --line-buffered '[A-C]'", (char *) 0); // oczywiście zamiast wywoływania innego programu można tutaj użyć własnego // kodu pracującego równolegle z programem głównym } default: { // funkcja zwróciła coś innego od 0 i -1 oznacza to że jesteśmy w procesie // macierzystym i otrzymaliśmy pid naszego dziecka ... // zamykamy niepotrzebne konce rur close(toSys[0]); close(fromSys[1]); // rury możemy obsługiwać jak pliki // i w ten spsób obsługiwana jest końcówka do zapisu FILE *pout = fdopen(toSys[1], "w"); fprintf(pout, "Ala ma kota\n"); fprintf(pout, "Kot ma coś\n"); fflush(pout); // można też je obsługiwać posługując się bezpośrednio deskryptorem // i tak obsługiwana jest rura do czytania // rurę do czytania ustawiamy w trybie nie blokującym fcntl(fromSys[0], F_SETFL, fcntl(fromSys[0], F_GETFL, 0) | O_NONBLOCK); // czytanie zostało wyniesione jest do osobnej funkcji getInput() // celem łatwiejszego jego powtarzania getInput(fromSys[0], pid); // potomek może potrzebować trochę czasu na przetworzenie danych sleep(1); getInput(fromSys[0], pid); // zamykamy nasz koniec rury do pisania // pozwoli to potomkowi czekającemu na input zakończyć się fclose(pout); // sprawdzamy czy jest jeszcze jakiś input // (konieczne np. gdy grep bez --line-buffered) sleep(1); getInput(fromSys[0], pid); // zamykamy rurę do czytania close(fromSys[0]); // czekamy na zakończenie potomka int ppid = wait(&retcode); printf("potomek o PID=%d zakończył się z kodem %d\n", ppid, WEXITSTATUS(retcode) ); } } } void getInput(int fd, int pid) { char* buf[256]; int len, gLen = 0; while ((len = read(fd, buf, 255)) > 0) { buf[len] = 0; gLen += len; printf("Odebrano od potomka %d:\n>>>>>\n%s\n<<<<<\n", pid, buf); } printf("Łącznie odebrano: %d\n", gLen); }
# napis który będzie podawany na standardowe wejście uruchamianego polecenia inStr = "Ala ma kota\nKot ma psa\n..." # Python pozwala na bezpośrednie stosowanie funkcji znanych z języka C # takich jak system(), popen(), fork(), execl() itd poprzez moduł os. # Odbywa się to w sposóba analogiczny do użycia w języku C. Na przykład: import os os.system('echo -en "' + inStr + '" | grep -v A') # Python zapewnia jednak także wygodny, zunifikowany sposób uruchamiania innych # programów / poleceń powłoki poprzez moduł subprocess oraz rozgałęziania # własnego procesu poprzez moduł multiprocessing. print(""" # # podstawy subprocess # """) import subprocess # uruchamiamy subprocess z grep'em res = subprocess.run(["grep", "-v", "A"], input=inStr.encode(), stdout=subprocess.PIPE) print("Kod powrotu to: " + str(res.returncode)) print("Standardowe wyjście z komendy to: " + res.stdout.decode()) # warto zwrócić uwagę na kodowanie i dekodowanie napisów # (przekazywanych/odbieranych przez stdin/stdout) do / z utf-8 # jeżeli chcemy korzystać np. z znaków uogólniających powłoki lub podać # komendę jako pojedynczy napis (a nie listę argumentów) to można użyć # opcji shell=True: subprocess.run(["ls -ld /etc/pa*"], shell=True) # jeżeli potrzebujemy tylko rozbicia napisu na listę argumentów można # użyć shlex.split() # run() pozwala także (obok subprocess.PIPE) na przekazywanie # istniejących deskryptorów (lub subprocess.DEVNULL, co ignoruje wyjście) # w ramach stdin, stdout, stderr # moduł subprocess oferuje także funkcję Popen() dającą większą kontrolę # nad uruchamianiem komendy print(""" # # podstawy multiprocessing # """) import multiprocessing, os, time def fun1(txt, st, cnt): for _ in range(cnt): print("Proces", os.getpid(), "z st =", st, " i argumentem:", txt) time.sleep(st) def fun2(c, e): # obsługa rury i zdarzenia c.send("Uwolnić mrożone truskawki") time.sleep(1.3) c.send([12, 13, { "ab" : 11, "cd" : "xx" }]) c.close() e.clear() if __name__ == '__main__': # przygotowanie potomków p1 = multiprocessing.Process(target=fun1, args=('Ala ma psa',1.5,4,)) p2 = multiprocessing.Process(target=fun1, args=('pies ma kota',1,3,)) print("mój pid to", os.getpid(), "... uruchamiam procesy") # uruchomienie potomków p1.start() p2.start() # czekanie na zakończenie p2 p2.join() print("p2 się zakończył") # wymuszenie zakończenia p1 jeżeli nadal żyje if p1.is_alive(): print("p1 żyje") p1.terminate() p1.join() print("p1 się zakończył") # rura i event # Pipe() tworzy nam parę obiektów multiprocessing.Connection # jednego będziemy używać w procesie głównym, a drugiego w potomnym pConn, cConn = multiprocessing.Pipe() # tworzymy obiekt typu Event ... jest to prosty sygnalizator # z flagą binarną zapewniający atomowość wykonywanych operacji evt = multiprocessing.Event() evt.set() # tworzymy i uruchamiamy kolejny podproces p1 = multiprocessing.Process(target=fun2, args=(cConn,evt,)) p1.start() # odbieramy dane z połączenia dopóki event jest ustawiony try: while evt.is_set(): if pConn.poll(0.1): print("otrzymałem: ", pConn.recv()) except EOFError: pass print(""" # # pool i bariera w multiprocessing # """) import multiprocessing, queue, os, time from threading import BrokenBarrierError def fun5Init(b): global bariera bariera = b def fun5(arg): for i in range(arg['n']): print("Wątek", arg['n'], " iter =", i) time.sleep(arg['ts']) try: print("Wątek", arg['n'], " czekam na innych") if bariera.wait() == 0: print("wątki zsynchronizowane") # więcej nie używaj tej bariery: bariera.abort() except BrokenBarrierError: print("czekanie anulowane") return arg['n'] + arg['ts'] if __name__ == '__main__': # tworzymy barierę i warunek b = multiprocessing.Barrier(3) # tworzymy pool'a ... w ramach inicjalizacji # przekazujemy barierę i warunek pool = multiprocessing.Pool( processes=3, initializer=fun5Init, initargs=(b,) ) # tworzymy listę argumentów dla funkcji # uruchamianej w procesach potomuych args = [ { 'n': 3, 'ts': 0.44 }, { 'n': 13, 'ts': 0.17 }, { 'n': 2, 'ts': 0.33 }, { 'n': 7, 'ts': 0.69 }, { 'n': 4, 'ts': 0.49 }, ] # tworzymy pulę 3 procesów potomnych i uruchamiamy w nich wskazaną # funkcję z kolejnymi zbiorami argumentów z podanej listy # uwaga: funkcja wywoływana przez map wraz z ewentualnymi zmiennymi # globalnymi musi być zdefiniowana przed wywołaniem Pool() results = pool.map(fun5, args) pool.close() pool.join()
pipe() a otwarcia specjalnego pliku (utworzonego mkfifo()) przez dwa procesy (jeden do czytania drugi do pisania).kill $PID) i ma odpowiednie do tego zachowania domyślne. Proces może zablokować lub zdefiniować własną obsługę większości (ale nie wszystkich) sygnałów.
#include <stdio.h> #include <signal.h> #include <fcntl.h> #include <time.h> #include <unistd.h> #include <errno.h> // funkcje obsługi sygnału void obslugaSygnalu(int sig) { printf("Otrzymałem sygnał numer: %i\n", sig); } void akcjaSygnalu(int sig, siginfo_t *info, void *context) { printf("Otrzymałem sygnał numer: %i z kodem %d i wartością %d od pid=%d\n", sig, info->si_code, info->si_value.sival_int, info->si_pid); // si_code określa powód wysłania sygnału } int main() { int sig, pid; pid = getpid(); printf("Mój PID=%d\n", pid); // sygnały są asynchronicznym mechanizmem jednokierunkowej komunikacji // systemy POSIXowe oferują dwa rodzaje sygnałów - standardowe i czasu rzeczywistego // sygnały czasu rzeczywistego (w odróżnieniu od standardowych przekazujących jedynie // numer sygnału) zawierają także kod i wartość oraz są priorytetyzowane // (niższy numer => wyższy priorytet => dostarczany w pierwszej kolejności) // do zdefiniowania obsługi sygnału standardowego (do jego przechwycenia) służy funkcja: signal(SIGINT, &obslugaSygnalu); // pojawia się m.in. w efekcie Ctrl+C signal(SIGTSTP, &obslugaSygnalu); // pojawia się m.in. w efekcie Ctrl+Z // oczywiście można definiować różne funkcje dla różnych sygnałów // zamiast wskaźnika do funkcji możemy podać także SIG_IGN albo SIG_DFL // co oznacza odpowiednio ignorowanie tego sygnału lub jego domyślną obsługę // możliwe jest też maskowanie sygnałów sigset_t sigSet; // inicjujemy maskę sygnałów na pustą, na pełną byłoby sigfillset() if (sigemptyset(&sigSet)) perror("Error in sigemptyset()"); // dodajemy sygnał do maski // można także usuwać - sigdelset() i sprawdzać czy jest obecny - sigismember() // zamaskowany SIGTERM jest domyślnym sygnałem wysyłanym przez kill if (sigaddset(&sigSet, SIGTERM)) perror("Error in sigaddset()"); // ustawiamy maskę sygnałów na przygotowaną if (sigprocmask(SIG_SETMASK, &sigSet, NULL)) perror("Error in sigprocmask()"); // sygnały nie obsługiwane i nie blokowane przez proces obsługiwane są w sposób domyślny // na ogół prowadząc do zakończenia procesu ... sygnałów SIGKILL (wysyłanego przez kill -9) // oraz SIGSTOP nie można przechwycić ani zamaskować // do zdefiniowania obsługi sygnału czasu rzeczywistego służy funkcja sigaction wraz z strukturą // sigaction opisującą obsługę (można tak definiować także obsługę standardowych sygnałów): struct sigaction rtSigAct; // funkcja obsługi (moża także podać SIG_IGN albo SIG_DFL) rtSigAct.sa_sigaction = &akcjaSygnalu; // flagi ... SA_SIGINFO oznacza funkcję przyjmującą 3 argumenty a nie 1 jak w signal() rtSigAct.sa_flags = SA_SIGINFO; // maska sygnałów blokowanych w trakcie obsługi sygnału ... blokujemy wszystko co się da sigfillset(&rtSigAct.sa_mask); // ustawienie obsługi sigaction(SIGRTMIN, &rtSigAct, NULL); // wysłanie sygnału czasu rzeczywistego // (funkcja pozwala też na wysyłanie standardowych sygnałów) sigqueue(pid, SIGRTMIN, (const union sigval)123); // oczekiwanie na sygnał: puts("Czekam na dowolny sygnał"); pause(); // dodajemy SIGINT do zbioru if (sigaddset(&sigSet, SIGINT)) perror("Error in sigaddset()"); puts ("Czekam na sygnał z podanej maski"); puts (" inne będą obsługiwane ale funkcja nie zakończy się ..."); printf(" aby kontynuować Ctrl+C lub kill %d\n", pid); if (sigwait(&sigSet, &sig)) perror("Error in sigwait()"); printf("Czekanie zakończone po otrzymaniu sygnału: %d\n", sig); puts ("Śpię przez 10s ... dowolny sygnał przerwie"); sleep(10); // jest też usleep() dla mikro sekund puts ("Czekam przez 10.5s na sygnał z podanej maski"); siginfo_t sigInfo; struct timespec timeOut; timeOut.tv_sec = 10; timeOut.tv_nsec = 500000000; sig = sigtimedwait(&sigSet, &sigInfo, &timeOut); if (sig<0) { if (errno == EINTR) puts("otrzymałem inny sygnał"); else if (errno == EINVAL) puts("timeout"); else perror("Error in sigtimedwait()"); } else { printf("Czekanie zakończone po otrzymaniu sygnału %d od pid=%d\n", sig, sigInfo.si_pid ); } // jest też sigwaitinfo działająca jak sigtimedwait ale bez timeout'u }
import os, signal pid = os.getpid() print("Mój PID:", pid); # obsługa sygnału def obslugaSygnalu(s, f): print("Otrzymałem sygnał numer:", s) # SIGINT - pojawia się m.in. w efekcie Ctrl+C signal.signal(signal.SIGINT, obslugaSygnalu) # SIGTSTP - pojawia się m.in. w efekcie Ctrl+Z signal.signal(signal.SIGTSTP, obslugaSygnalu) print("Czekam na dowolny sygnał") signal.pause() print("Czekam na sygnał z podanego zbioru") # w odróżnieniu od C inne będą obsługiwane dopiero po otrzymaniu # sygnału ze zbioru (zostaną zakolejkowane) sig = signal.sigwait([signal.SIGTERM, signal.SIGINT]) print("Czekanie zakończone po otrzymaniu sygnału:", sig) print("Czekam przez 10.5s na sygnał z podanego zbioru") # w odróżnieniu od C inny sygnał jest obsługiwany, # ale nie przerywa czekania sig = signal.sigtimedwait([signal.SIGTERM, signal.SIGINT], 10.5) if sig == None: print("timeout"); else: print("Czekanie zakończone po otrzymaniu sygnału:", sig.si_signo, "od pid:", sig.si_pid )
// przy kompilacji konieczne jest dodanie: // -lrt -lpthread #include <stdio.h> #include <mqueue.h> #include <errno.h> #include <time.h> #include <string.h> #include <unistd.h> void getMsg(union sigval sv) { puts("kolejka nie jest już pusta"); // tu mógłby być realizowany odbiór wiadomości z kolejki } int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "%s /nazwa 1|2\n", argv[0]); return 1; } char runType = argv[2][0]; // kolejki wiadomosci POSIX (wiecej man 7 mq_overview) // kolejka może mieć zarówno wielu producentów jak i odbiorców // raz odebrana wiadomość znika z kolejki // otwarcie kolejki struct mq_attr atrybuty; atrybuty.mq_maxmsg=3; // pojemność kolejki - 3 wiadomości atrybuty.mq_msgsize=20; // po 20 bajtów każda mqd_t msgQ = mq_open( argv[1], // nazwa zasobu O_RDWR|(runType == '1' ? O_CREAT|O_EXCL : 0), // flagi 0770, // prawa dostępu &atrybuty ); if (msgQ == (mqd_t) -1) { perror("Error in mq_open()"); return -1; } char buf[20]; unsigned priorytet; struct timespec timeout; clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec+=2; // odbieramy wiadomość z timeoutem ... // wiadomosci odbierane są w kolejności priorytetów, a następnie wysłania ssize_t size = mq_timedreceive(msgQ, buf, 20, &priorytet, &timeout); // mq_receive() - wersja bez timeoutu // mamy także możliwość odbioru bez timeoutu bądź zażądania powiadomienia // o wpisaniu pierwszej wiadomości do niepustej kolejki - mq_notify() if (size > 0){ printf("odebrałem %d bajtów z priotytetem %d: %s\n", size, priorytet, buf); } else { if (errno == ETIMEDOUT) puts("timeout"); else perror("Error in mq_timedreceive()"); } // ustawienie powiadamiania o pojawieniu się wiadomości w niepustej kolejce struct sigevent sev; sev.sigev_notify = SIGEV_THREAD; sev.sigev_notify_function = getMsg; sev.sigev_notify_attributes = NULL; if (mq_notify(msgQ, &sev) == -1) { perror("Error in mq_notify()"); } // wysyłamy wiadomość do kolejki // funkcja ta zawiesi proces gdy nie ma miejsca w kolejce która nie jest // otwarta z O_NONBLOCK na czas określony przez timeout // mq_send() - wersja bez timeout'u snprintf(buf, 20, "info A od: %d", getpid()); clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec+=2; if (mq_timedsend(msgQ, buf, strlen(buf), 9, &timeout)) perror("Error in mq_timedsend()"); printf("Wysłano: %s\n", buf); if (runType == '1') { snprintf(buf, 20, "info B od: %d", getpid()); if (mq_send(msgQ, buf, strlen(buf), 9)) perror("Error in mq_send()"); printf("Wysłano: %s\n", buf); sleep(20); mq_unlink(argv[1]); // w przypadku gdyby program zakończył się bez wykonania mq_unlink() // (np. na sutek kill -9) konieczne jest ręczne usunięcie plików z /dev/mqueue } }
print(""" # # kolejki w multiprocessing # """) import multiprocessing, multiprocessing.managers, queue, os, time def fun3(q): # obsługa kolejki ... zakończenie można także realizować # np. w oparciu o dane otrzymane w kolejce while True: if not q.empty(): el = q.get(block=False) # można także uruchamiać blokujący get() # z timeoutem lub bez ... if el[1] == None: break; else: print("potomek otrzymał", el) time.sleep(0.77) if __name__ == '__main__': # tworzymy kolejkę # moduł multiprocessing zawiera swoje implementacje kolejki FIFO # (Queue, SimpleQueue i JoinableQueue), których można bezpiecznie # używać pomiędzy procesami: # q = multiprocessing.Queue() # # chcemy jednak użyć kolejki priorytetowej z modułu queue # (oferuje także kolejki FIFO (Queue) i LIFO (LifoQueue)) # aby móc z niej poprawnie korzystać w multiprocessing # musimy użyć jej poprzez SyncManager'a: class Manager(multiprocessing.managers.SyncManager): pass Manager.register("PrioQueue", queue.PriorityQueue) m = Manager() m.start() q = m.PrioQueue() # dodajemy elementy do kolejki q.put((100, "delfin ma psa ;-)")) q.put((70, [13, 17])) q.put((200, [15, { "ab" : 11, "cd" : "xx" }])) # tworzymy i uruchamiamy kolejny podproces p1 = multiprocessing.Process(target=fun3, args=(q,)) p1.start() # wkładanie danych do kolejki time.sleep(0.404) print("dodaje nowe wiadomości do kolejki ...") q.put((13, "ABC ... i co dalej")) q.put((180, "bla bla bla ...")) # informacja dla potomka o zakończeniu obsługi kolejki print("czekam na opróżnienie kolejki ...") while q.qsize() > 0: time.sleep(0.333) # kończenie q.put((999, None)) p1.join() m.shutdown()
int blokada=false;
zrownoleglijKod();
if (!blokada) {
blokada=true;
sekcjaKrytyczna();
blokada=false;
}
Niestety takie rozwiązanie nie gwarantuje skuteczności blokady, gdyż:// przy kompilacji konieczne jest dodanie: // -lrt -lpthread #include <stdio.h> #include <semaphore.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> #include <errno.h> #include <time.h> #define BUF_SIZE 128 int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "%s nazwa 1|2\n", argv[0]); return 1; } int retcode = 1; char runType = argv[2][0]; // pamięć dzielona przez shm i mmap (więcej `man 7 shm_overview`) // można by także przez mmap i zwykły plik // 1) utworzenie zasobu (pliku) SHM // jeżeli runType == '1' i zasób istnieje to wywołanie zakończy się błędem // ja jeżeli nie istnieje zostanie utworzony int shmFd = shm_open( argv[1], // nazwa zasobu O_RDWR|(runType == '1' ? O_CREAT|O_EXCL : 0), // flagi 0770 // prawa dostępu ); if (shmFd<0) { perror("Error in shm_open()"); return 1; } // 2) ustalenie wielkości if (ftruncate(shmFd, BUF_SIZE)) { perror("Error in ftruncate()"); goto END_1; } // 3) zamapowanie zasobu SHM w pamięci void *addr = mmap(0, BUF_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, shmFd, 0); if (addr == MAP_FAILED) { perror("Error in mmap()"); goto END_1; } // semafory POSIX (więcej `man 7 sem_overview`) // 1) utworzenie semafora // jeżeli runType == '1' i semafor istnieje to wywołanie zakończy się błędem sem_t* sem = sem_open( argv[1], // nazwa zasobu O_RDWR|O_CREAT|(runType == '1' ? O_EXCL : 0), // flagi 0770, // prawa dostępu 1 // wartość semafora ); if (sem == SEM_FAILED) { perror("sem_open"); goto END_1; } int* c = (int*) addr; if (runType == '1') { *c = 0; } // próba wejścia w kod zabezpieczony semaforem if (sem_trywait(sem)) { if (errno == EAGAIN) puts("semafor nie wpuszcza ... trzeba spróbować później"); else perror("Error in sem_trywait()"); } else { // semafor został opuszczony ... jesteśmy w sekcji krytycznej *c = *c + 0x01; printf("c = 0x%x\n", *c); sleep(3); // śpimy, to nieładnie spać w sekcji krytycznej, ale to tylko demo ... // podniesienie semafora if (sem_post(sem)) perror("Error in sem_post()"); } sleep(5); // czekanie na możliwość wejścia w kod zabezpieczony semaforem z timeout'em struct timespec timeout; clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec+=10; if (sem_timedwait(sem, &timeout)) { if (errno == ETIMEDOUT) puts("timeout ... trzeba spróbować później"); else perror("Error in sem_timedwait()"); } else { // semafor został opuszczony ... jesteśmy w sekcji krytycznej *c = *c + 0x0100; printf("c = 0x%x\n", *c); sleep(5); // śpimy, to nieładnie spać w sekcji krytycznej, ale to tylko demo ... // podniesienie semafora if (sem_post(sem)) perror("Error in sem_post()"); } sleep(3); // czekamy do skutku na możliwość wejścia w sekcję krytyczną if (sem_wait(sem)) { perror("Error in sem_timedwait()"); } else { // semafor został opuszczony ... jesteśmy w sekcji krytycznej *c = *c + 0x010000; printf("c = 0x%x\n", *c); sleep(5); // śpimy, to nieładnie spać w sekcji krytycznej, ale to tylko demo ... // podniesienie semafora if (sem_post(sem)) perror("Error in sem_post()"); } retcode = 0; // o ile w większości przypadków zwalnianie i zamykanie zasobów w momencie // kończenia programu nie ma znaczenia (zasoby są automatycznie odbierane przez // system - pamięć będzie zwolniona, pliki, gniazda sieciowe pozamykane, itd) // to w przypadku zasobów współdzielonych może mieć istotne znaczenie // np. jeżeli nie usuniemy zasobu SHM o danej nazwie nie będzie go można później // otworzyć gdy używamy flag O_CREAT|O_EXCL // // dlatego w powyższym kodzie w obsłudze błędów używamy goto do sekcji kończącej: END_2: if (runType == '1') { // usunięcie semafora (stanie się niedostępny także dla innych procesów) sem_unlink(argv[1]); } END_1: // odmapowanie zasobu z pamięci munmap(addr, BUF_SIZE); // zamknięcie zasobu SHM close(shmFd); if (runType == '1') { // usunięcie zasobu SHM (stanie się niedostępny także dla innych procesów) shm_unlink(argv[1]); } // w przypadku gdyby jednak program zakończył się bez wykonywania tego kodu // (np. na sutek kill -9) konieczne jest ręczne usunięcie plików z /dev/shm }
print(""" # # współdzielenie danych w multiprocessing # """) import multiprocessing, queue, os, time def fun4(lock, l, c): # obsługa zmiennej warunkowej for i in range(6): # blokujemy dostęp do listy c.acquire() # modyfikujemy zmienną na której mamy warunek l[0] = i # powiadamiamy wszystkich o tym c.notify_all() # zwalniamy dostęp do listy c.release() time.sleep(0.13) # obsługa locka i listy while True: if lock.acquire(timeout=0.5): # jeżeli udało się założyć lock'a ... l[1] += 1 print("podprocess:", l) time.sleep(0.77) # zdjęcie lock'a lock.release() time.sleep(0.33) else: print("podprocess: can't lock") if __name__ == '__main__': # tworzymy locka'a, czyli blokadę wejścia do sekcji krytycznej # (zmieniającej dane współdzielone) lock = multiprocessing.Lock() # oprócz zwykłych Lock'ów Python oferuje także: # * RLock - Lock w którym wątek zakładający blokadę może ją zakładać # kolejne razy bez jej uprzedniego zwalniania, tzn sekwencja: # l = RLock(); # l.acquire(); l.acquire(); # cos(); # l.release(); l.release(); # jest poprawna (przy zwykłym Lock'u zatrzyma się na drugim # l.acquire()) # * Semaphore - Lock z licznikiem dozwolonych wejść do sekcji # krytycznej np. l = Semaphore(value=3) pozwoli na 3 wywołania # l.acquire() (bez l.release()) # tworzymy współdzieloną listę ... na menagerze m = multiprocessing.Manager() l = m.list([0, 1, 2, 3]) # tworzymy zmienną warunkową używającą lock'a lock c = multiprocessing.Condition(lock) # tworzymy i uruchamiamy kolejny podproces p1 = multiprocessing.Process(target=fun4, args=(lock,l,c,)) p1.start() print("czekamy na spełnienie warunku ..."); c.acquire() while l[0] < 4: print("czekamy ... l[0]=", l[0]) c.wait() c.release() print("warunek spełniony ... l[0] = ", l[0]); time.sleep(0.17) # obsługa listy for i in range(3): # założenie lock'a print("process: czekam na lock ...") lock.acquire() # dodanie elementu do listy i wypisanie l.append(13 + i) print("process:", l) time.sleep(0.69) # zdjęcie lock'a lock.release() time.sleep(1.0) # zakończenie p1.terminate() p1.join() m.shutdown()
fork() możliwe jest także tworzenie wątków (zwanych tez lekkimi procesami) w ramach procesu. Podstawowa różnica między procesem a wątkiem polega na tym iż wszystkie wątki (w ramach danego procesu) współdzielą całość pamięci (przestrzeni adresowej), podczas gdy każdy z procesów posiada niezależną pamięć i co najwyżej jakiś współdzielony z innymi fragment.
Każdy z wątków posiada natomiast niezależny stos (umieszczony we wspólnej przestrzeni adresowej), który jest używany m.in. do przechowywania zmiennych lokalnych (w tym argumentów funkcji). Jednak ze względu na umieszczenie tych danych w wspólnej przestrzeni adresowej są one także dostępne dla innych wątków (poprzez wskaźnik lub referencję).
// przy kompilacji konieczne jest dodanie: // -lrt -lpthread #include <pthread.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <string.h> struct watekInfo { pthread_mutex_t* mut; pthread_cond_t* cond; const char* napis; int licznik; }; void wypisz1(struct watekInfo* wi) { int i, l = -99, ret; for(i=0; i<4; i++) { // próbujemy zablokować wejście na ten kawałek kodu // można czekać do skutku przy pomocy pthread_mutex_lock() if ((ret = pthread_mutex_trylock(wi->mut)) != 0) { fprintf(stderr, "Wątek: error in pthread_mutex_trylock(): %s\n", strerror(ret) ); } else { wi->licznik += 2; l = wi->licznik; pthread_mutex_unlock(wi->mut); } printf("Wątek: %s %d\n", wi->napis, l); usleep(600000); } sleep(8); pthread_exit(0); // kończy działanie wątku } void wypisz2(struct watekInfo* wi) { int i; printf("To watek: %s\n", wi->napis); pthread_mutex_lock(wi->mut); for(wi->licznik=0; wi->licznik<10; wi->licznik++) { printf("."); fflush(stdout); if (wi->licznik==5) pthread_cond_signal(wi->cond); sleep(1); } pthread_mutex_unlock(wi->mut); // zabraniamy przerwania watku w trakcie ponizszego wypisywania pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); for(i=0; i<10; i++) { printf("."); fflush(stdout); sleep(1); } printf("\n"); // a teraz już może być anulowywany pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); // ale od teraz tylko w wyznaczonych momentach // na funkcjach takich jak sleep, system, ... // czy tez pthread_testcancel() pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); sleep(50); puts("koniec wypisz2"); pthread_exit(0); } int main() { int i, l, ret; // przygotowanie mutex'a pthread_mutexattr_t mutAttr; pthread_mutexattr_settype(&mutAttr, PTHREAD_MUTEX_ERRORCHECK); // możemy ustawić: // * PTHREAD_MUTEX_ERRORCHECK aby mechanizm reagował błędem na // unlockowanie zalokowanego przez inny wątek mutexu itp, // * PTHREAD_MUTEX_RECURSIVE aby blokada nałożona przez wątek A // nie obowiazywala watku A (umożliwia to korzystanie z // mutexów w funkcjach rekurencyjnych) // * PTHREAD_MUTEX_NORMAL lub PTHREAD_MUTEX_DEFAULT // flagi te mogą się nieco różnić w zależności od stosowanej // biblioteki i użytych #define szczegółów należy szukać // w plikach features.h pthread.h oraz dokumentacji man pthread_mutexattr_setprotocol(&mutAttr, PTHREAD_PRIO_NONE); // poza nic nie zmieniającym PTHREAD_PRIO_NONE mamy także: // * PTHREAD_PRIO_INHERIT zabezpieczający wątki przed inwersją // priorytetów (priorytet po wejściu do sekcji krytycznej // jest podwyższany do najwyższego z priorytetów procesów // czekających // * PTHREAD_PRIO_PROTECT zabezpieczający również przed // zakleszczeniem dzięki podnoszeniu tego priorytetu powyżej // najwyższego z priorytetów procesów czekających // // do zapobiegania inwersji priorytetów przydatne jest też: // pthread_mutexattr_setprioceiling(); // inicjalizujemy mutex (dla domyślnych ustawień można podać // NULL jako drugi argument) pthread_mutex_t mut; pthread_mutex_init(&mut, &mutAttr); // przygotowanie danych dla wątku struct watekInfo wInfo; wInfo.napis = "Ala ma kota"; wInfo.licznik = 10; wInfo.mut = &mut; // tworzymy nowy watek uruchomiając w nim funkcje wypisz1 // i przekazując jako pierwszy argument tej funkcji wInfo pthread_t watek; if ((ret = pthread_create( &watek, 0, (void* (*)(void *))wypisz1, (void*)&wInfo ) ) != 0 ) { fprintf(stderr, "Error in pthread_create(): %s\n", strerror(ret)); return 1; } for(i=0; i<7; i++) { pthread_mutex_lock(wInfo.mut); wInfo.licznik -= 1; printf("Program główny: %d\n", wInfo.licznik); pthread_mutex_unlock(wInfo.mut); sleep(1); } #ifdef _GNU_SOURCE // pobranie atrybutu wątku // i sprawdzenie czy jest wątkiem procesu czy systemu pthread_attr_t atrybuty; if ((ret = pthread_getattr_np(watek, &atrybuty))) { fprintf(stderr, "Error in pthread_getattr_np(): %s\n", strerror(ret)); return 1; } if ((ret = pthread_attr_getscope(&atrybuty, &i))) { fprintf(stderr, "Error in pthread_attr_getscope(): %s\n", strerror(ret)); return 1; } if (i == PTHREAD_SCOPE_PROCESS) { puts("wątek procesu"); } else if (i == PTHREAD_SCOPE_SYSTEM) { puts("wątek systemu ... będzie widoczny w ps -m:"); system("ps -m"); } else { puts("inny wątek :-o"); } #endif // możemy oczekiwać na zakończenie wątku poprzez wywołanie // w innym funkcji pthread_join() umożliwia ona także // uzyskanie kodu z jakim zakończył się wątek printf("Program główny czeka na koniec wątku\n"); if ((ret = pthread_join(watek, NULL))) { fprintf(stderr, "Error in pthread_join(): %s\n", strerror(ret)); return 1; } printf("Wątek zakończył się\n"); // jej wywołanie jest konieczne aby wątek po zakończeniu // był w całości usunięty, chyba że wątek został określony // jako taki do którego nie można się dołączyć przez wywołanie // pthread_detach() lub podanie przy tworzeniu flagi // PTHREAD_CREATE_DETACHED // przygotowujemy zmienną warunkową pthread_cond_t cond; pthread_cond_init(&cond, NULL); // aktualizujemy strukturę danych dla wątku wInfo.napis = "drugi"; wInfo.licznik = 0; wInfo.cond = &cond; // tworzymy kolejny wątek if ((ret = pthread_create( &watek, 0, (void* (*)(void *))wypisz2, (void*)&wInfo ) ) != 0 ) { fprintf(stderr, "Error in pthread_create(): %s\n", strerror(ret)); return 1; } // czekamy ... while (wInfo.licznik < 5) pthread_cond_wait(wInfo.cond, wInfo.mut); // pthread_cond_wait zwalnia tak na prawdę na chwile podany // do niego mutex a po odebraniu pthread_cond_signal na // dozorowanym przez niego warunku ponownie go zamyka printf(" jesteśmy za warunkiem "); fflush(stdout); sleep(7); printf(" zabijamy wątek "); // i go kończymy z zewnątrz // w zależności od ustawień wątku anulowanie wątku dzieje // się natychmiastowo albo w jednym z punktów anulowania // zobacz funkcje to ustawiające w wypisz2() pthread_cancel(watek); // wypada też odebrać jego kod powrotu pthread_join(watek, NULL); puts("a teraz watek już nie żyje ..."); // usunięcie mutex'a pthread_mutex_destroy(wInfo.mut); // usunięcie zmiennej warunkowej pthread_cond_destroy(wInfo.cond); }
// przy kompilacji konieczne jest dodanie: // -lboost_system -lboost_thread -lpthread #include <boost/thread/mutex.hpp> #include <boost/thread/thread.hpp> #include <queue> #include <string> #include <iostream> std::queue<std::string> kolejka; class Watek1 { Watek1(const Watek1&); public: int abc; boost::mutex mutex; // boost oferuje także mutx'a pozwalającego na tworzenie kolejnych // blokad przez wątek który utworzył pierwszą - boost::recursive_mutex Watek1() {} void operator()() { for (int i=0; i<8; i++) { if (mutex.try_lock()) { abc += 1; std::cout << "wątek, abc= " << abc << std::endl; mutex.unlock(); } else { std::cout << "wątek nie dostałem mutex'a ..." << std::endl; } boost::this_thread::sleep(boost::posix_time::millisec(500)); } for (int i=0; i<8; i++) { boost::this_thread::sleep(boost::posix_time::millisec(700)); boost::mutex::scoped_lock scoped_lock(mutex); // powyższy lock (na mutexie tego wątku) // blokuje od teraz do końca bloku if (!kolejka.empty()) { std::cout << i << ") odebrałem z kolejki \ " << kolejka.front() << std::endl; kolejka.pop(); } else { std::cout << i << ") kolejka pusta" << std::endl; } } } }; int main() { // przygotowanie danych wątku Watek1 wf1; wf1.abc=56; // uruchomienie wątku ... // boost::ref() potrzebne bo obiekty klasy Watek1 są niekopiowalne boost::thread watek1(boost::ref(wf1)); boost::this_thread::sleep(boost::posix_time::millisec(1500)); // manipulacja danymi wątku wf1.mutex.lock(); wf1.abc=13; boost::this_thread::sleep(boost::posix_time::seconds(1)); wf1.mutex.unlock(); // obsługa kolejki wf1.mutex.lock(); kolejka.push("Ala ma kota"); kolejka.push("Kot ma Ale"); wf1.mutex.unlock(); std::cout << "2 pierwsze elementy są w kolejce" << std::endl; boost::this_thread::sleep(boost::posix_time::seconds(4)); wf1.mutex.lock(); kolejka.push("Abecadlo"); kolejka.push("z pieca spadlo"); wf1.mutex.unlock(); std::cout << "2 kolejne elementy sa w kolejce" << std::endl; // czekamy na zakończenie wątku watek1.join(); }
import threading, time # UWAGA: # w standardowej implementacji CPython w danym # czasie tylko jeden wątek może wykonywać # kod pythonowy # # w efekcie stosowanie wątków ma sens tylko dla # wątków czekających na coś, a nie ma sensu dla # wątków mających wykonywać się równolegle # (na różnych rdzeniach) # # w takim przypadku należy używać pełnego # rozgałęziania procesu (moduł multiprocessing) def fun(txt, st): # używamy lokalnej pamięci dla danego wątku daneWatku = threading.local() for daneWatku.i in range(4): print(txt, daneWatku.i) time.sleep(st) # uruchomienie wątku t = threading.Thread(target=fun, args=("AA",2,)) t.start(); # można także zdefiniować timer który # uruchomi wątek po zadanym czasie tim = threading.Timer(3, fun, args=("BB",1.2,)) tim.start() # dla wątków można stosować także mechanizmy # synchronizacji takie jak dla multiprocessing # # ponadto wątki można także obsługiwać przez # interfejs identyczny z multiprocessing # poprzez multiprocessing.dummy
#include <sys/socket.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define BUF1_SIZE 256 #define BUF2_SIZE 64 const char* getAddresInfo( struct sockaddr_storage *srcAddr, socklen_t srcAddrLen, char* buf, int bufLen ); int main(int argc, char *argv[]) { char buf1[BUF1_SIZE], buf2[BUF2_SIZE]; int ret, pid; if (argc != 3 && argc != 4) { fprintf(stderr, "USAGE: %s dstHost dstPort [listenPort]\n", argv[0]); return EXIT_FAILURE; } // struktura zawierająca adres na który wysyłamy // użycie funkcji getaddrinfo pozwala na zapewnienie obsługi zarówno IPv4 jak i IPv6, // a także podawanie adresów w postaci numerycznej bądź przy użyciu nazw // alternatywnie można ręcznie przygotowywać struktury sockaddr_in / sockaddr_in6 // z użyciem np. htons(), inet_aton(), ... struct addrinfo *dstAddrInfo; if((ret = getaddrinfo(argv[1], argv[2], NULL, &dstAddrInfo)) != 0) { fprintf(stderr, "Error in getaddrinfo(): %s\n", gai_strerror(ret)); return EXIT_FAILURE; } // tutaj mogliśmy uzyskać kilka adresów, // ale optymistycznie zakładamy że pierwszy będzie OK // utworzenie gniazda sieciowego ... SOCK_DGRAM oznacza UDP // (na gnieździe z SOCK_DGRAM standardowo nie będzie widać błędów ICMP // związanych np. z niedostępnością docelowego hosta lub portu) int sfd = socket(dstAddrInfo->ai_family, SOCK_DGRAM, 0); if (sfd<0) { perror("Error in socket()"); return EXIT_FAILURE; } if (argc == 4) { // jeżeli podane opcjonalne argumentu to określamy na jakim porcie słuchamy if (dstAddrInfo->ai_family == AF_INET6) { struct sockaddr_in6 listenAddr; listenAddr.sin6_family=AF_INET6; listenAddr.sin6_port=htons(atoi(argv[3])); // host to network short (porządek bajtów), są też dla long i network to host listenAddr.sin6_addr=in6addr_any; // in6addr_any oznacza że nasłuchujemy na każdym adresie IP danego hosta // (zamiast tego można określić konkretny adres) if(bind(sfd, (struct sockaddr *) &listenAddr, sizeof(struct sockaddr_in6))) { perror("Error in bind()"); return EXIT_FAILURE; } } else if (dstAddrInfo->ai_family == AF_INET) { struct sockaddr_in listenAddr; listenAddr.sin_family=AF_INET; listenAddr.sin_port=htons(atoi(argv[3])); // host to network short (porządek bajtów), są też dla long i network to host listenAddr.sin_addr.s_addr=INADDR_ANY; // INADDR_ANY oznacza że nasłuchujemy na każdym adresie IP danego hosta // (zamiast tego można określić konkretny adres) if(bind(sfd, (struct sockaddr *) &listenAddr, sizeof(struct sockaddr_in))) { perror("Error in bind()"); return EXIT_FAILURE; } } // określenie tego wraz z uruchomieniem recvfrom() pozwala na oczekiwanie na // pakiety UDP na wskazanym adresie i porcie, gdyby w ramach obsługi odebranej // wiadomości odsyłana byłaby odpowiedź do nadawcy to byłby to "serwer UDP" } // aby móc niezależnie wysyłać i odbierać rozgałęziamy proces // alternatywnie można użyć select() do czekania na dane z sieci lub standardowego wejścia switch (pid = fork()) { case -1: { // funkcja zwróciła -1 co oznacza błąd perror("Error in fork"); return 1; } case 0: { // potomek odbiera odpowiedzi w nieskończonej pętli // zostanie zakończony sygnałem od rodzica while(1) { // struktura do której zapisany zostanie adres nadawcy struct sockaddr_storage srcAddr; socklen_t srcAddrLen = sizeof(struct sockaddr_storage); // informację tą można wykorzystać do odsyłania odpowiedzi do nadawcy // odbiór wiadomości ... blokuje do momentu otrzymania wiadomości ret = recvfrom( sfd, buf1, BUF1_SIZE, 0, (struct sockaddr *) &srcAddr, &srcAddrLen ); // wypisanie wiadomości z informacją o nadawcy buf1[ret] = '\0'; printf("odebrano %d bajtów od %s: %s\n", ret, getAddresInfo(&srcAddr, srcAddrLen, buf2, BUF2_SIZE), buf1 ); } } default: { // rodzic wysyła wiadomości while (fgets( buf1, BUF1_SIZE, stdin )) { ret = sendto( sfd, buf1, strlen(buf1), 0, dstAddrInfo->ai_addr, dstAddrInfo->ai_addrlen ); printf("wysłano: %d bajtów: %s\n", ret, buf1); } // kończymy potomka gdy skończyliśmy nadawanie kill(pid, SIGTERM); wait(0); } } // zamkniecie gniazda puts("KONIEC"); freeaddrinfo(dstAddrInfo); close(sfd); } // konwersja adresów IPv4 i IPv6 wraz z numerem portu na napis const char* getAddresInfo( struct sockaddr_storage *srcAddr, socklen_t srcAddrLen, char* buf, int bufLen ) { if (srcAddrLen > sizeof(struct sockaddr_storage)) return NULL; const char* addrStr = NULL; uint16_t port; if (srcAddr->ss_family == AF_INET) { struct sockaddr_in* srcAddrPtr = (struct sockaddr_in*) srcAddr; addrStr = inet_ntop(AF_INET, &(srcAddrPtr->sin_addr), buf, bufLen); port = ntohs(srcAddrPtr->sin_port); } else if (srcAddr->ss_family == AF_INET6) { struct sockaddr_in6* srcAddrPtr = (struct sockaddr_in6*) srcAddr; addrStr = inet_ntop(AF_INET6, &(srcAddrPtr->sin6_addr), buf, bufLen); port = ntohs(srcAddrPtr->sin6_port); } if (addrStr) { int len = strlen(buf); snprintf(buf+len, bufLen-len, ":%d", port); } return addrStr; }
import socket, sys, os, signal if len(sys.argv) != 3 and len(sys.argv) != 4: print("USAGE: " + sys.argv[0] + " dstHost dstPort [listenPort]", file=sys.stderr) exit(1); # struktura zawierająca adres na który wysyłamy dstAddrInfo = socket.getaddrinfo(sys.argv[1], sys.argv[2]) # tutaj mogliśmy uzyskać kilka adresów, # ale optymistycznie zakładamy że pierwszy będzie OK dstAddrInfo = dstAddrInfo[0] # utworzenie gniazda sieciowego ... SOCK_DGRAM oznacza UDP # (na gnieździe z SOCK_DGRAM standardowo nie będzie widać błędów ICMP # związanych np. z niedostępnością docelowego hosta lub portu ...) sfd = socket.socket(dstAddrInfo[0], socket.SOCK_DGRAM) if len(sys.argv) == 4: # jeżeli podane opcjonalne argumentu to określamy na jakim porcie słuchamy if dstAddrInfo[0] == socket.AF_INET6: sfd.bind(('::', int(sys.argv[3]))) # :: oznacza dowolny adres IPv6 elif dstAddrInfo[0] == socket.AF_INET: sfd.bind(('0.0.0.0', int(sys.argv[3]))) # 0.0.0.0 oznacza dowolny adres # określenie tego wraz z uruchomieniem recvfrom() pozwala na oczekiwanie na # pakiety UDP na wskazanym adresie i porcie, gdyby w ramach obsługi odebranej # wiadomości odsyłana byłaby odpowiedź do nadawcy to byłby to "serwer UDP" while True: rdfd, _, _ = select.select([sfd, sys.stdin], [], []) if sfd in rdfd: # odbieramy i wypisujemy dane z UDP data, addr = sfd.recvfrom(4096) print("odebrano od", addr, ":", data.decode()); if sys.stdin in rdfd: # wczytujemy stdin data = sys.stdin.readline() if not data: # konczymy sfd.close() sys.exit() else: # wysyłamy dane z stdin sfd.sendto(data.encode(), dstAddrInfo[4]) # alternatywnie można użyć fork(): # pid = os.fork() # if pid == 0: # # potomek odbiera odpowiedzi w nieskończonej pętli # # zostanie zakończony sygnałem od rodzica # while True: # data, addr = sfd.recvfrom(4096) # print("odebrano od", addr, ":", data.decode()); # else: # # rodzic wysyła wiadomości # while True: # data = sys.stdin.readline() # if not data: # break # else: # sfd.sendto(data.encode(), dstAddrInfo[4]) # os.kill(pid, signal.SIGTERM) # sfd.close()
close(), co informuje zarówno nasz system jak i serwer o zakończeniu połączenia.#include <sys/socket.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define BUF_SIZE 256 int main(int argc, char *argv[]) { int ret, sfd; char buf[BUF_SIZE]; if (argc != 3) { fprintf(stderr, "%s dstHost dstPort\n", argv[0]); return 1; } // struktura zawierająca adres na który wysyłamy // użycie funkcji getaddrinfo pozwala na zapewnienie obsługi zarówno IPv4 jak i IPv6, // a także podawanie adresów w postaci numerycznej bądź przy użyciu nazw // alternatywnie można ręcznie przygotowywać struktury sockaddr_in / sockaddr_in6 // z użyciem np. htons(), inet_aton(), ... struct addrinfo *dstAddrInfo; if((ret = getaddrinfo(argv[1], argv[2], NULL, &dstAddrInfo)) != 0) { fprintf(stderr, "Error in getaddrinfo(): %s\n", gai_strerror(ret)); return 1; } // mogliśmy uzyskać kilka adresów, więc próbujemy używać kolejnych do skutku struct addrinfo *aiIter; for (aiIter = dstAddrInfo; aiIter != NULL; aiIter = aiIter->ai_next) { // utworzenie gniazda sieciowego ... SOCK_STREAM oznacza TCP sfd = socket(aiIter->ai_family, SOCK_STREAM, 0); if (sfd<0) { perror("Error in socket()"); continue; } // łączymy z serwerem if (connect(sfd, aiIter->ai_addr, aiIter->ai_addrlen)) { perror("Error in connect()"); close(sfd); sfd = -1; continue; } // udało się połączyć ... przerywamy sprawdzanie kolejnych adresów break; } freeaddrinfo(dstAddrInfo); if (sfd<0) { fprintf(stderr, "Can't connect\n"); return 1; } const char* msg = "Ala ma Kota\n"; if (send(sfd, msg, strlen(msg), 0) < 0) { // takie wywołanie send() jest równoważne wywołaniu write() perror("Error in send()"); return 1; } // ustalamy limit czasu oczekiwania na dane 13s struct timeval timeOut; timeOut.tv_sec = 13; timeOut.tv_usec = 0; // tworzymy zestaw deskryptorów dla funkcji select() // i umieszczamy w nim deskryptor gniazda sieciowego fd_set fdSet; FD_ZERO(&fdSet); FD_SET(sfd, &fdSet); // czekamy na dane w którymś z deskryptorów wchodzących // w skład zestawu lub timeout select(FD_SETSIZE, &fdSet, 0, 0, &timeOut); // select zmieni wartość fdSet i timeOut ... // będą one zawierały odpowiednio zbiór deskryptorów gotowych // do czytania i czas jaki pozostał do "wytimeoutowania" if (FD_ISSET (sfd, &fdSet)) { // jeżeli sfd jest w zbiorze gotowych do czytania to czytamy ret = read(sfd, buf, BUF_SIZE); if (ret < 0) { perror("Error in read()"); } else if (ret == 0) { puts("connection close by peer"); } else { buf[ret]='\0'; printf("odebrano %d bajtów: %s\n", ret, buf); } } else { puts("timeout"); } // w zależności od wymagań implementowanego protokołu można kontynuować // czekanie na kolejne dane z TCP i/lub jakiegoś innego wejścia (np. stdin), // można wysłać kolejne dane (żądanie) do serwera, itd ... // // w przypadku oczekiwania na dalsze dane (ponownego wywołania select) należy // pamiętać o ponownym ustawieniu fdSet i timeOut // zamkniecie gniazda close(sfd); }
import socket, select, sys if len(sys.argv) != 3: print("USAGE: " + sys.argv[0] + " dstHost dstPort", file=sys.stderr) exit(1); # struktura zawierająca adres na który wysyłamy dstAddrInfo = socket.getaddrinfo(sys.argv[1], sys.argv[2], proto=socket.IPPROTO_TCP) # mogliśmy uzyskać kilka adresów, więc próbujemy używać kolejnych do skutku for aiIter in dstAddrInfo: try: print("try connect to:", aiIter[4]) # utworzenie gniazda sieciowego ... SOCK_STREAM oznacza TCP sfd = socket.socket(aiIter[0], socket.SOCK_STREAM) # połączenie ze wskazanym adresem sfd.connect(aiIter[4]) except: # jeżeli się nie udało ... zamykamy gniazdo if sfd: sfd.close() sfd = None # i próbujemy następny adres continue break; if sfd == None: print("Can't connect", file=sys.stderr) exit(1); # wysyłanie sfd.sendall("Ala ma Kota\n".encode()) # czekanie na dane i odbiór danych while True: rdfd, _, _ = select.select([sfd], [], [], 13.0) if sfd in rdfd: d = sfd.recv(4096) d = d.decode() print(d, end="") # odbiór pustego pakietu lub pakietu zawierającego # jedynie pustą linię kończy działanie if d == "" or d == "\n" or d == "\r\n": break else: # timeout kończy działanie break # zamykanie połączenia sfd.shutdown(socket.SHUT_RDWR) sfd.close()
bind)listen)accept)accept() zwraca nowe gniazdo związane z nawiązanym połączeniem TCP. Ma ono ustawiony adresem IP i portem używany przez klienta oraz inny (niż używany do nasłuchiwania) numer portu po stronie serwera.
#include <sys/socket.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <sys/wait.h> #define BUF_SIZE 256 #define QUERY_SIZE 3 #define MAX_CHILD 5 int childNum = 0; void onChildEnd(int sig) { puts("odebrano sygnał o śmierci potomka"); --childNum; waitpid(-1, NULL, WNOHANG); } int main(int argc, char *argv[]) { int res; char buf[BUF_SIZE]; if (argc != 2) { fprintf(stderr, "USAGE: %s listenPort\n", argv[0]); return EXIT_FAILURE; } // obsługa sygnału o zakończeniu potomka signal(SIGCHLD, &onChildEnd); // utworzenie gniazda sieciowego ... SOCK_STREAM oznacza TCP int sfd = socket(PF_INET6, SOCK_STREAM, 0); if (sfd<0) { perror("Error in socket()"); return EXIT_FAILURE; } // ustawienie opcji gniazda ... IPV6_V6ONLY=0 umożliwia korzystanie z tego // samego socketu dla IPv4 i IPv6 (rozszerzenie Linux-owe - na niektórych // systemach nie jest możliwe dualne słuchanie przy pomocy socketu IPv6) int opt = 0; if (setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) { perror("setsockopt(IPV6_V6ONLY=0)"); return EXIT_FAILURE; } // utworzenie struktury opisującej adres struct sockaddr_in6 serwer; serwer.sin6_family=AF_INET6; serwer.sin6_port=htons(atoi(argv[1])); // host to network short (porządek bajtów), są też dla long i network to host serwer.sin6_addr=in6addr_any; // in6addr_any oznacza że nasłuchujemy na każdym adresie IP danego hosta // (zamiast tego można określić konkretny adres) // przypisanie adresu ... if (bind(sfd, (struct sockaddr *) &serwer, sizeof(struct sockaddr_in6)) < 0) { perror("Error in bind()"); return EXIT_FAILURE; } // określenie gniazda jako używanego do nasłuchiwania połączeń przychodzących if (listen(sfd, QUERY_SIZE) < 0) { perror("Error in listen()"); return EXIT_FAILURE; } while(1) { // odebranie połączenia struct sockaddr_in6 from; socklen_t fromLen=sizeof(struct sockaddr_in6); int sfd2 = accept(sfd, (struct sockaddr *) &from, &fromLen); // weryfikacja ilości potomków if (childNum >= MAX_CHILD) { printf("za dużo potomków\n"); // można tutaj także wysłać informacje o błędzie serwera do klienta close(sfd2); continue; } // aby móc obsługiwać wiele połączeń rozgałęziamy proces int pid = fork(); if (pid == 0) { // proces potomny ... zajmuje się tylko nowym połączeniem, // a nie nasłuchaniem więc zamykamy sfd close(sfd); // konwersja adresu na postać napisową char fromStr[64]; snprintf(fromStr, 64, "%s:%d", inet_ntop(AF_INET6, &(from.sin6_addr), buf, BUF_SIZE), ntohs(from.sin6_port) ); // obsługa połączenia printf("połączenie od: %s\n", fromStr); FILE * net; net=fdopen(sfd2, "r+"); // należałoby tutaj używać czekania z timeout'em ... // inaczej usługa jest podatna na ataki DoS while(fgets(buf, BUF_SIZE, net)) { printf("odebrano od %s: %s\n", fromStr, buf); fputs(buf, net); } fclose(net); printf("koniec połączenia od: %s\n", fromStr); // zakończenie połączenia i potomka close(sfd2); return 0; } if (pid == -1) perror("Error in fork()"); ++childNum; close(sfd2); } // zamkniecie gniazda close(sfd); }
import socket, select, signal, sys, os MAX_CHILD = 5 QUERY_SIZE = 3 TIMEOUT = 13 BUF_SIZE = 4096 if len(sys.argv) != 2: print("USAGE: " + sys.argv[0] + " listenPort", file=sys.stderr) exit(1); # obsługa sygnału o zakończeniu potomka childNum = 0 def onChildEnd(s, f): print("odebrano sygnał o śmierci potomka") global childNum childNum -= 1 os.waitpid(-1, os.WNOHANG); signal.signal(signal.SIGCHLD, onChildEnd) # utworzenie gniazd sieciowych ... SOCK_STREAM oznacza TCP sfd_v4 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sfd_v6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) # ustawienie opcji gniazda ... IPV6_V6ONLY=1 wyłącza korzystanie # z tego samego socketu dla IPv4 i IPv6 sfd_v6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) # przypisanie adresów ... # '0.0.0.0' oznacza dowolny adres IPv4 (czyli to samo co INADDR_ANY) # '::' oznacza dowolny adres IPv6 (czyli to samo co in6addr_any) sfd_v4.bind(('0.0.0.0', int(sys.argv[1]))) sfd_v6.bind(('::', int(sys.argv[1]))) # określenie gniazd jako używanych do odbioru połączeń przychodzących # (długość kolejki połączeń ustawiona na wartość QUERY_SIZE) sfd_v4.listen(QUERY_SIZE) sfd_v6.listen(QUERY_SIZE) # czekanie na połączenia z użyciem select() w nieskończonej pętli while True: sfd, _, _ = select.select([sfd_v4, sfd_v6], [], []) for fd in sfd: # odebranie połączenia sfd_c, sAddr = fd.accept() # weryfikacja ilości potomków if childNum >= MAX_CHILD: print("za dużo potomków - odrzucam połączenie od:", sAddr); sfd_c.send("Internal Server Error\r\n".encode()) sfd_c.close() break # aby móc obsługiwać wiele połączeń rozgałęziamy proces pid = os.fork() if pid > 0: # rodzic - zwiększamy licznik potomków childNum += 1 else: # potomek - obsługa danego połączenia print("połączenie od:", sAddr) while True: # czekanie na dane z timeout'em # aby zabezpieczyć się przed atakiem DoS rd, _, _ = select.select([sfd_c], [], [], TIMEOUT) if sfd_c in rd: data = sfd_c.recv(BUF_SIZE) if data: print("odebrano od", sAddr, ":", data.decode()); sfd_c.send(data) else: print("koniec połączenia od:", sAddr) break else: print("timeout połączenia od:", sAddr) break # zamykanie połączenia sfd_c.shutdown(socket.SHUT_RDWR) sfd_c.close() sys.exit()
ulimit -s), lub w trakcie jego pracy (przy pomocy setrlimit(RLIMIT_STACK, ...)), pod warunkiem że mieści się w ramach ogólnosystemowego limitu. Zwiększenie rozmiaru stosu wpływa na rozmiar pamięci wirtualnej przydzielonej procesowi, jednak (samo w sobie) nie wpływa na rzeczywiste zużycie pamięci przez proces.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/resource.h> char buf[64], *bufS; int fun() { int i, j, x[10000000]; for (i=0; i<17; i+=2) { for (j=0; j<20000; ++j) x[i*10000+j] = j; sleep(1); system(buf); } } int main() { snprintf(buf, 64, "ps -o 'vsize,size,rss,cmd' -p %d", getpid()); system(buf); puts("zwiększam rozmiar stosu"); struct rlimit rl; rl.rlim_cur = 2000000000; rl.rlim_max = rl.rlim_cur; if(setrlimit(RLIMIT_STACK, &rl)) perror("Error in setrlimit()"); system(buf); bufS = buf + strlen(buf); snprintf(bufS, 64 - (bufS-buf), " --no-headers"); fun(); *bufS = '\0'; system(buf); *bufS = ' '; fun(); }
new i funkcje z rodziny malloc. Alokacja pamięci poza stosem powoduje konieczność jawnego zwalniania pamięci (nie jest ona zwalniana wraz z zniknięciem zmiennej) przy pomocy odpowiednio operatora delete lub funkcji free.#include <stdio.h> #include <stdlib.h> #include <string.h> #include <string> #include <inttypes.h> struct Abc { std::string a; int b; private: short c; public: Abc() { puts("konstruktor ABC"); c = 123; } ~Abc() { puts("destruktor ABC"); } void print() { printf("a: %s, b: %d, c:%d\n", a.c_str(), b, c); } }; struct Xxx { Abc abc1; Abc abc2; int tab[9]; int xx1; int xx2; }; int main(int argc, char *argv[]) { int a[5], aa; // alokacja pamięci int *bb = (int*)malloc(sizeof(int)); int *cc = new int; int *c = new int[20]; // tablica alokowana dynamicznie w stylu C++ Abc *dd = new Abc; printf("na stosie: %p %p %p %p\n", &argv, &argc, &aa, a); printf("poza stosem: %p %p %p %p\n", bb, cc, c, dd); // zwalnianie pamięci free(bb); delete cc; delete c; delete dd; // alokujemy większy obszar pamięci przy pomocy malloc'a char* x = (char*)malloc(1024); // jest też realloc() umożliwiający zmianę rozmiaru zaalokowanego obszaru // zerujemy zaalokowaną pamięć ... na ogół nie jest to potrzebne, // ale w tym przykładzie może się przydać memset(x, 0, 1024); // często przydatna jest też funkcja memcpy() kopiująca wskazany // fragment pamięci w inne miejsce // pamięć zaalokowaną z użyciem malloc'a możemy dzięki // artmetyce wskaźnikowej oraz rzutowaniu typów wskaźnikowych // interpretować w dowolny sposób ... Abc *d1, *d2; int *b1, *b2; d1 = (Abc*)x; d2 = (Abc*)(x) + 1; b1 = (int*)(x + 2 * sizeof(Abc)); b2 = b1 + 10; printf("rozmiar Abc: %ld == %ld == %ld\n", sizeof(Abc), (uint64_t)d2-(uint64_t)d1, (uint64_t)b1-(uint64_t)d2 ); d1->b = 15; d1->print(); // jak widać powstał obiekt typu Abc, ale nie wykonał się konstruktor ... // ani konstruktory składowych (jak std::string), więc wywołanie: // d1->a = "aa bb cc"; // może się nie udać (zakończyć się np. Segmentation fault) // // aby uniknąć związanych z tym problemów możemy skorzystać z wariantu // operatora new który utworzy obiekt w zaalokowanej pamięci: new(d2) Abc; d2->a = "aa bb cc"; d2->print(); for (aa=0; aa<10; ++aa) b1[aa] = aa + 16; *b2 = 136917; // możemy też na taki obszar pamięci (nawet już zapełniony) // patrzeć np. jak na strukturę Xxx* z = (Xxx*)x; printf("%d == %d %d == %d %d == %d\n", z->tab[2], b1[2], z->xx1, b1[9], z->xx2, *b2 ); free(x); // takie zwolnienie nie wywoła destruktorów dla d1 i d2 }
#include <time.h> #include <stdlib.h> #include <string.h> #include <stdio.h> size_t time2str( const time_t* time, const char* timeZone, const char *format, char *buf, size_t bufLen, struct tm *tmPtr ); int main() { time_t czas1 = time(0); printf("Od początku epoki upłynęło %d sekund\n", czas1); // początek epoki to: 1970-01-01 00:00:00 UTC // wynik jest niezależny od lokalnej strefy czasowej // (zawsze wyrażany jest w UTC) // jeżeli potrzebna jest większa precyzja to można użyć np.: struct timespec czas2; clock_gettime(CLOCK_REALTIME, &czas2); printf("Od poczotku epoki upłyneło %d sekund i %d ns\n", czas2.tv_sec, czas2.tv_nsec); // na niektórych platformach (np. Linux) w clock_gettime() // wspierane są także inne zegary niż czasu rzeczywistego // np. podające czas monotoniczny lub od uruchomienia systemu char buf[128]; // korzystamy z własnej funkcji tworzącej napis w oparciu o // format zmiennej czasowej ... co prawda do UTC jest gmtime(), // ale w połączeniu z strftime potrafi dawać złe wyniki // (np. dla "%s") ponadto funkcja taka pozwala także na // obsługę innych stref czasowych time2str( &(czas2.tv_sec), "UTC", "%Y-%m-%d %H:%M:%S %Z (%s)", buf, sizeof(buf), NULL ); printf("Mamy teraz: %s\n", buf); time2str( &(czas2.tv_sec), NULL, "%Y-%m-%d %H:%M:%S %Z (%s)", buf, sizeof(buf), NULL ); printf("Mamy teraz: %s\n", buf); } size_t time2str( const time_t* time, const char* timeZone, const char *format, char *buf, size_t bufLen, struct tm *tmPtr ) { // jeżeli podano tmPtr != NULL użyj podanej struktury, // w przeciwnym razie użyj lokalnej struct tm tmTmp; if (!tmPtr) { tmPtr = &tmTmp; } // jeżeli podano strefę czasową to // zapamiętaj oryginalną strefę i ustaw nową char oldTZ[128]; if (timeZone) { strncpy(oldTZ, getenv("TZ"), 128); setenv("TZ", timeZone, 1); tzset(); } // pobierz rozłożony czas w ustawionej strefie czasowej localtime_r(time, tmPtr); // zapisz sformatowany napis do bufora bufLen = strftime(buf, bufLen, format, tmPtr); // przywróć oryginalną strefę czasową if (timeZone) { setenv("TZ", oldTZ, 1); tzset(); } return bufLen; }
import time import os def time2str(t, tz=None, fmt="%Y-%m-%d %H:%M:%S %Z (%s)"): if tz: oldTZ = os.environ['TZ'] os.environ['TZ'] = tz time.tzset() tm = time.localtime(t) s = time.strftime(fmt, tm) if tz: os.environ['TZ'] = oldTZ time.tzset() return [s, tz] czas = time.time() print("Od początku epoki upłynęło " + str(czas) + " sekund") # początek epoki to: 1970-01-01 00:00:00 UTC # wynik jest niezależny od lokalnej strefy czasowej # (zawsze wyrażany jest w UTC) tt = time2str(czas, tz="UTC") print("Mamy teraz: " + tt[0]); tt = time2str(czas) print("Mamy teraz: " + tt[0]); from datetime import datetime, timezone dt = datetime.fromtimestamp(czas, tz=timezone.utc) print("Mamy teraz: " + dt.strftime("%Y-%m-%d %H:%M:%S %Z (%s)")) # w celu pobrania aktualnego czasu zamiast fromtimestamp() # można skorzystać z metody now()
sleep lub podobnych celem przeczekania podanego czasu lub skorzystać z timerów celem otrzymania sygnału po zadanym czasie.// przy kompilacji konieczne jest dodanie: // -lrt -lpthread #include <time.h> #include <signal.h> #include <stdio.h> #include <unistd.h> #include <errno.h> void onTimer(int sig) { puts("timer !!!"); // drzemka 0.35s // do spania można wykorzystać usleep(), sleep() lub nanosleep() // usleep i sleep działają bardzo podobnie // (z tym że pierwszy przyjmuje czas w sekundach, a drugi w mikrosekundach) usleep(350000); } int main() { // obslugujemy sygnał SIGALRM signal(SIGALRM, &onTimer); // najprostrzy timer ... alarm(4); pause(); // timery POSIX (wiecej man timer_settime) // 1) tworzymy timer timer_t timer; timer_create(CLOCK_REALTIME, NULL, &timer); // podanie wskaźnika na strukturę sigevent zamiast NULL pozwala na określenie // innego sygnału jaki ma wygenerować timer lub obsługę tak jak wątku // (więcej w `man 7 sigevent`) // 2) konfiguryujemy timer struct itimerspec tSpec; // pierwsze wykonanie timera po 100ms tSpec.it_value.tv_sec=0; tSpec.it_value.tv_nsec=100000000; // kolejne co 1s // (jezeli it_interval będzie wyzerowane to timer wykona się tylko raz) tSpec.it_interval.tv_sec=1; tSpec.it_interval.tv_nsec=0; // 3) uruchamiamy timer // drugim argumentem są flagi - flaga TIMER_ABSTIME pozwala na // ustawienie timera na czas absolutny (a nie okres czasu) timer_settime(timer, 0, &tSpec, NULL); char i=0; do { struct itimerspec tRest; timer_gettime(timer, &tRest); printf("do timera pozostało: %dns\n", tRest.it_value.tv_nsec); pause(); // czekamy na timer printf("aktualna liczba utraconych tyknięć timera wynosi %d\n", timer_getoverrun(timer)); } while(i++<3); // powyższa metoda zapewnia rozpoczynanie w równych odstępach czasu ... // w przypadku zastosowania sleep() należałoby obliczać czas przez który // wykonywał się kod i odejmować go od czasu który podajemy do sleep() // drzemka // nanosleep pozwala na określenie sekund i milisekund oraz zwraca czas // różnicę pomiędzy planowanym czasem spania a rzeczywistym czasem // od wejścia do powrotu z funkcji nanosleep() - uwzględniając np. czas // obsługi sygnału który przerwał działanie funkcji nanosleep() struct timespec drzemka, pobudka; drzemka.tv_sec=2; drzemka.tv_nsec=100000000; // chcemy spac 2.1s ale obudzi nas timer // to ile zesmy niedospali zostanie zapisane w pobudka if (nanosleep(&drzemka, &pobudka)<0 && errno == EINTR) printf("niedospalismy: %ds i %dns\n", pobudka.tv_sec, pobudka.tv_nsec); // usuniecie timera timer_delete(timer); }
/* PLIK: py_api.cpp kompilacja: g++ --std=c++11 -shared -fPIC -I/usr/include/python3.7m/ py_api.cpp \ -o MyPyAPI.so -lpython3.7m -lboost_python37 uwaga: - kolejność argumentów może być istotna - plik wynikowy powinien mieć taką samą nazwę jak moduł zadeklarowany przy pomocy BOOST_PYTHON_MODULE() */ #include <iostream> #include <string> #include <list> std::string f1(int a) { for (int i=0; i<a; ++i) std::cout << "Ala ma kota\n"; return "Kot ma Alę"; } struct K1 { int a; static K1* obj; int f1(int b) { return a + b; } static K1* get() { return obj; } }; void f2(K1& k, int n) { std::cout << "run f2(): " << k.f1(2*n) << "\n"; k.a += 1; } K1* K1::obj = NULL; #include <boost/python.hpp> BOOST_PYTHON_MODULE(MyPyAPI) { boost::python::def("f1", f1); boost::python::class_<K1>("Klasa") .def_readwrite("a", &K1::a) .def("f1", &K1::f1) // w pythonie w odróżnieniu od C++ referencja do klasy jest jawnym // argumentem metod nie statycznych zatem f2 które przyjmuje jako // pierwszy argument referencję do obiektu klasy K1 możemy użyć // jako metody klasy pythonowej .def("f2", f2) // typo z funkcji do pythona zwracana jest wartość zmiennej (a nie // referencja do zmiennej C++) jednak dla funkcji zwracających // wskaźnik lub referencję na ogół chcemy aby zwracana była właśnie // referencja do istniejącej zmiennej C++ .def("get", &K1::get, boost::python::return_value_policy< boost::python::reference_existing_object >() ) ; // f2 możemy też użyć jako niezależnej funkcji boost::python::def("f22", f2); // podobnie jak f1 boost::python::def("f11", &K1::f1); }
# skrypt wykorzystujący moduł # MyPyAPI stworzony w C++ # dodajemy bierzący katalog do ścieżki w której python szuka bibliotek import sys sys.path.append('./') # importujemy naszą bibliotekę (z pliku MyPyAPI.so) import MyPyAPI # uruchomienie funkcji f1 i odebranie wyniku ret = MyPyAPI.f1(2) print(ret) # utworzenie obiektu klasy K1 (Klasa) kk = MyPyAPI.Klasa() # oraz jego używanie ... kk.a = 3 print(kk.f1(2)) kk.f2(1) MyPyAPI.f22(kk, 1) print("kk.a =", kk.a) print(MyPyAPI.f11(kk, 1))
# skrypt używający biblioteki C # bez API pythonowego # skrypt powoduje włączenie NumLock # przy pracy w trybie graficznym # zgodnym z X11 from ctypes import * X11 = cdll.LoadLibrary("libX11.so.6") X11.XOpenDisplay.restype = c_void_p display = X11.XOpenDisplay(None); X11.XkbLockModifiers( c_void_p(display), c_uint(0x0100), c_uint(16), c_uint(16) ) X11.XCloseDisplay(c_void_p(display))
/* PLIK: py_run.cpp kompilacja: g++ -I/usr/include/python3.7m/ py_run.cpp -lpython3.7m -lboost_python37 uwaga: - kolejność argumentów może być istotna - plik używa "py_api.cpp" z poprzedniego przykładu - plik wywołuje skrypt "py_run.script.py" z bieżącego katalogu */ // korzystamy z wcześniej przygotowanego (w "API pythonowe biblioteki C++") interfejsu pythonowego #include "py_api.cpp" int main(int, char **) { K1 *o1 = new K1(); o1->a = 1; K1 *o2 = new K1(); o2->a = 2; K1::obj = o1; std::cout << "o1->a = " << o1->a << " o2->a = " << o2->a << "\n"; // initialise python Py_Initialize(); try { // initialise and import MyPyAPI module PyObject* module = PyInit_MyPyAPI(); PyDict_SetItemString(PyImport_GetModuleDict(), "MyPyAPI", module); Py_DECREF(module); PyRun_SimpleString("import MyPyAPI" ); // poprzez PyRun_SimpleString można też uruchamiać inne fragmenty kodu pythonowego // prepare to run scripts boost::python::object main = boost::python::import("__main__"); boost::python::object global(main.attr("__dict__")); // export object to python global["ck1"] = boost::python::ptr(o1); // run file boost::python::object result = boost::python::exec_file( "./py_run.script.py", global, global ); // import object from python boost::python::object script = global["script1"]; // run python function with args from C++ if(!script.is_none()) { // run scripts std::cout << "RUN\n"; std::cout << "return = " << boost::python::extract<int>( script(boost::python::ptr(o2)) ) << "\n"; } } catch(boost::python::error_already_set &) { PyErr_Print(); exit(-1); } std::cout << "o1->a = " << o1->a << " o2->a = " << o2->a << "\n"; delete o1; delete o2; return 0; }
# PLIK: py_run.script.py # uruchamiany przez kod C++ z py_api.cpp print("początek pliku .py") ck0 = MyPyAPI.Klasa.get() print("ck0:", ck0.f1(0)) print("ck1:", ck1.f1(0)) ck0.a=4 print("ck0:", ck0.f1(0)) print("ck1:", ck1.f1(0)) def script1(arg): print("run script1") print("ck0:", ck0.f1(0)) print("ck1:", ck1.f1(0)) print("arg:", arg.f1(0)) arg.a=13 print("ck0:", ck0.f1(0)) print("ck1:", ck1.f1(0)) print("arg:", arg.f1(0)) print("end script1") return arg.a + ck0.a; print("koniec pliku .py")
// przy kompilacji konieczne jest dodanie: // -lsqlite3 #include <sqlite3.h> #include <stdio.h> /* bazę można utworzyć zarówno w poniższym kodzie poleceniami SQL, * jak też z poziomu powłoki: cat << EOF | sqlite3 example.db CREATE TABLE posts (pid INT PRIMARY KEY, uid INT, text TEXT); INSERT INTO posts VALUES (1, 21, 'abc ..'); INSERT INTO posts VALUES (3, 2671, 'test'); EOF */ int main() { sqlite3 *db; if ( sqlite3_open("example.db", &db) ) { fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); return 1; } sqlite3_stmt *stmt; int ret = sqlite3_prepare_v2(db, "SELECT uid, text FROM posts", -1, &stmt, NULL ); while ( ( ret = sqlite3_step(stmt) ) == SQLITE_ROW ) { int uid = sqlite3_column_int(stmt, 0); const char* text = (const char*) sqlite3_column_text(stmt, 1); printf("uid = %d, text: %s\n", uid, text); } }
import sqlite3 import os.path if os.path.isfile('example.db'): create = False else: create = True conn = sqlite3.connect('example.db') c = conn.cursor() if create: print("create new db") c.execute("CREATE TABLE users (uid INT PRIMARY KEY, name TEXT);") c.execute("CREATE TABLE posts (pid INT PRIMARY KEY, uid INT, text TEXT);") c.execute("INSERT INTO users VALUES (21, 'user A');") c.execute("INSERT INTO users VALUES (2671, 'user B');") c.execute("INSERT INTO posts VALUES (1, 21, 'abc ..');") c.execute("INSERT INTO posts VALUES (2, 21, 'qwe xyz');") c.execute("INSERT INTO posts VALUES (3, 2671, 'test');") conn.commit() maxUid = 100 for r in c.execute("SELECT * FROM users WHERE uid < ?;", (maxUid,)): print(r) for r in c.execute("""SELECT u.name, p.text FROM users AS u JOIN posts AS p ON (u.uid = p.uid);"""): print(r)
<?php $sql_conn = mysqli_connect('server', 'user', 'password', 'database'); if (!$sql_conn) { echo "ERROR: " . mysqli_connect_error(); exit; } else { echo "Connected to MySQL: " . mysqli_get_host_info($sql_conn); } $query = $db->query("SELECT u.name, p.text FROM users AS u JOIN posts AS p ON (u.uid = p.uid);"); while ($row = $query->fetch_array()) { print($row["name"] . ": " . $row["text"] . "\n"); } ?>
# interfejs pseudo graficzny z użyciem biblioteki curces ... # a tak naprawdę programu dialog z niej korzystającego from dialog import Dialog from time import sleep d = Dialog(dialog="dialog") # tak naprawdę jest to wraper # wykorzystujący program o nazwie "dialog" # # jest w stanie (w ograniczonym zakresie) # współpracować z innymi tego typu narzędziami # (także działającymi w trybie graficznym) # takimi jak: whiptail, kdialog, ... # d = Dialog(dialog="kdialog", compat="kdialog") d.set_background_title("ABC ...") res = d.yesno("Tak czy NIE?") if res == d.OK: d.msgbox("TAK !!!") else: d.msgbox("NIE !!!") d.gauge_start("proszę czekać ... będzie rozmowa ...") for i in range(0, 101, 5): d.gauge_update(i) sleep(1)
# interfejs pseudo graficzny z użyciem Qt4 import sys from PyQt4 import QtGui, QtCore app = QtGui.QApplication(sys.argv) win = QtGui.QWidget() win.resize(250, 150) win.move(300, 300) win.setWindowTitle('ABC ...') def showDialog(): reply = QtGui.QMessageBox.question(None, 'Message', "Tak czy nie?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: print("YES") elif reply == QtGui.QMessageBox.No: print("NO") else: print("cos innego?!") button = QtGui.QPushButton('Nacisnij mnie', win) button.clicked.connect(showDialog) button.resize(button.sizeHint()) button.move( 250/2-button.sizeHint().width()/2, 150/2-button.sizeHint().height()/2 ) win.show()
# interfejs pseudo graficzny z użyciem Gtk 3 import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk window = Gtk.Window(title="Hello World") def showDialog(caller): dialog = Gtk.MessageDialog(window, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, "Ważne pytanie !!!") dialog.format_secondary_text("Tak czy nie?") response = dialog.run() if response == Gtk.ResponseType.YES: print("TAK !!!") elif response == Gtk.ResponseType.NO: print("NIE !!!") else: print("bez odpowiedzi!?") dialog.destroy() button1 = Gtk.Button("Nacisnij mnie") button1.connect("clicked", showDialog) window.add(button1) window.show_all() window.connect("delete-event", Gtk.main_quit) Gtk.main()
#include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/i2c-dev.h> #ifndef I2C_FUNC_I2C #include <i2c/smbus.h> #endif #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> // wymagany pakiet libi2c-dev, kompilacja: // dla libi2c-dev < 4: // gcc komunikacja_I2C.c // dla libi2c-dev >= 4: // gcc -li2c komunikacja_I2C.c void help(const char *prog_name) { fprintf(stderr, "USAGE (without on chip register addres):\n"); fprintf(stderr, " - read: %s i2c_dev addr r -\n", prog_name); fprintf(stderr, " - write: %s i2c_dev addr w - data\n", prog_name); fprintf(stderr, "\n"); fprintf(stderr, "USAGE (with on chip register addres):\n"); fprintf(stderr, " - read: %s i2c_dev addr r reg_addr\n", prog_name); fprintf(stderr, " - write: %s i2c_dev addr w reg_addr data\n", prog_name); fprintf(stderr, "\n"); fprintf(stderr, "i2c_dev = /dev/i2c-... device path\n"); fprintf(stderr, "addr = I2C _devide_ address\n"); fprintf(stderr, "reg_addr = register (data) address in I2C chip\n"); fprintf(stderr, "data = data to write to device\n"); } int main(int argc, char *argv[]) { int fd, i2c_addr, reg_addr, res, d; if (argc < 4) { help(argv[0]); return 1; } // otwarcie urządzenia fd = open(argv[1], O_RDWR); if (fd < 0) { fprintf(stderr, "Error open I2C device file (%s): %s\n", argv[1], strerror(errno)); return 2; } // ustawienie adresu slave i2c_addr = strtol(argv[2], NULL, 0); res = ioctl(fd, I2C_SLAVE, i2c_addr); if (res < 0) { fprintf(stderr, "ERROR set device address to 0x%02x: %s\n", i2c_addr, strerror(errno)); return 3; } if (argv[3][0] == 'r' && argc == 5) { // odczyt if (argv[4][0] != '-') { // jeżeli jest podany to ustawiamy adres rejestru do odczytu // warto zauważyć że jest to robione jako WRITE do urządzenia I2C reg_addr = strtol(argv[4], NULL, 0); res = i2c_smbus_write_byte(fd, reg_addr); if (res < 0) { fprintf(stderr, "ERROR write (register address) %d to i2c device 0x%02x on %s: %s\n", reg_addr, i2c_addr, argv[1], strerror(errno)); return 4; } } // czytamy dane z urządzenia res = i2c_smbus_read_byte(fd); if (res < 0) { fprintf(stderr, "ERROR read from i2c device 0x%02x on %s: %s\n", i2c_addr, argv[1], strerror(errno)); return 5; } printf("0x%02x\n", res); } else if (argv[3][0] == 'w' && argc == 6) { // zapis d = strtol(argv[5], NULL, 0); if (argv[4][0] != '-') { // jeżeli jest podany adres rejestru do zapisu to go używamy i wykonujemy zapis reg_addr = strtol(argv[4], NULL, 0); res = i2c_smbus_write_byte_data(fd, reg_addr, d); if (res < 0) { fprintf(stderr, "ERROR write %d to i2c device 0x%02x, register 0x%02x on %s: %s\n", d, i2c_addr, reg_addr, argv[1], strerror(errno)); return 5; } } else { // w przeciwnym razie po prostu zapisujemy dane do urządzenia res = i2c_smbus_write_byte(fd, d); if (res < 0) { fprintf(stderr, "ERROR write %d to i2c device 0x%02x on %s: %s\n", d, i2c_addr, argv[1], strerror(errno)); return 5; } } } else if (argv[3][0] == 'R' && argc == 5 && argv[4][0] != '-') { // odczyt z rejestru jedną funkcją reg_addr = strtol(argv[4], NULL, 0); res = i2c_smbus_read_byte_data(fd, reg_addr); // odczyt z rejestru o danym adresie może być wykonany także jedną funkcją (analogicznie jak zapis) if (res < 0) { fprintf(stderr, "ERROR read from i2c device 0x%02x, register 0x%02x on %s: %s\n", i2c_addr, reg_addr, argv[1], strerror(errno)); return 5; } printf("0x%02x\n", res); } else { help(argv[0]); return 1; } close(fd); }
char* i funkcje z rodziny string.h) operuje się obiektami zawierającymi w sobie dane i posiadającym metody na nich operujące (np. napis typu std::string).#include <iostream> struct Product { virtual void info() = 0; virtual ~Product() {} }; struct ProductA : public Product { virtual void info() { std::cout << "ProductA: a, b, c\n"; } }; struct ProductB : public Product { virtual void info() { std::cout << "ProductB: a, f, g\n"; } }; struct ProductFactory { static Product* getProduct(const std::string& type) { if (type == "A") return new ProductA(); else if (type == "B") return new ProductB(); else return NULL; } }; int main(int argc, char *argv[]) { std::string id = "A"; if (argc > 1 && argv[1][0] == 'B') id = "B"; Product* p = ProductFactory::getProduct(id); p->info(); delete p; }
#include <iostream> struct Product { virtual void info() = 0; virtual ~Product() {} }; struct ProductA: public Product { virtual void info() { std::cout << "ProductA: a, b, c\n"; } }; struct ProductB: public Product { virtual void info() { std::cout << "ProductB: a, f, g\n"; } }; struct Factory { static Factory* getFactory(const std::string& type); virtual Product* getProduct() = 0; // fabryka taka może z łatwością produkować kilka // różnych produktów (całą rodzinę produktów) virtual ~Factory() {} }; struct FactoryA: public Factory { virtual Product* getProduct() { return new ProductA(); } }; struct FactoryB: public Factory { virtual Product* getProduct() { return new ProductB(); } }; Factory* Factory::getFactory(const std::string& type) { if (type == "A") return new FactoryA(); else if (type == "B") return new FactoryB(); else return NULL; } int main(int argc, char *argv[]) { std::string id = "A"; if (argc > 1 && argv[1][0] == 'B') id = "B"; Factory* f = Factory::getFactory(id); Product* p = f->getProduct(); p->info(); delete p; delete f; }
#include <iostream> struct Product { void info() { std::cout << "Product:\n"; std::cout << " " << el1 << "\n"; std::cout << " " << el2 << "\n"; } void setElement1(const std::string& e) { el1 = e; } void setElement2(const std::string& e) { el2 = e; } private: std::string el1, el2; }; struct ProductBuilder { virtual ~ProductBuilder() {} void createProduct() { p = new Product(); } Product* getProduct() { return p; } // w takiej implementacji nie odebranie lub // nie skasowanie produktu przez klienta będzie // prowadziło do wycieków pamięci virtual void buildElement1(const std::string& d) = 0; virtual void buildElement2() = 0; protected: Product* p; }; struct ProductDirector { void setData(const std::string& d) { this->d = d; } void setBuilder(ProductBuilder* b) { this->b = b; } void createProduct() { b->createProduct(); b->buildElement1(d); b->buildElement2(); } private: ProductBuilder* b; std::string d; }; struct ProductBuilderA : public ProductBuilder { void buildElement1(const std::string& d) { p->setElement1(d + d); }; void buildElement2() { p->setElement2("ab"); }; }; struct ProductBuilderB : public ProductBuilder { void buildElement1(const std::string& d) { p->setElement1("b" + d); }; void buildElement2() { p->setElement2("bb"); }; }; int main(int argc, char *argv[]) { ProductBuilder* b; if (argc > 1 && argv[1][0] == 'B') b = new ProductBuilderB(); else b = new ProductBuilderA(); ProductDirector* d = new ProductDirector(); d->setData("x"); d->setBuilder(b); d->createProduct(); Product* p = b->getProduct(); p->info(); delete p; delete d; delete b; }
#include <iostream> class Singleton { private: // konstruktor, konstruktor kopiujący oraz operator // przypisania są prywatne aby uniemożliwić tworzenie // lub kopiowanie obiektów tej klasy z zewnątrz Singleton(); Singleton(const Singleton&) {} Singleton& operator=(const Singleton&); // destruktor też prywatny ... dla zabezpieczenia przed // możliwością zrobienia delete na uzyskanym wskaźniku ~Singleton() {} // klasa zawiera prywatny statyczny wskaźnik na siebie // (na swoją jedyną instancję) static Singleton* objPtr; public: // a także metodę służącą pobraniu tego wskaźnika // (i utworzeniu obiektu gdy nie istnieje) static Singleton* getPtr() { // alternatywnie zamiast wskaźnika można użyć // statycznego obiektu tej klasy zadeklarowanego // wewnątrz tej funkcji if (!objPtr) objPtr = new Singleton(); return objPtr; } }; Singleton* Singleton::objPtr = 0; Singleton::Singleton() { std::cout << "konstruktor Singleton\n"; } int main() { Singleton* a = Singleton::getPtr(); Singleton* b = Singleton::getPtr(); std::cout << a << " == " << b << "\n"; }
#include <iostream> struct Product { virtual ~Product() {} virtual void info() = 0; }; struct ProductA : public Product { void info() { std::cout << "Product:\n"; std::cout << " ab\n"; } }; struct DecoratorX : public Product { DecoratorX(Product* p) { this->p = p; } void info() { p->info(); std::cout << " xx\n"; } protected: Product* p; }; struct DecoratorY : public Product { DecoratorY(Product* p) { this->p = p; } void info() { p->info(); std::cout << " yy\n"; } protected: Product* p; }; int main(int argc, char *argv[]) { Product* p = new ProductA(); Product* p1 = new DecoratorX(p); Product* p2 = new DecoratorY(p1); p1->info(); p2->info(); delete p2; delete p1; delete p; }
#include <iostream> #include <list> struct Product { virtual void info() = 0; virtual ~Product() {}; }; struct ProductA : public Product { virtual void info() { std::cout << "ProductA: a, b, c\n"; } }; struct ProductB : public Product { virtual void info() { std::cout << "ProductB: a, f, g\n"; } }; class ProductComposite : public Product { public: std::list<Product*> elements; virtual void info() { for (auto e : elements) e->info(); } ~ProductComposite() { for (auto e : elements) delete e; } }; int main(int argc, char *argv[]) { ProductComposite* pc = new ProductComposite(); pc->elements.push_back(new ProductA()); pc->elements.push_back(new ProductB()); pc->elements.push_back(new ProductA()); pc->info(); delete pc; }
#include <iostream> #include <map> struct Obserwator { virtual bool onUpdate(int eventID) = 0; }; struct Obserwowany { void addListener(Obserwator* l, int key) { // sprawdź czy jest już zarejestrowany auto subset = listeners.equal_range(key); for (auto iter=subset.first; iter!=subset.second; ++iter) if (iter->second == l) return; // zarejestruj listeners.insert(std::pair<int, Obserwator*>(key, l)); } void remListener(Obserwator* l) { for (auto iter=listeners.begin(); iter!=listeners.end(); ++iter) { if (iter->second == l) { listeners.erase(iter); return; } } } // klasyczny wzorzec listenera void callListenersAll(int eventID) { // mapa zapewnia wywoływanie listenerów wg kolejności kluczy for (auto& l : listeners) { l.second->onUpdate(eventID); } } // wzorzec listenera połączony z łańcuchem zobowiązań void callListeners(int eventID) { // mapa zapewnia wywoływanie listenerów wg kolejności kluczy for (auto& l : listeners) { if (l.second->onUpdate(eventID)) break; } } protected: std::multimap<int, Obserwator*> listeners; }; struct ObserwatorA : public Obserwator { virtual bool onUpdate(int eventID) { std::cout << " ObserwatorA z id=" << eventID << "\n"; if (eventID == 4) return true; return false; }; }; struct ObserwatorB : public Obserwator { virtual bool onUpdate(int eventID) { std::cout << " ObserwatorB z id=" << eventID << "\n"; if (eventID == 7) return true; return false; }; }; int main(int argc, char *argv[]) { Obserwowany* o = new Obserwowany(); Obserwator* o1 = new ObserwatorA(); Obserwator* o2 = new ObserwatorA(); Obserwator* o3 = new ObserwatorB(); o->addListener(o1, 30); o->addListener(o2, 10); o->addListener(o3, 20); std::cout << "callAll:\n"; o->callListenersAll(7); std::cout << "call:\n"; o->callListeners(7); delete o; delete o1; delete o2; delete o3; }
#include <iostream> #include <map> struct Receiver { virtual void receive(std::string msg) = 0; }; struct Mediator { void addReceiver(Receiver* l, int key) { // sprawdź czy jest już zarejestrowany auto subset = receivers.equal_range(key); for (auto iter=subset.first; iter!=subset.second; ++iter) if (iter->second == l) return; // zarejestruj receivers.insert(std::pair<int, Receiver*>(key, l)); } void remReceiver(Receiver* l) { for (auto iter=receivers.begin(); iter!=receivers.end(); ++iter) { if (iter->second == l) { receivers.erase(iter); return; } } } bool send(std::string msg, int dstID) { // gdyby zamiast natychmiastowego wywoływania receive() u adresatów // zaimplementować gromadzenie wiadomości w jakiejś strukturze // i osobną funkcję wysyłającą kolejne wiadomości z tej struktury // to wzorzec ten pozwoliłby na realizację kolejki wiadomości // (pętli komunikatów) // // struktura przechowująca zakolejkowane do wysłania wiadomości może // uwzględniać kolejność ich odebrania przez Mediatora lub wartość // priorytetu zawartego w wiadomości (kolejka priorytetowa) auto subset = receivers.equal_range(dstID); for (auto& iter=subset.first; iter!=subset.second; ++iter) { iter->second->receive(msg); } } protected: std::multimap<int, Receiver*> receivers; };
class PID { // poprzednia różnica między wartością zadaną a otrzymaną // (poprzedni błąd regulacji / uchyb) float lastDiff; // poprzednia wartość otrzymana (zmienna procesu) float lastVal; // część całkująca, akumulowana pomiędzy krokami float integral; public: // nastawa - wartość zadana float setPoint; // wartość wyjścia dla sterowania krokowego // (akumulacja w układzie realizującym) float outStep; // wartość wyjścia dla sterowania sygnałem float outValue; // parametry regulatora PID float Kp, Ki, Kd; // limity wartości sterowanej float outValueMin, outValueMax; // konstruktor - inicjalizacja parametrów PID ( float initVal, float sp, float kp, float ki, float kd, float min, float max ) : lastDiff(0), lastVal(initVal), integral(0), setPoint(sp), outStep(0), outValue(0), Kp(kp), Ki(ki), Kd(kd), outValueMin(min), outValueMax(max) {} int doStep(float inputVal, float stepTime) { // obliczmy aktualny błąd regulacji // (na podstawie odczytanej wartości wejściowej) float diff = setPoint - inputVal; // wyłączamy regulację gdy prowadziłby do przesterowania if (outValue > outValueMax && diff > 0) return 1; if (outValue < outValueMin && diff < 0) return -1; // całkowanie przybliżamy jako jako suma pól trapezów integral += (diff + lastDiff) / 2 * stepTime; // różniczkowanie przybliżamy jako tangens nachylenia // prostej pomiędzy poprzednim krokiem a obecnym // celem złagodzenia odpowiedzi na zmiany wartości zadanej // różniczkujemy sygnał wejściowy float derivative = -(inputVal - lastVal) / stepTime; // a nie błąd regulacji: derivative = (diff - lastDiff) / stepTime; // obliczenie wartości sygnału sterującego na podstawie tego kroku outStep = Kp*diff + Ki*integral + Kd*derivative; // akumulacja sygnału sterującego outValue += outStep; // zapamiętanie aktualnego błędu regulacji // jako poprzedni dla następnego kroku lastDiff = diff; // zapamiętanie aktualnej wartości wejściowej // jako poprzedniej dla następnego kroku lastVal = inputVal; return 0; } }; int main() { PID pid(readInput(), 21.0, 1.0, 0.0, 0.0, -100.0, 100.0); // okres działania algorytmu [us] unsigned int stepTime = 250000; // pętla algorytmu while(1) { // odczyt wejścia float curVal = readInput(); // obliczenie wartości sterującej z użyciem PID pid.doStep( curVal, stepTime ); // wystawienie wartości sterującej setOutput(pid.outValue); // odczekanie do następnego kroku usleep(stepTime); } }
::.2001:db8::a17/48 oznacza że pierwsze 48 bity stanowią adres sieci a kolejne 128-48 = 80 bitów stanowi adres hosta w tej sieci.2001:db8::/48. Informacja taka jest wystarczająca do sprawdzenia czy dowolny inny adres IP należy do tej sieci czy nie.# adresacja IPv6 from ipaddress import * a1 = IPv6Address("2001:0db8::17:15") aa1 = int(a1) print("adress IPv6 jest 128 bitową liczbą całkowitą np.: " + str(a1) + " == " + hex(aa1)) n0 = IPv6Network("::/112"); m1 = n0.netmask mm1 = int(m1) p1 = n0.prefixlen print("Maska podsieci IPv6 jest 128 bitową liczbą całkowitą np.: " + str(m1) + " == " + hex(mm1)) print("Jako że maska jest liczbą, która zapisana binarnie, zawsze zawiera ciągły ciąg bitów") print("o wartości 1, a po nim ciągły ciąg bitów o wartości 0 (mogą być zerowej długości), to") print("często stosowany jest zapis polegający na podawaniu długości prefiksu: /" + str(p1)) print("jest to ilość bitów o wartości 1 w masce, czyli im większy prefix tym mniejsza sieć.") n1 = IPv6Network("2001:0db8::17:15/112", strict=False); nn1 = int(n1.network_address) print("Aby obliczyć adres sieci (czyli wspólną dla wszystkich hostów w danej sieci część") print("adresu IP) należy wykonać binarny AND pomiędzy adresem IP hosta a maską podsieci.") print("Dla powyższego przykładu:") print(hex(mm1 & aa1) + " == " + str(n1) + " == " + hex(nn1)) # aby sprawdzić czy adres IP należy do danej sieci trzeba obliczyć adres sieci tego hosta # w oparciu o maskę sieci którą sprawdzamy def sprawdzSiec(n, a): nn = int(a) & int(n.netmask) if nn == int(n.network_address): print(str(a) + " należy do sieci " + str(n)) else: print(str(a) + " NIE należy do sieci " + str(n)) sprawdzSiec(n1, IPv6Address("2001:0db8::17:ab13")) sprawdzSiec(n1, IPv6Address("2001:0db8::13:a"))
# adresacja IPv4 from ipaddress import * a1 = IPv4Address("192.168.34.17") aa1 = int(a1) print("adress IPv4 jest 32 bitową liczbą całkowitą np.: " + str(a1) + " == " + hex(aa1)) n0 = IPv4Network("0.0.0.0/25"); m1 = n0.netmask mm1 = int(m1) p1 = n0.prefixlen print("Maska podsieci IPv4 jest 32 bitową liczbą całkowitą np.: " + str(m1) + " == " + hex(mm1)) print("Jako że maska jest liczbą, która zapisana binarnie, zawsze zawiera ciągły ciąg bitów") print("o wartości 1, a po nim ciągły ciąg bitów o wartości 0 (mogą być zerowej długości), to") print("często stosowany jest zapis polegający na podawaniu długości prefiksu: /" + str(p1)) print("jest to ilość bitów o wartości 1 w masce, czyli im większy prefix tym mniejsza sieć.") n1 = IPv4Network("192.168.34.17/25", strict=False); nn1 = int(n1.network_address) print("Aby obliczyć adres sieci (czyli wspólną dla wszystkich hostów w danej sieci część") print("adresu IP) należy wykonać binarny AND pomiędzy adresem IP hosta a maską podsieci.") print("Dla powyższego przykładu:") print(hex(mm1 & aa1) + " == " + str(n1) + " == " + hex(nn1)) # aby sprawdzić czy adres IP należy do danej sieci trzeba obliczyć adres sieci tego hosta # w oparciu o maskę sieci którą sprawdzamy def sprawdzSiec(n, a): nn = int(a) & int(n.netmask) if nn == int(n.network_address): print(str(a) + " należy do sieci " + str(n)) else: print(str(a) + " NIE należy do sieci " + str(n)) sprawdzSiec(n1, IPv4Address("192.168.34.13")) sprawdzSiec(n1, IPv4Address("192.168.34.199"))
ciekawi.icm.edu.pl.), którą najczęściej pomija się w zapisieNS – informacja o serwerach obsługujących DNS danej domenyA – mapowanie nazwy na adres IPv4AAAA – mapowanie nazwy na adres IPv6MX – informacja o serwerach obsługujących pocztę danej domenySRV – informacje o hoście świadczącym usługę (usługa określana jest w nazwie domeny o którą pytamy)PTR – mapowanie adresów IP na nazwy domenowe, realizowane w specjalnym drzewie in-addr.arpa (dla IPv4) lub ip6.arpa (IPv6),
gdzie adres IP zapisywany jest w odwróconej kolejności po bajcie dla IPv4 lub cyfrze szesnastkowej dla IPv6
(zobacz wynik polecenia host z adresem IPv4 i IPv6)
TXT – informacje dodatkowe (np. jakie serwery pocztowe, są upoważnione do wysyłania poczty z tej domeny)SOA – informacje podstawowe o strefie opisującej domenęCNAME – alias na inną domenę (domena którą aliasujemy nie może mieć innych wpisów, nawet SOA)/sbin/init (którego podstawowym zadaniem jest zamontowanie właściwego rootfs). Po jego zakończeniu (lub od razu gdy nie używamy initrd) uruchamiany jest program wskazany w opcji init= jądra (domyślnie typowo /sbin/init) z rootfs wskazanego w opcji root= jądra. W opcji init= można wskazać dowolny program lub skrypt (uruchomiony zostanie z prawami root'a).screen i tmux są tzw. multiplexerami terminala - pozwala na uzyskanie wielu okien konsoli (także np. wyświetlanych jedno obok drugiego) na pojedynczym terminalu. Ponadto pozwalają na odłączanie i podłączanie sesji, co pozwala na łatwe pozostawienie działającego programu po wylogowaniu i powrót do niego później./. W tym punkcie zamontowany jest główny system plików (rootfs), inne systemy plików mogą być montowane w kolejnych katalogach./ oznaczający właśnie korzeń).bash potrafi "znajdować się" gdzieś w tej strukturze plików i miejsce to nazywane jest bieżącym katalogiem roboczym (Present Working Directory). Względem niego będą wyrażane ścieżki nie zaczynające się od korzenia, może być też oznaczony jawnie przy pomocy pojedynczej kropki.sh informację o jej bieżącym katalogu roboczym można wypisać przy pomocy polecenia pwd. Często jest ona także podawana przed znakiem zachęty. Natomiast zmiany tego katalogu można dokonać przy pomocy polecenia cd, po którym podawana jest ścieżka do katalogu który ma być ustawiony jako katalog roboczy. W ten sposób zmienimy katalog względem którego będą interpretowane ścieżki względne w danej powłoce i w programach przez nią uruchamianych po wykonaniu tego polecenia. Zmiana ta nie wpłynie na uruchomione wcześniej i nadal działające w tle programy. Uruchomiony program w trakcie działania może także zmienić swój bieżący katalog roboczy wywołaniem odpowiedniej funkcji systemowej..) oznaczającej aktualny katalog w ścieżce oraz dwóch kropek (..) oznaczających katalog nadrzędny (np. ../../abc to katalog abc w katalogu nadrzędnym katalogu nadrzędnego). W ścieżkach bezwględnych są one jednak zawsze nadmiarowe i dlatego rzadko spotykane, natomiast w ścieżkach względnych pełnią bardzo istotną rolę. Dwie kropki (..) użyte odpowiednio wiele razy (z rozdzielającym je /) pozwalają wrócić ścieżką względną z dowolnego katalogu nawet aż do korzenia, a zatem dojść także do dowolnego innego pliku w naszym drzewie..) użyteczna jest praktycznie jedynie na początku ścieżki (gdzie oznacza katalog względem którego jest interpretowana ścieżka względna, na przykład bieżący katalog roboczy). Nierzadko jest używana nawet jako cała ścieżka do aktualnego katalogu../nazwa), który pozwala na bardziej jednoznaczne zasugerowanie iż mamy na myśli ścieżkę a nie jakąś nazwę. Dotyczy to zwłaszcza uruchamiania programów znajdujących się w bieżącym katalogu, gdyż w tym wypadku nazwa polecenia nie zawierająca ukośnika nie jest traktowana jako ścieżka..) traktowane są jako pliki ukryte i nie będą domyślnie pokazywane przez niektóre z programów.? oznacza dowolny znak* oznacza dowolny (także pusty) ciąg znaków[a-z AD] oznacza dowolny znak z wymienionych w zbiorze ujętym w nawiasach kwadratowych, zbiór może być definiowany z użyciem zakresów, np. a-z AD oznacza dowolną małą literę od a do z włącznie, spację, dużą literą A lub D[!a-z] oznacza dowolny znak z wyjątkiem znaków wymienionych w podanym zbiorze, zbiór może być definiowany z użyciem zakresów, np. a-z oznacza dowolną małą literę od a do z włączniea[0-9]/* oznacza wszystkie (nieukryte) pliki i katalogi znajdujące się wewnątrz katalogów których nazwa jest dwuznakowa, gdzie pierwszym znakiem jest a a kolejnym cyfra.[^ad]* oznacza wszystkie (nieukryte) pliki i katalogi w bieżącym katalogu których nazwa nie zaczyna się na literę a lub d.[!.]* oznacza wszystkie ukryte pliki i katalogi w bieżącym katalogu (z wyjątkiem odwołania do katalogu bieżącego i nadrzędnegolink1.txt, pokazany ilustracji zaznaczony kolorem czerwonym, utworzony komendą ln plik2.txt link1.txt, stanowi po prostu kolejne dowiązanie do tych samych danych do których dowiązaniem był plik2.txt. Jest on równoprawny oryginalnemu dowiązaniu do tych danych, czyli:plik2.txt, podobnie w drógą stronę - modyfikacja dokonana za pomocą plik2.txt będzie widoczna przy dostępie poprzez nazwę link1.txtls z opcją -l. Warto także zwrócić uwagę na wpisy związane z kropką i dwiema kropkami – są to automatycznie tworzone dowiązania typu link twardy odpowiednio do katalogu bieżącego i nadrzędnego. Liczba dowiązań do . to 2 plus liczba podkatalogów, a liczba dowiązań do .. równa jest liczbie dowiązań do kropki w katalogu nadrzędnym.link2.txt) jest odniesienie do innej ścieżki (w tym przykładzie ./plik1.txt). Jest on w istocie odniesieniem do innej ścieżki, a nie danych na dysku jako takich.l a podawany rozmiar wynika z długości przechowywanej ścieżki – tyle danych zawiera sam link symboliczny. Zajętość dysku (podawana przez du) dla linku symbolicznego będzie najczęściej wynosiła zero, gdyż sam link (jeżeli jest odpowiednio krótki) nie zajmuje osobnego miejsca na dysku - zapisywany jest bezpośrednio w strukturze systemu plików (najczęściej bezpośrednio w i-node).ln (używane do tworzenia linków) standardowo zapisze do tworzonego linku literalnie podaną w linii poleceń ścieżkę. Oznacza to że można tworzyć linki symboliczne do nieistniejących plików, ale też przez pomyłkę można utworzyć błędne dowiązania.~) użyta jako pierwszy znak w ścieżce oznacza że jest ona wyrażona względem katalogu domowego naszego lub wskazanego użytkownika - np. ~/abc to katalog abc w naszym katalogu domowym, a ~xyz/abc to katalog abc w katalogu domowym użytkownika xyz./root, aby był dostępny nawet gdy takie montowanie nie doszło do skutku)/run przeznaczony do trzymania danych tymczasowych działających usług takich jak numery pid, blokady, itp/sys zawierający informacje i ustawienia dotyczące m.in. urządzeń\) lub ujęcia zawierającego je napisu w apostrofy (') lub cudzysłowa (").|. Na przykład: ls --help | less. Konstrukcja ta przekieruje wynik komendy ls uruchomionej z opcją --help do komendy less.> lub >>, gdy chcemy dopisywać do pliku) lub pobrać standardowe wejście z pliku (przy pomocy <). 2> pozwala na przekierowanie standardowego wyjścia błędu do pliku.2>&1 w celu przekierowania strumienia drugiego do pierwszego. Następnie można użyć | aby przekierować połączony strumień do następnej komendy. Jeżeli chcemy przekierować go do pliku połączenie strumieni powinno mieć miejsce po przekierowaniu pierwszego z nich do pliku, np.: ls . NieIstniejącyPlik >log.txt 2>&1. Bash pozwala użyć >& i |&, które przekierowują oba strumienie odpowiednio do pliku lub standardowego wejścia innego polecenia, ale jest to rozszerzenie wykraczające poza standardową składnię sh.main). Zero oznacza że polecenie zakończyło się sukcesem (np. znaleziono szukane pliki), wartość nie zerowa że zakończyło się porażką (np. nie ma pasujących plików) lub błędem (np. składnia wprowadzonego polecenia była niepoprawna).a && b – polecenie b wykona się gdy a zakończyło się sukcesem (zwróciło kod 0)a || b – polecenie b wykona się gdy a zakończyło się porażką lub błędem (zwróciło kod różny od 0)a ; b – polecenie b po zakończeniu polecenia a (bez względu na jego kod powrotu)a & b – polecenie b będzie wykonywane równocześnie z a (dokładniej polecenie a zostanie uruchomione w tle, a na terminal zajmie polecenie b)& mogą być dodane do polecenia także gdy nie ma kolejnego w ciągu:
a& uruchomi polecenie a w tle i odda linię poleceń,a; uruchomi polecenie a (dokładnie tak samo jakby nie było tego średnika).man lub info / pinfo. Większość poleceń obsługuje także opcje --help lub -h, które wyświetlają informację na temat ich użycia.|. Np. a [b] c|d oznacza iż polecenie a wymaga argumentu postaci c albo d, który może być poprzedzony argumentem b.apropos -s 1 '' pozwala zapoznać się z spisem wszystkich stron w rozdziale pierwszym (co jest w którym rozdziale można zobaczyć w man man)
--help lub -h, które wyświetlają informację na temat ich użycia. W tym miejscu warto też zauważyć że typowo opcje krótkie (pojedyncza litera) poprzedzane są pojedynczym myślnikiem (i po jednym myślniku może wystąpić kilka kolejnych opcji), natomiast opcje długie poprzedzane są dwoma myślnikami.|. Np. a [b] c|d oznacza iż polecenie a wymaga argumentu postaci c albo d, który może być poprzedzony argumentem b.? oznaczający dowolny znak* oznaczający dowolny (także pusty) ciąg znaków[a-z AD] oznaczający dowolny znak z wymienionych w zbiorze ujętym w nawiasach kwadratowych, zbiór może być definiowany z użyciem zakresów, np. a-z AD oznacza dowolną małą literę od a do z włącznie, spację, dużą literą A lub D[!a-z] oznaczający dowolny znak z wyjątkiem znaków wymienionych w podanym zbiorze, zbiór może być definiowany z użyciem zakresów, np. a-z oznacza dowolną małą literę od a do z włącznie\, np. aaa\ bbb) lub ujęcie całego napisu w cudzysłowie pojedynczym (', np. 'aaa bbb') lub podwójnym (", np. "aaa bbb"). Oba typy cudzysłowów zabezpieczają przed rozwijaniem znaków uogólniających (zastępowaniem napisu ze znakami listą pasujących nazw / ścieżek). Cudzysłów pojedynczy (w odróżnieniu od podwójnego) zabezpiecza także przed interpretacją umieszczonych wewnątrz innych znaków specjalnych takich jak czy odwołania do zmiennych.
-D port tworzy tunel dynamiczny na wskazanym porcie (może on być użyty jako proxy typu SOCKS np. w Firefoxie w celu zapewnienia dostępu do zasobów WWW dostępnych z serwera SSH a niedostępny z komputera lokalnego)-L portNasłuchiwania:hostZdalny:portZdalny tworzy tunel przekierowujący dane kierowane na portNasłuchiwania komputera na którym działa klient ssh do portu portZdalny na serwerze hostZdalny poprzez serwer SSH (przydatne gdy hostZdalny jest osiągalny z hostSSH ale nie z komputera lokalnego)-R portNasłuchiwania:hostZdalny:portZdalny tworzy tunel przekierowujący dane kierowane na portNasłuchiwania komputera na którym działa serwer ssh do portu portZdalny na serwerze hostZdalny dostępnego z klienta SSH (podobnie jak -L, tyle że w drugą stronę - nasłuchiwanie po stronie serwera a host zdalny po stronie klienta)-p port określa inny niż domyślny port serwera SSH-X aktywuje przekazywanie komend X serwera ze strony zdalnej do klienta (pozwala na uruchomienie po stronie zdalnej aplikacji z GUI, które zostanie wyświetlone na lokalnym X serwerze)
-i pozwala na określenie co ile sekund ma aktualizować wynik poleceniaq.less posiada większe możliwości od more, w szczególności posiada możliwość przeglądanie dokumentu w tył oraz umożliwia także wyszukiwanie -- klawisz / pozwala na wprowadzenie szukanej frazy, a n na wyszukanie kolejnego wystąpienia.less:-X nie czyści ekranu przy wychodzeniu z less'a (całość historii wyświetlania pliku pozostaje w historii terminala)-F automatycznie kończy gdy wyświetlany tekst mieści się na jednym ekranie
-R przepuszcza surowe sekwencje sterujące terminalem dotyczące kolorów
Esc powrót do trybu komendi tryb wstawiania; A tryb wstawiania ze skokiem na koniec linii, o / O tryb wstawiania ze wstawieniem nowej linii po / przed bierzącąR tryb zastępowaniaInsert zmiana trybu wstawiania i zastępowaniav tryb wizualny (umożliwia zaznaczenie przy pomocy strzałek); ctrl+v tryb wizualny blokowy, V tryb wizualny liniowy:set paste włącza :set nopaste wyłącza tryb wklejania (nie będzie działać automatyczne formatowanie itp.)gv ponawia ostatnie zaznaczenie trybu wizualnegoy skopiuj; d - wytnij (skopiuj i usuń)
po y, d można podać np. 20l lub 20[strzałka w prawo] co oznacza 20 kolejnych znaków, 2w oznacza dwa słowa
(więcej o takich punktach skoku poniżej)x wytnij (skopiuj i usuń) znak (może być poprzedzone ilością znaków do wycięcia); wielkie X działa analogicznie, tyle że w tyłyy skopiuj linię; dd - wytnij (skopiuj i usuń)
w obu wypadkach może być poprzedzone ilością linii do skopiowania/wycięciap wkleja po; P - wkleja przed" i podajemy przed licznikiem, np. "a3dd wytnie do rejestru a 3 linie), część rejestrów jest używana automatycznie, a niektóre są tylko do odczytu, podgląd aktualnej zawartości rejestrów możliwy jest przy pomocy komendy :registers/ szukanie w przód, ? szukanie w tył; * szukanie w przód słowa pod kursorem, # szukanie w tył słowa pod kursoremn wyszukanie następnego wystąpienie; N wyszukanie poprzedniego wystąpienieG przejście do wskazanej linii, numer podajemy przed G, 0 oznacza ostatnią linię w pliku, więc 0G spowoduje przejście do niej:[zakres]s@regexp@napis@[g] wyszukaj i zastąp wyrażenie regularne regexp przez napis;
zakres może być:
numerem linii,
przedziałem z numerami linii postaci pierwsza,ostatnia, gdzie:
. oznacza bieżącą linię,
$ oznacza ostatnią linię w pliku,
wartość numeryczna poprzedzona + oznacza tyle kolejnych linii od bieżącej, a poprzedzona - przed bieżącą,
znakiem % (co oznacza cały plik),
zakresem zaznaczonym w trybie wizualnym;
podanie opcji g powoduje zastępowanie wszystkich wystąpień a nie tylko pierwszego;
znak @ pełni rolę separatora i może zostać zamiast niego użyty inny znak:e ścieżka otwarcie wskazanego pliku:w zapis (można także podać ścieżkę pod jaka ma zostać zapisany plik):q wyjście:q! wyjście bez zapisywania:wq zapis i wyjście:n następny plik; :N poprzedni plik:split poziomy podział okna; :vs pionowy podział okna; Ctrl+W a następnie strzałka - przełączanie między oknamiu, :undo cofa ostatnią operacjęCtrl+r, :redo ponawia cofniętą operacjęd, y):l / h / k / j jeden znak/linię w prawo / lewo / górę / dół (działa tak jak strzałki)0 / ^ / $ początek linii / początek tekstu w linii, koniec liniiw / b / e następne słowo / poprzednie słowo / koniec słowa; wielkie W / B / E działa analogicznie, różni się traktowaniem spacji przy słowief / F następny / poprzedni znak podany po tej komendzie, włącznie z nim (np. dfX usunie wszystko do najbliższego wystąpienia X wraz z tym X); t / T działa analogicznie, tyle wyłącza podany znak10l - 10 znaków w prawo, 3F: - trzeci dwukropek w lewoG poprzedzane numerem linii do której ma się odbyć skokm i następie znak ją identyfikujący - utworzenie zakładki w miejscu kursora (np. ma - utworzy zakładkę a)` (backtick) / ' (apostrof) skok do zakładki / linii z zakładką podaną po tej komendzie:marks - lista zakładek; :delmarks / :delmarks! - usunięcie zakładki / usunięcie wszystkich nie automatycznych zakładek:r plik, wstawienie zawartości pliku:%!xxd pokazanie wartości numerycznych i umożliwienie edycji pliku jako binarnego; :%!xxd -r powrót do normalnej edycji> / < zwiększanie / zmniejszanie wcięcia zaznaczonego (w trybie wizualnym) tekstuzc zwija bieżący blok, zC zwija bieżący blok aż do najwyższego poziomu, zo rozwija bieżące zwinięcie, zO rozwija rekurencyjnie bieżące zwinięcie, zR rozwija wszystkie zwinięcia w dokumencie:set wrap włącza :set nowrap wyłącza zawijania linii w podglądzie-a wyświetlaj pliki ukryte (zaczynających się od kropki)-l wyświetlaj pliki w formie listy z szczegółowymi informacjami (uprawnienia, rozmiar, data modyfikacji, właściciel, grupa, rozmiar)-1 wyświetlaj pliki w formie 1 plik w jednej linii (bez dodatkowych informacji; stosowane domyślne gdy wynik komendy przekazywany jest strumieniem do innej komendy lub pliku)-h stosuj jednostki typu k, M, G zamiast podawać rozmiar w bajtach-t sortuj wg daty modyfikacji-S sortuj wg rozmiaru-r odwróć kolejność sortowania-c użyj daty utworzenia zamiast daty modyfikacji (stosowane w połączeniu z -l i/lub -t)-d wyświetlaj informacje o katalogu zamiast jego zawartości
-P wypisuj informacje o linkach symbolicznych a nie plikach przez nie wskazywanych (domyślne)-L wypisuj informacje o wskazywanych przez linki symboliczne plikach-name "wyrażenie" pliki których nazwa pasuje do wyrażenia korzystającego z shellowych znaków uogólniających-iname "wyrażenie" jak -name, tyle że nie rozróżnia wielkości liter-path "wyrażenie" pliki których ścieżka pasuje do wyrażenia korzystającego z shellowych znaków uogólniających-ipath "wyrażenie" jak -path, tyle że nie rozróżnia wielkości liter-regex "wyrażenie" pliki których ścieżka pasuje do wyrażenia regularnego-iregex "wyrażenie" jak -regexp, tyle że nie rozróżnia wielkości literwarunek -o warunek łączy warunki sumą logiczną OR zamiast domyślnego iloczynu logicznego AND! warunek negacja warunku-mtime [+|-]n pliki których modyfikacja odbyła się n*24 godziny temu-mmin [+|-]n pliki których modyfikacja odbyła się n minut temu-ctime [+|-]n pliki które zostały utworzone n*24 godziny temu-cmin [+|-]n pliki które zostały utworzone n minut temu-size [+|-]n[c|k|M|G] pliki których rozmiar wynosi n (c - bajtów, k - kilobajtów, M - Megabajtów, G - gigabajtów)-exec polecenie \{\} \; dla każdego znalezionego pliku wykonaj polecenie podstawiając ścieżkę do tego pliku pod \{\} (zastosowane odwrotne ukośniki służą zabezpieczeniu nawiasów klamrowych i średnika przed zinterpretowaniem ich przez powłokę)-execdir polecenie \{\} \;, podobnie jak -exec tyle że polecenie zostanie uruchomione w katalogu w którym znajduje się wyszukany plik
--relative-to=katalog powoduje wypisanie względnej w stosunku co do katalog),-s wyłącza tą funkcjonalność),-m pozwala na operowanie nie istniejącymi ścieżkami,readlink
-s podaje łączną ilość zajętego miejsca dla każdego argumentów (zamiast wypisywać rozmiar każdego pliku)-c podaje łączną ilość zajętego miejsca dla wszystkich argumentów-h stosuje jednostki typu k, M, G-r pozwala na (rekursywne) kopiowanie katalogów-a podobnie jak -r, dodatkowo zachowując atrybuty plików-l zamiast kopiować tworzy twarde dowiązania (hard links)-s zamiast kopiować tworzy linki symboliczne do plików-f nadpisywanie bez pytania-i zawsze pytaj przed nadpisaniem
-s tworzy dowiązania symboliczne zamiast twardych-r używa ścieżki względnej zamiast bezwzględnej przy tworzeniu dowiązań symbolicznych-f nadpisywanie bez pytania-i zawsze pytaj przed nadpisaniem
-r pozwala na na (rekursywne) kasowanie katalogów wraz z zawartością-f usuwanie bez pytania-i zawsze pytaj przed usunięciem
-p pozwala na tworzenie całej ścieżki a nie tylko ostatniego elementu, nie zgłasza błędu gdy wskazany katalog istnieje
-r pozwala na (rekursywne) kopiowanie katalogów-P port określa port SSHcp źródło lub cel w postaci [user@]host:[ścieżka] wskazują na zdalny system dostępny poprzez SSH.
-r pozwala na (rekursywne) kopiowanie katalogów-l kopiuje linki symboliczne jako linki symboliczne (zamiast kopiowania zawartości pliku na który wskazują)-t zachowuje czas modyfikacji plików-u kopiuje tylko gdy plik źródłowy nowszy niż docelowy-c kopiuje tylko gdy plik źródłowy i docelowy mają inne sumy kontrolne--delete usuwa z docelowego drzewa katalogów elementy nie występujące w drzewie źródłowym-e 'ssh' pozwala na kopiowanie na/z zdalnych systemów za pośrednictwem ssh, źródło lub cel w postaci [user@]host:[ścieżka] wskazują na zdalny system--partial --partial-dir=."-tmp-" zachowuje skopiowane częściowo pliki w katalogu .-tmp- (pozwala na przerwanie i wznowienie transferu pliku)--progress pokazuje postęp kopiowania--exclude="wzorzec" pomija (w kopiowaniu i kasowaniu) pliki pasujące do wzorzec (wzorzec może zawierać znaki uogólniające powłoki)-n symuluje pracę (pokazuje co zostałoby skopiowane, ale nie kopiuje)
-p port określa inny niż domyślny port serwera SSH-o workaround=rename, który zapewnia poprawne mv na istniejący plik
tar -xf plik.tar - wypakowuje zawartość niekompresowanego archiwum plik.tar interpretując zapisane w archiwum ścieżki względem bieżącego katalogutar -czf - ścieżka1 [ścieżka2 [...]] | ssh [user@]host 'cat > plik.tgz' - archiwizuje wskazane pliki/katalogi bezpośrednio na zdalny system z użyciem ssh i kompresji gzip
ar rcs libAA.a aa*.o - tworzy bibliotekę libAA.a z wszystkich plików aa*.oar x pakiet.deb - wypakowuje zawartość pakietu Debiana z pliku pakiet.deb (będą to dwa archiwa i plik z wersją formatu pakietu)
-n x określa że ma zostać wyświetlone x ostatnich linii-f uruchamia dopisywania (gdy do pliku zostaną dopisane nowe linie tail je wyświetli)
-n x określa że ma zostać wyświetlone x pierwszych linii
-v zamiast pasujących wypisz nie pasujące-i ignoruj wielkość liter-a przetwarzaj pliki binarne jak tekstowe-E korzystaj z Extended Regular Expressions (ERE) zamiast Basic Regular Expressions (BRE)-P korzystaj z Perl-compatible Regular Expressions (PCRE) zamiast Basic Regular Expressions (BRE)-r rekursywnie przetwarzaj podane katalogi wyszukując w wszystkich znalezionych plikach-R jak -r, ale zawsze podąża za linkami symbolicznymi--exclude="wyrażenie" pomiń pliki pasujące do wyrażenie (może zawierać znaki uogólniające powłoki)-l wypisuje pliki z pasującymi liniami-L wypisuje pliki z bez pasujących linii-f wczytaj wyrażenia z podanego pliku-e może być użyta do poprzedzenia wyrażenia (przydatne zwłaszcza jeżeli chcemy podać kilka)
-r rekursywnie przetwarzaj podane katalogi-u wypisuje różnice w formacie "unified"-c wypisuje różnice w formacie "context"
diff z opcją -y), pozwalając jednak na edycję tych plikówpatch -pn < plik.diff co powoduje zastosowanie zmian opisanych w plik.diff na plikach w bieżącym katalogu,
n określa ilość poziomów ścieżek podanych w pliku łaty które mają zostać zignorowane
-n traktuj liczby jako wartości numeryczne a nie napisy-i ignoruj wielkość liter-r odwróć kolejność sortowania-k n sortuj wg kolumny n-t sep kolumny rozdzielane są przy pomocy separatora sep
-d zamiast zastępować usuwa znaki występujące w zbiór1sed -e 'y#zbiór1#zbiór2#' lub trs -f 'zbiór1 zbiór2'
tr program służy do zamieniania znaków z jednego zbioru na znaki z drugiego zbioru (w odróżnieniu od tr nadaje się do znaków kilku bajtowych - utf8, lepiej od sed'a sprawdza się przy zmianie kodowań - patrz przykład w manualu)-e "polecenie" - wykonuj na pliku polecenie (może wystąpić wielokrotnie celem podania wielu poleceń)-f "plik" - wczytaj polecenia z pliku plik-E - używaj rozszerzonych wyrażeń regularnych-i - modyfikuj podany plik zamiast wypisywać zmieniony na stdout-n - wyłącza domyślne wypisywanie linii, wypisanie musi być wykonane jawnie poleceniem ps@regexp@napis@[g] - wyszukaj dopasowania do wyrażenia regularnego regexp i zastąp je przez napis, podanie opcji g powoduje zastępowanie wszystkich wystąpień a nie tylko pierwszego, znak @ pełni rolę separatora i może zostać zamiast niego użyty inny znaky@zbiór1@zbiór2@ - zastąp znaki z zbiór1 znakami odpowiadającymi im pod względem kolejności znakami z zbiór2, znak @ pełni rolę separatora i może zostać zamiast niego użyty inny znak0,/regexp/ s@regexp@napis@ wykona polecenie s na liniach od początku pliku do linii pasującej do wyrażenia regularnego regexp, czyli zastąpi tylko pierwsze wystąpienie w pliku
-f nnn wypierz kolumny określone przez nnn (np. 1,3-4,6- oznacza kolumnę 1, kolumny od 3 do 4 i od 6, a -3 oznacza 3 pierwsze kolumny)-d sep kolumny rozdzielane są przy pomocy separatora sep (musi być pojedynczym jedno bajtowym znakiem, aby ominąć to ograniczenie należy skorzystać z awk)
-c wypisz liczbę powtórzeń-d wypisz tylko linie z 2 lub więcej wystąpieniami-u wypisz tylko linie z 1 wystąpieniem
grep) zadziała też (mniej lub baradziej sensownie) na plikach tekstowych. Istnieją też narzędzia do pracy z takimi plikami:hexdump -e '1/4 "%010d " 1/2 "%05d " 2/1 "%02x " "\n"' plik każde kolejne 8 bajtów pliku zostanie zinterpretowane jako:
jedna liczba 32 bitowa liczba całkowita (4 bajty), 16 bitowowa liczba całkowita (2 bajty) i dwie liczby jedno bajtowe (wyświetlane szesnastkowo)
/lib/ld-linux.so.2 z tym plikiem i jego opcjami jako parametrem.
-R powodującą rekursywne wykonywanie zmian na drzewku katalogów/plików rozpoczynającym się w podanej ścieżce.xhost LOCAL:użytkownik)-Af powoduje wyświetlenie wszystkich procesów w rozszerzonym formacie wypisywania
/proc/PID/fd/)fg (które wznowi go jako pierwszoplanowy – okupujący terminal) lub bg (które wznowi go jako jako proces w tle – oddając terminal, przodkowi który go posiadał wcześniej)./etc/nsswitch.confping6 najczęściej jest równoważna poleceniu ping z opcją -6 wymuszającą używanie jedynie IPv6,
na starszych systemach komenda ping może nie obsługiwać adresów IPv6 i wtedy konieczne jest stosowanie do nich polecenia ping6),
ważniejsze opcje:-c n wykonaj n (domyślnie pyta do momentu przerwania przy pomocy np. Ctrl-C, lub sygnału wysłanego z uzyciem komendy kill)-n nie zamieniaj adresu IP hosta który odpowiedział na nazwę domenową
polecenie [opcje] host.
Warianty z 6 na końcu nazwy będą używały jedynie adresów IPv6, natomiast polecenia bez 6 na końcu nazwy mogą potrafić ich używać lub nie.
Wszystkie popularne warianty pozwalają na podanie opcji -n wyłączającej zamienianie adresu IP hosta który odpowiedział na jego nazwę domenową.-n nie zamieniaj adresu IP hosta który odpowiedział na nazwę domenową
@adres serwera który chcemy odpytać oraz na określenie (poprzez drugi argument) typu rekordu który chcemy uzyskać
zamiast typu rekordu można podać: ANY (powoduje odpytanie o wszystkie rekordy) lub AXFR (powoduje wysłanie prośby o transfer całej strefy, działa jeżeli dany host ma prawo transferu całej strefy z danego serwera)
-o bind umożliwiającą montowanie katalogu w innym katalogu oraz na -o remount umożliwiającą zmianę parametrów podmontowanego systemu plików
update-pciids)konwert utf8-ascii "inteligentnie" usunie znaki nie ascii z pliku kodowanego w utf-8 (np. znaczki z polskimi ogonkami zamieni na odpowiednie znaki ASCII bez tych ogonków);konwert qp-8bit pozwoli zamienić kodowanie quoted printable na normalne 8 bitowe (rtf-8bit zrobi to z kodowaniem rtf'u)
alias 'bc'='bc <(echo scale=3)' (aby zaraz po starcie mieć 3 miejsca po przecinku) lub alias 'bc'='bc -l' (dla pełniejszej precyzji)cat /dev/ttyS0 > ~/serial.log będzie logowało informacje z portu do wskazanego pliku)/etc/services - mapowanie nazwy usługi na numer portu/etc/hosts - mapowanie nazw hostów na numery IP (lokalna baza)/etc/resolv.conf - adresy serwerów DNS do których mogą być kierowane zapytania o adresy hostów
mail lub sendmail. Z kolei komenda sendEmail pozwala także na wysyłanie poczty przez wskazany serwer SMTP.crontab pozwala oglądać i edytować tablice zaplanowanych zadań cyklicznych (dla cron'a)at pozwala jednorazowo zaplanować zadanie
minuty godzina dzienMiesiaca miesiac dzienTygodnia polecenie.
Wpis oznacza że polecenie ma zostać wykonane jeżeli wszystkie warunki będą spełnione, jeżeli jakiś warunek nie jest nam potrzebny można użyć gwiazdki *,
z kolei */n oznacza wykonywanie jeżeli dana wartość jest podzielna przez n. Np.:*/20 3 * * 1 ls oznacza wykonanie komendy ls w każdy poniedziałek o godzinie 3:00 3:20 i 3:40
anacron pozwalający na mniej precyzyjne planowanie zadań
| zakres tematyczny | nazwa | autor | licencja | opis | link | kopia lokalna |
|---|---|---|---|---|---|---|
| Elektronika | ||||||
| Elektronika | Lessons In Electric Circuits | Tony R. Kuphaldt | Design Science License | Rozbudowany podręcznik do elektroniki od podstaw | EN | |
| Elektronika | Electronics | wikibooks.org | CC-SA | Podręcznik do elektroniki | EN | |
| Podstawy elektroniki | Electronics Fundamentals | wikibooks.org | CC-SA | Podręcznik podstaw elektroniki | EN | |
| Podstawy elektroniki | Practical Electronics | wikibooks.org | CC-SA | Przewodnik rozumienia i tworzenia obwodów elektronicznych | EN | |
| Podstawy elektroniki | Circuit Idea | wikibooks.org | CC-SA | Zbiór wyjaśnień zasad działania popularnych obwodów | EN | |
| Teoria obwodów | Circuit Theory | wikibooks.org | CC-SA | Podręcznik do teorii obwodów | EN | |
| Teoria obwodów | Metody analizy obwodów liniowych | Wojciech Meller | CC-NC-SA | Podręcznik do teorii obwodów | PL | |
| Elektronika analogowa | Analogue Electronics | wikibooks.org | CC-SA | Podręcznik elektroniki analogowej | EN | |
| Elektronika cyfrowa | Digital Circuits | wikibooks.org | CC-SA | Podręcznik do elektroniki cyfrowej | EN | |
| Systemy mikroprocesorowe | Microprocessor Design | wikibooks.org | CC-SA | Podręcznik projektowania systemów mikroprocesorowe | EN | |
| Systemy mikroprocesorowe | Embedded Systems | wikibooks.org | CC-SA | Podręcznik do systemów wbudowanych | EN | |
| Automatyka | Control Systems | wikibooks.org | CC-SA | Podręcznik do systemów kontroli i sterowania | EN | |
| Telekomunikacja | Communication Systems | wikibooks.org | CC-SA | Podręcznik do systemów telekomunikacyjnych | EN | |
| Podstawy informatyki | ||||||
| Systemy operacyjne | Pisanie OS | Jarosław Pelczar i inni | CC-SA, oryginał: PD | Podręcznik przybliżający zagadnienia związane z działaniem i tworzeniem systemów operacyjnych | PL | |
| Sterowniki urządzeń | Linux Device Drivers, Third Edition | Jonathan Corbet, Alessandro Rubini, Greg Kroah-Hartman | CC-NC-SA | Podręcznik opisujący zagadnienia związane z tworzeniem sterowników urządzeń dla systemu Linux | EN | |
| Algorytmy i struktury danych | Algorithms | wikibooks.org | CC-SA | Wprowadzenie do algorytmów | EN | |
| Algorytmy i struktury danych | Data Structures | wikibooks.org | CC-SA | Wprowadzenie do struktur danych | EN | |
| Algorytmy i struktury danych | Algorithm Implementation | wikibooks.org | CC-SA | Przykłady implementacji algorytmów | EN | |
| Języki programowania | ||||||
| C | C Programming | wikibooks.org | CC-SA | Podręcznik programowania w C | EN | |
| C | A Little C Primer | wikibooks.org | CC-SA | Podręcznik programowania w C | EN | |
| C++ | C++ reference | CC-SA | Podręcznik referencyjny dla C i C++ | EN | ||
| C++ | C++ Programming | wikibooks.org | CC-SA | Podręcznik programowania w C++ | EN | |
| C i C++ | Programming Language Concepts Using C and C++ | wikibooks.org | CC-SA | Podręcznik wprowadzający do programowania | EN | |
| Python | Byte of Python / Ukąś Pythona | Swaroop C H | CC-SA, przykłady: BSD | Podręcznik programowania w Pythonie | EN PL | |
| Python | Dive into Python / Zanurkuj w Pythonie | Mark Pilgrim | GNU FDL | Podręcznik programowania w Pythonie | EN PL | |
| Python | How to Think Like a Computer Scientist: Learning with Python | Peter Wentworth, Jeffrey Elkner, Allen B. Downey, Chris Meyers | GNU FDL | Podręcznik programowania w Pythonie | EN EN | |
| Python | Non-Programmer's Tutorial for Python 3 | wikibooks.org | CC-SA | Kurs programowania w Pythonie | EN | |
| PHP | PHP Programming | wikibooks.org | CC-SA | Podręcznik programowania w PHP | EN | |
| Bash | Advanced Bash-Scripting Guide | Mendel Cooper | PD | Podręcznik tworzenia skryptów w bashu | EN | |
| AWK | GAWK: Effective AWK Programming | Arnold D. Robbins | GNU FDL | Podręcznik dla języka AWK w odmianie GNU | EN | |
| AWK | Opis języka AWK | T. Przechlewski | CC-BY-SA type | Wprowadzenie do programowania w AWK | PL | |
| AWK | AWK Reference Card / Ściąga do AWK | Arnold D. Robbins | CC-BY-SA type | Karta referencyjna dla języka AWK | EN PL | |
| AWK | An Awk Primer | wikibooks.org | CC-SA | Podręcznik dla języka AWK | EN | |
| JavaScript | Programming Ajax | wikibooks.org | CC-SA | Podręcznik technologii Ajax | EN | |
| języki opisu sprzętu | Programmable Logic | wikibooks.org | CC-SA | Podręcznik programowania układów logicznych | EN | |
| języki opisu sprzętu | VHDL for FPGA Design | wikibooks.org | CC-SA | Podręcznik programowania układów FPGA w VHDL | EN | |
| języki opisu sprzętu | VHDL quick reference card | Qualis Design Corporation | CC-ND-SA type | Karta referencyjna VHDL | EN | |
| Sieci komputerowe | ||||||
| TCP/IP | Linux Network Administrator's Guide, 2nd Edition | Olaf Kirch, Terry Dawson | GNU FDL | Podręcznik opisujący zagadnienia związane z sieciami TCP/IP w systemie Linux | EN PL | |
| TCP/IP | Linux Network Administrator's Guide, 3nd Edition | Olaf Kirch, Terry Dawson | CC-NC-SA | Podręcznik opisujący zagadnienia związane z sieciami TCP/IP w systemie Linux | EN | |
| TCP/IP | Computer network technologies and services | Lorenzo David, Luca Ghio | CC_SA | Podręcznik opisujący zagadnienia związane z sieciami komputerowymi TCP/IP i usługami sieciowymi | EN EN | |
| TCP/IP | Routing protocols and architectures | Luca Ghio | CC_SA | Podręcznik opisujący zagadnienia związane z routingiem w sieciach komputerowych | EN EN | |
| Ethernet (LAN) | Local Area Network design | Luca Ghio | CC_SA | Podręcznik opisujący zagadnienia związane z lokalnymi sieciami komputerowymi | EN EN | |
| Telekomunikacja | Communication Networks | wikibooks.org | CC-SA | Podręcznik sieci komunikacyjnych (nie tylko komputerowych ale także radiowych, telewizyjnych, itp) | EN | |
| Linux/BSD/Posix | ||||||
| Polecenia systemów typu Posix | Unix Toolbox | Colin Barschel | CC-SA | Zbiór użytecznych poleceń systemów Unix/Linux/BSD | EN | |
| Vim | Ściąga do vi | T. Przechlewski | CC-SA type | Karta referencyjna dla edytora vim | PL | |
| Vim | Ściąga do vi | Laurent Grégoire | GNU GPL | Karta referencyjna dla edytora vim | EN | |
| Debian | Debian Reference | Osamu Aoki | GNU GPL | Podręcznik Debiana | EN | |
| Debian | The Debian Administrator's Handbook | Raphaël Hertzog, Roland Mas | CC-SA / GNU GPL | Podręcznik administratora Debiana | EN | |
| Debian | Debian GNU/Linux Reference Card | W. Martin Borgert, Holger Wansing | GNU GPL | Karta referencyjna dla Debian GNU/Linux | EN PL | |
| FreeBSD | FreeBSD Handbook | BSD-type | Podręcznik dla systemu FreeBSD | EN PL | ||
| Arch | Arch Linux Wiki | GNU FDL | Dokumentacja dla Arch Linux | EN | ||
| PLD Linux | Podręcznik użytkownika, administratora i twórcy | GNU FDL | Podręcznik dla PLD Linux Distribution | PL | ||
| Linux | Linux Networking | wikibooks.org | CC-SA | Podręcznik sieci w środowisku Linux | EN | |
| Języki zapytań i oznaczeń | ||||||
| SQL | Structured Query Language | wikibooks.org | CC-SA | Podręcznik do języka zapytań baz danych SQL | EN | |
| wyrażenia regularne | Regular Expressions | wikibooks.org | CC-SA | Podręcznik wyrażeń regularnych | EN | |
| XML | XML Schema | wikibooks.org | CC-SA | Podręcznik Schcemy XML | EN | |
| LaTeX | The Not So Short Introduction to LaTeX / Nie za krótkie wprowadzenie do systemu LaTeX | Tobias Oetiker, Hubert Partl, Irene Hyna, Elisabeth Schlegl | GNU GPL | Podręcznik wprowadzający do systemu składu tekstu LaTeX | EN PL | |
| LaTeX | LaTeX | wikibooks.org | CC-SA | Podręcznik wprowadzający do systemu składu tekstu LaTeX | EN | |
| Inne, ogólne, wielotematyczne | ||||||
| aparatura przemysłowa | Lessons In Industrial Instrumentation | Tony R. Kuphaldt | Design Science License | Rozbudowany podręcznik do aparatury przemysłowej od podstaw | EN | |
| Wikipedia | CC-SA | Otwarta, wolna, wielojęzyczna encyklopedia | EN PL | |||
| opcode.eu.org | Robert Paciorek | BSD/MIT-type | Serwis internetowy poświęcony różnym zagadnieniom z elektroniki, programowania, sieci i instalacji budynkowych | PL | ||
Copyright (c) 2003-2025, Robert Ryszard Paciorek <rrp@opcode.eu.org> To jest wolny i otwarty dokument/oprogramowanie. Redystrybucja, użytkowanie i/lub modyfikacja SĄ DOZWOLONE na warunkach licencji MIT. This is free and open document/software. Redistribution, use and/or modify ARE PERMITTED under the terms of the MIT license. The MIT License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
char,short,int,long,long longunsigned char,unsigned short,unsigned int,unsigned long,unsigned long longint, choć ten najczęściej ma 32 bitystdint.h):int8_t,int16_t,int32_t,int64_tuint8_t,uint16_t,uint32_t,uint64_tfloat,doubleprintfz biblioteki standardowej języka C (z nagłówkastdio.h).%) pod które będą podstawiane wartości kolejnych zmiennych%określa sposób wypisywania, np.:%d- liczba całkowita typuintwypisana dziesiętnie%x- liczba całkowita bez znaku wypisana szesnastkowo%f- liczba zmiennoprzecinkowa%s- napis%3d- całkowita rezerwująca 3 miejsca (dopełnianie spacjami od przodu)%03d- całkowita rezerwująca 3 miejsca (dopełnianie zerami wiodącymi)%.2d- zmiennoprzecinkowa z dwoma miejscami po kropce%6.2d- zmiennoprzecinkowa z dwoma miejscami po kropce, rezerwująca 6 miejsc (wliczając kropkę i miejsca po kropce)