Πρόσβαση σε Αρχεία και Καταλόγους



  Στο UNIX/Linux υπάχρουν πολλές εντολές για τη διαχείριση αρχείων και καταλόγων, όπως οι  cd, ls, rm, cp, mkdir  κλπ. Τώρα θα δούμε πώς μπορούμε να πετύχουμε παρόμοιες λειτουργίες μέσα από ένα πρόγραμμα C.

Διαχείριση Καταλόγων <unistd.h>

  Αυτή η βιβλιοθήκη περιλαμβάνει τις κατάλληλες συναρτήσεις για την προσπέλαση και έλεγχο των περιεχομένων καταλόγων.

int chdir(char *path) -- μετακίνηση στο κατάλογο που προσδιορίζεται από το όρισμα path. Επιστρέφει 0 για επιτυχή εκτέλεση, διάφορο του μηδενός για σφάλμα.

Παράδειγμα: μια απλή έκδοση της εντολής cd:


#include<stdio.h>
#include<unistd.h>
 
main(int argc,char **argv) 
{ if (argc < 2)
{  printf("Usage: %s <pathname>\n",argv[0]);
exit(1);
}
if (chdir(argv[1]) != 0)
{ printf("Error in chdir\n");
exit(1);
}
}

char *getcwd(char *path, size_t size) --
επιστρέφει το πλήρες όνομα διαδρομής του τρέχοντος καταλόγου (εντολή pwd του UNIX/Linux). Η συμβολοσειρά του ονόματος αποθηκεύευται σο όρισμα path. Το size ορίζει μέγιστο μέγεθος για τη συμβολοσειρά path. Επίσης η getcwd επιστρέφει και ένα δέικτη σε αυτή τη συμβολοσειρά ή δείκτη NULL σε περίπτωση σφάλματος.

Ανάγνωση και Ταξινόμηση Καταλόγων <dirent.h>

Υπάρχουν μερικές εύχρηστες συναρτήσεις υψηλού επιπέδου:

int scandir(char *dirname, struct dirent ***namelist, int (*selector)(const struct dirent *), 
            int (*cmp)(const void *, const void *))
 

Σαρώνει τον κατάλογο με όνομα dirname και επιστρέφει ένα δείκτη namelist ο οποίος δείχνει σε ένα πίνακα δεικτών σε δομές καταλόγου (struct dirent) όπου κάθε δομή περιγράφει ένα στοιχείο του καταλόγο. Ο δείκτης είναι -1 αν προκύψει σφάλμα.

O (*selector)() είναι ένας δείκτης σε συνάρτηση που ορίζεται από το χρήστη και δείχνει σε δομή καταλόγου  (struct dirent) . Η σύνάρτηση αυτή καλείται για κάθε στοιχείου του καταλόγου και επιστρέφει μη-μηδενική τιμή άν το συγκεκριμένο στοιχείο πρέπει να συμπεριληφθεί στον τελικό πίνακα. Αν η τιμή επίστροφής είναι  NULL, το στοιχείο δεν περιλαμβάνεται. Έτσι μπορούμε να επιλέξουμε ποιά στοιχεία του καταλόγου θα εμφανιστούν.

Το τελευταίο όρισμα cmp είναι ένας δείκτης σε μια ρουτίνα που περνά στο qsort --μια ενσωματωμένη συνάρτηση για τη ταξινόμηση του πίνακα των στοιχείων του καταλόγου. Αν ο δείκτης είναι NULL, ο πίνακας δεν ταξινομείται.

H alphasort(const void *,const void *) είναι η ενσωματωμένη συνάρτηση που ταξινομεί τον πίνακα αλφαβητικά.

Η τιμή που επιστρέφει η scandir είναι ο αριθμός των στοιχείων στο πίνακα namelist, δηλαδή των στοιχείων του καταλόγου.

Παράδειγμα, μια απλή έκδοση της εντολής ls 

#include <stdio.h>
#include <dirent.h>

int file_select (struct dirent *unused)
{ return 1;
}

int main (void)
{ struct dirent **eps;
int n;

n = scandir ("./", &eps, file_select, alphasort);
if (n >= 0)
{ int cnt;
for (cnt = 0; cnt < n; ++cnt)
puts (eps[cnt]->d_name);
}
else
perror ("Couldn't open the directory");

return 0;
}

Η file_select() ουσιαστικά επιλέγει όλα τα στοιχεία. Παρακάτω, μια λίγο πιο σύνθετη έκδοση της εντολής ls 

#include <stdio.h>
#include <dirent.h>

 
char pathname[MAXPATHLEN];
 
main()  
{ int count,i;
struct dirent **files;
int file_select();
 
if (getcwd(pathname, MAXPATHLEN) == NULL )
{ printf("Error getting path\n");
exit(0);
}
printf("Current Working Directory = %s\n",pathname);
count = scandir(pathname, &files, file_select, alphasort);
 
/* If no files found, make a non-selectable menu item */
if (count <= 0)
{ printf("No files in this directory\n");
exit(0);
}
printf("Number of files = %d\n",count);
for (i=1; i<count+1; ++i)
printf("%s ",files[i-1]->d_name);
printf("\n"); /* flush buffer */
}
int file_select(struct dirent *entry)   
{if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0))  
  return 0; 
 else return 1; 
}

Η file_select() αποκλείει τον τρέχοντα και τον γονικό κατάλογο (. και ..).

Μπορούμε να προχωρήσουμε παραπέρα κια να αναζητήσουμε συγκεκριμένα αρχεία: Ας τροποποιήσουμε τη file_select() ώστε να επιλέγει αρχεία με κατάληξη .c, .o ή .h:


int file_select(struct dirent *entry)
{ char *ptr;
  char *rindex(char *s, char c);
 
  if ((strcmp(entry->d_name, ".")== 0) || (strcmp(entry->d_name, "..") == 0))
    return 0;
 
  /* Check for filename extensions */
  ptr = rindex(entry->d_name, '.')
  if ((ptr != NULL) && ((strcmp(ptr, ".c") == 0) 
$\mid\mid$ (strcmp(ptr, ".h") == 0)
$\mid\mid$ (strcmp(ptr, ".o") == 0) ))
return 1;
else
return 0;
}

Σημείωση: η συνάρτηση char *rindex(char *s, char c) βρίσκεται στη βιβλιοθήκη string.h και επιστρέφει ένα δείκτη στη τελευταία εμφάνιση ενός χαρακτήρα c στη συμβολοσειρά s, ή ένα δείκτη NULL αν ο c δεν εμφανίζεται. (Υπάρχει και η συνάρτηση char *rindex(char *s, char c) που λειτουργεί παρόμοια για τη πρώτη εμφάνιση.

Δημιουργία και Διαγραφή Καταλόγων <unistd.h>, <sys/types.h>, <sys/stat.h>

int mkdir(char *path, int mode) -- δημιουργία καταλόγου στη διαδρομή path, με άδειες πρόσβασης mode.
int rmdir(char *path) -- διαγραφή άδειου καταλόγου στη διαδρομή path.

Πρόκειται για ισοδύναμα των αντιστοίχων απλών εντολών φλοιού. 

Ο δείκτης path δείχνει σε ένα όνομα διαδρομής. Οι άδειες πρόσβασης mode ορίζονται με μακροεντολές #define , που ορίζεται στη unistd.h και αναλύονται στο εγχειρίδιο της πρότυπης βιβλιοθήκης της C.

Οι συναρτήσεις επιστρέφου: 0 στην επιτυχία, -1 στην αποτυχία. Ταυτόχρονα θέτουν το errno στον κατάλληλο κωδικό σφάλματος αρχείων. Οι κωδικοί σφάλματος αρχείων βρίσκονται στο εγχειριδίο της πρότυπης βιβλιοθήκης της C.

Η πρότυπη βιβλιοθήκη της C περιλαμβάνει επιης πολλές ακόμη συναρτήσεις χαμηλοτέρου επιπέδου που επιτρέπουν λεπτομερέστερη διαχείριση καταλόγων. Επίσης υπάρχουν συναρτήσεις που επιδρούν πάνω σε ιεραρχίες καταλόγων.

Διαχείριση αρχείων <unistd.h>, <sys/types.h>, <sys/stat.h>

Η πρότυπη βιβλιοθήκης της C παρέχει πλήρη σειρά συναρτήσεων για τη διαχείριση αρχείων, έλεγχο της πρόσβασης σε αυτά, διαχείριση των σχετικών σφαλμάτων κλπ.

Πρόσβαση σε Αρχεία

int access(char *path, int mode) -- ελέγχει τις άδειες πρόσβασης σε ένα αρχείο.

Ο δείκτης path δείχνει σε ένα όνομα διαδρομής αρχείου. Η συνάρτηση access() ελέγχει τις άδειες πρόσβασης του συγκεκριμένου αρχείου σύμφωνα με το mode, που ορίζεται στη unistd.h:

R_OK 
- ανάγνωση
W_OK
- εγγραφή
X_OK
- εκτέλεση
F_OK
- έλεγχος αν επιτρέπεται προσπέλαση στους γονικού; καταλόγους ή αν υπάρχει το αρχείο.

Η access() επιστρέφει: 0 στην επιτυχία, -1 στην αποτυχία και θέτει το errno στον κατάλληλο κωδικό σφάλματος. Οι κωδικοί σφάλματος βρίσκονται στο εγχειριδίο της πρότυπης βιβλιοθήκης της C (sys/stat.h).

int chmod(char *path, int mode) -- μεταβάλει τις άδειες πρόσβασης σε ένα αρχείο με όνομα διαδρομής path. Οι άδειες πρόσβασης mode ορίζονται με μακροεντολές #define , που ορίζεται στη unistd.h .

Η chmod() επιστρέφει: 0 στην επιτυχία, -1 στην αποτυχία και θέτει το errno στον κατάλληλο κωδικό σφάλματος. Οι κωδικοί σφάλματος βρίσκονται στο εγχειριδίο της πρότυπης βιβλιοθήκης της C (sys/stat.h). 

Μπορούμε να χρησιμοποιήσουμε τους οκταδικούς κωδικούς αντίστοιχους με τη εντολή chmod τοτ φλοιού, αλλά γενικά είναι καλό να μη χρησιμοποιούμε τους αριθμητικούς κωδικούς του mode αλλά τους ορισμούς των μακροεντολών..

Κατάσταση Αρχείων

Υπάρχουν δύο χρήσιμες συναρτήσεις που δίνουν πολλές πληροφορίες για τη τρέχουσα κατάσταση ενός αρχείου, στη μορφή μιας σύνθετης δομής. Μπορούμε, για παράδειγμα, να μάθουμε πόσο μεγάλο είναι ένα αρχείο (st_size) πότε δημιουργήθηκε (st_ctimeΟι συναρτήσεις ορίζονται στη βιβλιοθήκη sys/stat.h

int stat(char *path, struct stat *buf),  
int fstat(int fd, struct stat *buf)
Η stat()παρέχει πληροφορίες για το αρχείο που το όνομά του δίνεται από το path. Δεν απαιτούνατι δικαιώματα ανάγνωσης, εγγραφής ή εκτέλεσης στο αρχείο αλλά όλοι κατάλογοι στη διαδρομή θα πρέπει να επιτρέπουν ανάγνωση.

Η fstat() παρει τις ίδιες πληροφορίες με βάση τον περιγραφέα αρχείου που επιστρέφεται από τη κληση open (δες Ε/Ε Χαμηλού επιπέδου).

Οι συναρτήσεις stat() και fstat() επιστρέφουν 0 στην επιτυχία, -1 στην αποτυχία και θέτουν το errno στον κατάλληλο κωδικό σφάλματος. Οι κωδικοί σφάλματος βρίσκονται στο εγχειριδίο της πρότυπης βιβλιοθήκης της C (sys/stat.h).

Ο buf είναι ένας δείκτης σε μια δομή stat που περιέχει τις πληροφορίες του αρχείου και ορίζεται στη βιβλιοθήκη sys/types.h, ως εξής:

struct stat {
mode_t st_mode; /* File mode (type, perms) */
ino_t st_ino; /* Inode number */
dev_t st_dev; /* ID of device containing */
/* a directory entry for this file */
dev_t st_rdev; /* ID of device */
/* This entry is defined only for */
/* char special or block special files */
nlink_t st_nlink; /* Number of links */
uid_t st_uid; /* User ID of the file's owner */
gid_t st_gid; /* Group ID of the file's group */
off_t st_size; /* File size in bytes */
time_t st_atime; /* Time of last access */
time_t st_mtime; /* Time of last data modification */
time_t st_ctime; /* Time of last file status change */
/* Times measured in seconds since */
/* 00:00:00 UTC, Jan. 1, 1970 */
long st_blksize; /* Preferred I/O block size */
blkcnt_t st_blocks; /* Number of 512 byte blocks allocated*/
}

Διαγραφή και Μετονομασία Αρχείων <stdio.h>, <unistd.h>

Σε αντιστοιχία με τις συναρτήσεις για καταλόγους και τις εντολές φλοιού UNIX/Linux υπάρχουν συναρτήσεις για τις βασικές λειτουργίες σε αρχεία. Η δημιουργία αρχείου έχει ήδη καλυφθεί από τις συναρτήσεις fopen() και open(). Μένουν  οι λειτουργίες διαγραφής και μετακίνησης (μετονομασίας). Οι πιο απλές συναρτήσεις είναι στη βιβλιοθήκη stdio.h :

int remove(const char *path);  
int rename(const char *old, const char *new);

Δύο πιο σύνθετες συναρτήσεις ορίζονται στη βιβλιοθήκη unistd.h, και στην πραγματικότητα καλούνται από τις συναρτήσεις remove() και rename(), έχουν ενδιαφέρον για τους προγραμματιστές συστημάτων UNIX/Linux.

int unlink(cons char *path) -- διαγράφει το στοιχείο καταλόγου (αρχείο) με όνομα διαδρομής path

Η unlink() επιστρέφει 0 στην επιτυχία, -1 στην αποτυχία και θέτουν το errno στον κατάλληλο κωδικό σφάλματος. Οι κωδικοί σφάλματος βρίσκονται στο εγχειριδίο της πρότυπης βιβλιοθήκης της C (sys/stat.h).

Μια παρόμοια συνάρτηση, η link(const char *path1, const char *path2) δημιουργεί ένα δεσμό (link) μεταξύ μιας υπάρχουσας διαδρομής (αρχείο ή κατάλογος) path1 και μιας νέας διαδρομής path2 .

Δημιουργία Προσωρινών Αρχείων <stdio.h>

Πολλές φορές τα προγράμματα πρέπει να δημιουργήσουν αρχεία που η διάρκεια ζωής τους είναι όση και αυτή του προγράμματος. Για την υποστήριξη αυτής της εργασίας υπάρχουν δύο βασικές συναρτήσεις. Με τη χρήση τους το UNIX/Linux αναλαμβάνει τη διαχείριση των προσωρινών αρχείων.

Η συνάρτηση FILE *tmpfile(void) δημιουργεί ένα προσωρινό αρχείο και ανοίγει την αντίστοιχη ροή. Με τον τερματισμό του προγράμματος και με το κλείσιμο όλων των σχετικών αναφορών το αρχείο διαγράφεται.

Η συνάρτηση char *tmpnam(char *s) επιστρέφει ένα όνομα αρχείου που μπορεί να χρησιμοποιηθεί με ασφάλεια για τη δημιουργία ενός προσωρινού αρχείου. 

Σημείωση: Στη πρότυπη βιβλιοθήκη C υπάρχουν αρκετές ακόμη συναρτήσεις διαχείρισης αρχείων.

Ασκήσεις

Άσκηση 12675

Γράψτε ένα πρόγραμμα C που να προσομοιώνει την εντολή ls -l του UNIX/Linux . MHN κάνετε απλά exec ls -l μέσα από το πρόγραμμα.

Άσκηση 12676

Γράψτε ένα πρόγραμμα C που να εμφανίζει τις γραμμές ενός αρχείου που περιέχουν μια συμβολοσειρά (απλή έκδοση της εντολής grep του UNIX/Linux).

Άσκηση 12677

Γράψτε ένα πρόγραμμα που να προσομοιώνει ένα από σελιδοποιητή, όπως το more του UNIX/Linux)

'Ασκηση 12678

Γράψτε ένα πρόγραμμα που να εμφανίζει τα ονόματα των αρχείων του τρέχοντος καταλόγου καθώς και όλων των υποκαταλόγων.

Άσκηση 12679

Γράψτε ένα πρόγραμμα που εμφανίζει μόνο τους υποκαταλόγους ενός καταλόγου σε αλφαβητική σειρά.

Άσκηση 12680

Γράψτε ένα πρόγραμμα που εμφανίζει τα αρχεία ενός καταλόγου και να ρωτά το χρήστη αν θέλει να αλλάξει τα δικαιώματα σε κάποιο από αυτά. Αν υπάρξει θετική απάντηση, να γίνεται αλλαγή των δικαιωμάτων του συγκεκριμένου αρχείου.

Exercise 12681

Γράψτε ένα πρόγραμμα που εμφανίζει τα αρχεία ενός καταλόγου ένα-ένα και να ρωτά το χρήστη αν θέλει να το διαγράψει. Αν υπάρξει θετική απάντηση το αρχείο διαγράφεται.


Dave Marshall
1/5/1999
μετάφραση και προσαρμογή στα Ελληνικά Κ.Γ. Μαργαρίτης
11/3/2008