piątek, 18 września 2009

Fstab i montowanie podkatalogów

Postanowiłem dzisiaj, że dysk z XPkiem będzie się montował przy każdym starcie systemu. Docelowo chcę ujednolicić katalogi ściągania, obrazów, kodu itd., tak abym nie musiał przeszukiwać raz dysku z XP, raz dysku z Ubuntu w poszukiwaniu tego czym się akurat zajmuję. Na przykład mogę pracować nad sprawozdaniem w OO.org w Windowsie i w Ubuntu, ale nie mam dostępu do partycji ext3 z Windowsa. Dlatego podlinkuję sobie Moje Dokumenty do /home/daniel, a w tym celu potrzebuję odpowiednich wpisów w fstab.

Było z tym trochę problemu, ale nic czego nie załatwiłoby użycie wyszukiwarki.
Po kolei:
Można wpisać "sudo fdisk -l" aby dostać listę wszystkich dysków. Jako że mój dysk jest wewnętrzny i nie ma szansy, aby zmieniła się jego nazwa, to mógłbym odwołać się właśnie po niej: /dev/sda1/. Jednak inne dyski w fstab odwołują się do UUID dysku, więc ja też tak zrobię. Taka mała dygresja: mam dwa dyski zewnętrzne i pendrive'a. Mogę kazać im montować się nie w katalogu /media/disk i /media/disk1, ale na przykład w katalogu /media/czarny i /media/srebrny w zależności od tego który z nich jest podłączony.
Aby uzyskać UUID dysku należy wpisać:
sudo vol_id /dev/sda1
W moim przypadku dostałem:
ID_FS_UUID=5E543A8F543A69C3
Teraz trzeba otworzyć fstab:
gksu gedit /etc/fstab
i na końcu dodać:
#dysk z XP
UUID=5E543A8F543A69C3 /media/XP ntfs-3g defaults 0 0
Ja oddzielam kolumny tabem, ale można też spacją. Po kolei są to: identyfikator dysku, punkt montowania, system plików, opcje i kolejne jakieś 2 parametry, które przeważnie są zerami. Coś związanego z częstotliwością jakiejś czynności, ale nie wiem jakiej bo nie zagłębiałem się w temat. ;) Alternatywnym wpisem może być:
/dev/sda1 /media/XP ntfs-3g defaults 0 0
Tutaj odwołuję się do dysku po jego nazwie. Więcej informacji po czym jeszcze można się odwoływać znajduje się w internecie.
Na koniec zapisujemy zmiany i wracamy do konsoli. Teraz trzeba dać znać systemowi, żeby przemontował wszystkie dyski zgodnie z nowym plikiem fstab:
sudo mount -a
No i błąd:
mount: mount point /media/XP does not exist
Zatem trzeba naprawić to niedopatrzenie:
sudo mkdir /media/XP
Tym razem sudo mount -a nie wyrzuca żadnych komunikatów i można cieszyć się zamontowanym dyskiem.

Teraz przykładowe dowiązanie katalogu z obrazami:
/media/XP/Documents\040and\040Settings/Yelonek/Moje\040dokumenty/Moje\040obrazy /home/daniel/Obrazy none bind 0 0
Co to za \040? Otóż jeśli w nazwie występuje spacja, to zamiast niej dajemy odpowiadający jej "escape sequence". Jest to wartość 32(kod ANSI dla spacji) w systemie ósemkowym. Gdyby wstawić tam zwykłą spację, to po wywołaniu sudo mount -a dostaniemy komunikat, że "fstab is bad", bo program uzna spację za przejście do następnej kolumny.

Ponowne wywołanie sudo mount -a spowoduje, że w katalogu ~/Obrazy zobaczymy plik z Moje obrazy z dysku XP. Ale moment! Przecież w Obrazy miałem kilka innych plików na dysku Ubuntu, a teraz ich nie widać. No tak, nie widać ich, bo teraz Obrazy są dowiązane do Moje obrazy. Nie wszystko jednak stracone. Co zatem zrobić? Można skasować linijkę odpowiadającą za to dowiązanie i zresetować komputer... Ale to bardzo niewygodne. :] Dlatego należy wpisać:
sudo umount ~/Obrazy
i znowu widzimy stare pliki. Można je przenieść do katalogu Moje obrazy i ponownie dowiązać katalogi. Teraz już wszystko jest tak jak być powinno.

* PS ~ (tylda) oznacza katalog domowy użytkownika, czyli w moim przypadku /home/daniel

środa, 16 września 2009

Sqlite - wprawka

Zainteresowałem się ostatnio kolejnym tematem, a mianowicie SQL. Do tej pory słyszałem, że to potężne narzędzie do zarządzania bazami danych, ale jakoś nigdy nie wziąłem się za siebie i nie przysiadłem do tego. Ostatnio mam trochę wolnego czasu, więc zajrzałem na stronę: www.w3schools.com gdzie znajduje się wiele materiałów o SQL i to w bardzo przystępnej formie. Po przejściu podstaw miałem okazję wystosować parę zapytań do treningowej bazy danych. Okazało się to łatwiejsze niż przypuszczałem.

Jako bibliotekę obsługującą bazę danych wybrałem sobie Sqlite3. Jest dość nieskomplikowana i nie trzeba stawiać żadnych serwerów SQL czy coś takiego. Instaluje się ją poleceniem:
sudo apt-get install libsqlite3-0 libsqlite3-dev
Bardzo prawdopodobne, że libsqlite3-0 będzie już w systemie gdyż korzysta z niej na przykład Firefox.

Teraz należy stworzyć nowy projekt, dodać do linkera /usr/lib/libsqlite3.so i napisać trochę kodu:
/* 
* File: main.cpp
* Author: daniel
*
* Created on 14 wrzesień 2009, 22:24
*/

#include <stdlib.h>
#include <sqlite3.h>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using std::cout;
using std::endl;

/*
*
*/

//pokazuje wiadomość błędu jeśli istnieje, a jeśli nie to wartość kodu
void handleReturnCode(int returnCode, char* ErrorMsg = NULL) {
if (returnCode != SQLITE_OK) {
if (ErrorMsg != NULL) {
std::cout << ErrorMsg << std::endl;
sqlite3_free(ErrorMsg);
} else {
std::cout << "Return code: " << returnCode << std::endl;
}
}
}

//struktura przechowująca dane o osobie
struct sOsoba {
std::string imie;
std::string nazwisko;
int rokUrodzenia;

sOsoba(const char* p_imie, const char* p_nazwisko, const int p_rokUrodzenia) {
imie = p_imie;
nazwisko = p_nazwisko;
rokUrodzenia = p_rokUrodzenia;
}
};

int main(int argc, char** argv) {
sqlite3* db;
int returnCode;

cout << "Otwieranie bazy danych: " << endl;
returnCode = sqlite3_open("test2.db", &db);
if (returnCode != 0) std::cout << returnCode << std::endl;
char* ErrorMsg;

cout << "Usunięcie tabeli tab" << endl;
returnCode = sqlite3_exec(db, "drop table if exists tab", NULL, NULL, &ErrorMsg);
handleReturnCode(returnCode, ErrorMsg);

cout << "Utworzenie tabeli tab" << endl;
returnCode = sqlite3_exec(db,
"create table tab (imie string, nazwisko string,
rokUrodzenia integer);",
NULL, NULL, &ErrorMsg);
handleReturnCode(returnCode, ErrorMsg);

cout << "Utworzenie listy osob" << endl;
std::vector<sOsoba> listaOsob;
listaOsob.push_back(sOsoba("Donald", "Tusk", 1957));
listaOsob.push_back(sOsoba("Lech", "Kaczyński", 1949));
listaOsob.push_back(sOsoba("Jarosław", "Kaczyński", 1949));
listaOsob.push_back(sOsoba("Grzegorz", "Napieralski", 1974));
listaOsob.push_back(sOsoba("Waldemar", "Pawlak", 1959));
listaOsob.push_back(sOsoba("Grzegorz", "Schetyna", 1963));

//Dodawanie kolejnych nazwisk
std::vector<sOsoba>::iterator it = listaOsob.begin();
while (it != listaOsob.end()) {
cout << "Dodawanie " << it->imie<< " " << it->nazwisko << endl;
std::stringstream query;
query << "insert into tab values (\""
<< it->imie
<<"\", \""
<< it->nazwisko
<< "\", "
<< it->rokUrodzenia <<");";
returnCode = sqlite3_exec(db, query.str().c_str(), NULL, NULL, &ErrorMsg);
handleReturnCode(returnCode, ErrorMsg);
it++;
}

char** result;
int nRows, nCols;
//zapytanie o polityków którzy urodzili się po 1960
sqlite3_get_table(db,
"select nazwisko, rokUrodzenia
from tab
where rokUrodzenia > 1960
order by rokUrodzenia asc",
&result, &nRows, &nCols, &ErrorMsg);
cout << "Result: [" << nRows << ", " << nCols << "]" << endl;
//zerowy wiersz to nagłówki tabeli, a reszta to wyniki, więc rzędów jest nRows + 1
for (int i = 0; i <= nRows; i++) {
for (int j = 0; j < nCols; j++) {
cout << result[i * nCols + j] << "\t";
if (j > 0 && i>0) {
//dla sprawdzenia czy da się łatwo skonwertować liczbę
int rok = atoi(result[i * nCols + j]);
cout << "\t skonwertowany rok: " << rok;
}
}
cout << endl;
}
//zwalnianie pamięci przydzielonej dla tabeli
sqlite3_free_table(result);

cout << "Zamykanie bazy danych" << endl;
sqlite3_close(db);
//wywołanie polecenia systemowego eksportującego tabelę tab do pliku csv
system("sqlite3 -csv test2.db \"select * from tab;\" > wyjscie.csv");
cout << endl;
//wyświetlenie zawartości pliku csv w konsoli
system("cat wyjscie.csv");

return (EXIT_SUCCESS);
}