Linux system programming: Open file, read file and write file
This is my first article in what I’m hoping will be a series of articles on system programming for POSIX compliant operating systems with focus on Linux. Actually I’ve touched this topic a while ago when I wrote three articles about library programming on Linux (static libraries, dynamic libraries and dynamic libraries using POSIX API). In this series my goal is to go trough basics of Linux system programming from the easiest topics like open file, read file and file write to a bit more complicated things like Berkeley sockets network programming. So lets get started with environment setup and an example of program that copies source file into destination file using POSIX API system calls to demonstrate open(), read() and write() system calls on Linux operating system.
Configuring your environment
I’ll use my trustworthy Ubuntu Linux operating system but you can actually use any POSIX compliant operating system, the only difference will probably be that you will need to configure your environment differently. What we need to begin with Linux system programming is gcc compiler with related packages and POSIX related man pages. So here’s how to install this packages on Ubuntu based operating system:
sudo apt-get install build-essential manpages manpages-dev manpages-posix manpages-posix-dev
Basically that’s all you need to create serious system tools for Linux operating system. Later we will probably need some more libraries but we will install them when necessary.
open(), read() and write() system calls
Lets continue with our first system call open() whose purpose is to open file for reading or writing or to create new file. You should open it’s man page if you haven’t already done so using man 2 open command and read trough basics (2 is manual section number, use man man to read more about integrated manual section numbers). In the following example we also use read() and write() system calls to copy from one file descriptor to the other (both descriptors returned by open() system call) so it is wise to open their man pages as well ( man 2 read and man 2 write ). So here’s the example code for program that copies input file passed as first argument into output file passed as second argument:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
/* ============================================================================ Name : sp_linux_copy.c Author : Marko Martinović Description : Copy input file into output file ============================================================================ */ #include #include #include #include #include #include #define BUF_SIZE 8192 int main(int argc, char* argv[]) { int input_fd, output_fd; /* Input and output file descriptors */ ssize_t ret_in, ret_out; /* Number of bytes returned by read() and write() */ char buffer[BUF_SIZE]; /* Character buffer */ /* Are src and dest file name arguments missing */ if(argc != 3){ printf ("Usage: cp file1 file2"); return 1; } /* Create input file descriptor */ input_fd = open (argv [1], O_RDONLY); if (input_fd == -1) { perror ("open"); return 2; } /* Create output file descriptor */ output_fd = open(argv[2], O_WRONLY | O_CREAT, 0644); if(output_fd == -1){ perror("open"); return 3; } /* Copy process */ while((ret_in = read (input_fd, &buffer, BUF_SIZE)) > 0){ ret_out = write (output_fd, &buffer, (ssize_t) ret_in); if(ret_out != ret_in){ /* Write error */ perror("write"); return 4; } } /* Close file descriptors */ close (input_fd); close (output_fd); return (EXIT_SUCCESS); }
If you have named this code file sp_linux_copy.c and if you want to name executable file sp_linux_copy to compile this program you would probably use something like this:
gcc -Wall -o sp_linux_copy sp_linux_copy.c
Then if your source file is named source_file.txt and if you want to name the destination file destination_file.txt you would run this program like this:
./sp_linux_copy source_file.txt destination_file.txt
Now lets go trough the code and explain tricky parts. First thing we must do is to include necessary header files. Man page of every system call tells you what header files you need to include to be able to use this system call. Second we will define constant we will use to define size of our buffer in bytes. Smaller buffer size will make our copy process longer but it will save memory. Next we open source and destination file descriptors, source with O_RDONLY to make it read only, destination with O_WRONLY | O_CREAT to make it writable and to create destination file with 0644 file system permission flags. In case of error we use perror() man 3 perror to print relatively user friendly error message.
Now we are ready to start copy process. We run read() and write() inside loop (because source file might be bigger than our buffer) to copy from one file into another. Important to notice is that write() is using number of bytes read from source file returned by read() so it would know how much to write into destination file. If number of bytes read (ret_in) and number of bytes written (ret_out) differ this indicates error so once again we use perror() to print out error description. At the end if all went well we do cleanup by closing both file descriptors and returning 0 (EXIT_SUCCESS) to indicate that program ended without errors.
That’s it for this introductory article on Linux system programming topic. In my next article I will show you few more examples on POSIX input/output and then move on to memory management related system calls.
How to open file with default program from command line?
If I click on a file in a GUI file explorer, a default program is used to open it. This is useful for files which I don’t know how to open from the command line. Is there a way to open a file with the default program using command-line instead? Alternatively, is there a way, given a file extension, to determine a command to open it?
1 Answer 1
Is there a way to open a file with the default program using command-line instead?
xdg-open opens a file or URL in the user’s preferred application. If a URL is provided the URL will be opened in the user’s preferred web browser. If a file is provided the file will be opened in the preferred application for files of that type. xdg-open supports file, ftp, http and https URLs.
xdg-open comes pre-installed in Ubuntu.
Alternatively, is there a way, given a file extension, to determine a command to open it?
By the extension, no. The default application is chosen based upon the MIME type, not the extension. Linux does not have «extensions».
For a specific file, you can use xdg-mime to determine both the file’s MIME type and the default application associated to it:
- Syntax for displaying a file’s MIME type:
xdg-mime query default "$(xdg-mime query filetype )"
As a (hacky) workaround you could use the command
xdg-mime query default \ `xdg-mime query filetype "$(find ~ / -iname '*.png' -print -quit)"`
to display the default application for, e.g., PNG images.
This will work if and only if you have a PNG image on your computer and the first found file ending with .png is a valid PNG image.
`open` command to open a file in an application
Wasn’t it supposed open this file on my browser? Also can’t I run this command: open index.html -a «Sublime Text» . The result of these commands are:
$ open index.html Couldn't get a file descriptor referring to the console $ open index.html -a "Sublime Text" - open: invalid option -- 'a' Usage: open [OPTIONS] -- command
I assume you mean xdg-open which should open the file in your browser. The open command is a link to the openvt command and opens a binary in a new virtual console. That’s apparently not what you want.
@slm, as Marco points out, there is an open command on Linux, linked to openvt ; the error messages in the question are consistent with that situation.
@EdgarOliveira If you want to open a program (a binary) just type its name into the terminal. If you want to open a file with an associated program, use xdg-open
4 Answers 4
The primary purpose of OS X’s open command is to open a file in the associated application. The equivalent of that on modern non-OSX unices is xdg-open .
xdg-open doesn’t have an equivalent of OSX’s open -a to open a file in specific application. That’s because the normal way to open a file in an application is to simply type the name of the application followed by the name of the file. More precisely, you need to type the name of the executable program that implements the application.
Linux, like other Unix systems (but not, as far as I know, the non-Unixy parts of OS X) manages software by tracking it with a package manager, and puts individual files where they are used. For example, all executable programs are in a small set of directories and all those directories are listed in the PATH variable; running sublime_text looks up a file called sublime_text in the directories listed in PATH . OS X needs an extra level of indirection, through open -a , to handle applications which are unpacked in a single directory tree and registered in an application database. Linux doesn’t have any application database, but it’s organized in such a way that it doesn’t need one.
If running the command sublime_text shell doesn’t work for you, then Sublime Text hasn’t been installed properly. I’ve never used it, and apparently it comes as a tar archive, not as a distribution package (e.g. deb or rpm), so it’s possible that you need to do an extra installation step. It’s really the job of the makers of Sublime Text to make this automatic, but if they haven’t done it, you can probably do it yourself by running the command
sudo -s …/sublime_text /usr/local/bin
Replace … by the path where the sublime_text executable is, of course.
The open command you encountered is an older name for the openvt command (some Linux distributions only include it under the name openvt ). The openvt command creates a new virtual console, which can only be done by root and isn’t used very often in this century since most people only ever work in a graphical window environment.