I wrote a file parser for a project that parses a file provided on the command line.
However, I would like to allow the user to enter their input via stdin as well, but exclusively through redirection via the command line.
Using a Linux based command prompt, the following commands should yield the same results:
./check infile.txt (Entering filename via command line)
./check < infile.txt
cat infile.txt | ./check
The executable should accept a filename as the first and only command-line argument. If no filename is specified, it should read from standard input.
Edit: I realized how simple it really was, and posted an answer. I will leave this up for anyone else who might need it at some point.
This is dangerously close to "Please write my program for me". Or perhaps it even crossed that line. Still, it's a pretty simple program.
We assume that you have a parser which takes a single FILE* argument and parses that file. (If you wrote a parsing function which takes a const char* filename, then this is by way of explaining why that's a bad idea. Functions should only do one thing, and "open a file and then parse it" is two things. As soon as you write a function which does two unrelated things, you will immediately hit a situation where you really only wanted to do one of them (like just parse a stream without opening the file.)
So that leaves us with:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "myparser.h"
/* Assume that myparser.h includes
* int parseFile(FILE* input);
* which returns non-zero on failure.
*/
int main(int argc, char* argv[]) {
FILE* input = stdin; /* If nothing changes, this is what we parse */
if (argc > 1) {
if (argc > 2) {
/* Too many arguments */
fprintf(stderr, "Usage: %s [FILE]\n", argv[0]);
exit(1);
}
/* The convention is that using `-` as a filename is the same as
* specifying stdin. Just in case it matters, follow the convention.
*/
if (strcmp(argv[1], "-") != 0) {
/* It's not -. Try to open the named file. */
input = fopen(argv[1], "r");
if (input == NULL) {
fprintf(stderr, "Could not open '%s': %s\n", argv[1], strerror(errno));
exit(1);
}
}
}
return parse(input);
}
It would probably have been better to have packaged most of the above into a function which takes a filename and returns an open FILE*.
I guess my brain is fried because this was a very basic question and I realized it right after I posted it. I will leave it up for others who might need it.
ANSWER:
You can fgets from stdin, then to check for the end of the file you can still use feof for stdin by using the following:
while(!feof(stdin))
Related
what is a proper way of writing to /proc or /sys filesystem in linux in c ?
Can I write as I would in any other file, or are there special considerations I have to be aware of?
For example, I want to emulate echo -n mem > /sys/power/state. Would the following code be the right way of doing it?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
FILE *f;
f = fopen("/sys/power/state", "w");
if(f == NULL) {
printf("Error opening file: /sys/power/state\n");
exit(1);
}
fprintf(f,"%s","mem");
fclose(f);
}
Your approach lacks some error handling in the write operation.
The fprintf (or fwrite, or whatever you prefer to use) may fail, e.g. if the kernel driver behind the sysfs file doesn't like what you're writing. E.g.:
echo 17 > /sys/class/gpio/export
-bash: echo: write error: Invalid argument
In order to catch those errors, you MUST check the output of the fprintf to see if all characters that you expected to write were written, and you should also check the output of ferror(). E.g. if you're writing "mem", fprintf should return 3 and there should not be any error set in the stream.
But one additional thing is missing: sysfs are not standard files. For the previous write error to be returned correctly you MUST disable buffering in your stream, or otherwise the fprintf (or fwrite)) may happily end without any error. You can do that with setvbuf like this just after the fopen.
setvbuf (f, NULL, _IONBF, 0);
I'm new to C and I'm trying to open a file and print its content line by line to console.
The source code is attached along with a couple screen shots to show my situation. (The redded-out part contain my computer's directories and personal info). As you can see from the screenshot, the program prints "before" but not "after". Of course, neither does it print out anything from coc.txt.
I can't figure out why this is the case. Everything seems correct and I don't see any errors.
#include <stdio.h>
#include <stdlib.h> // For exit()
const int MAX_LINE_LENGTH = 300;
int main() {
FILE *inputFile;
inputFile = fopen("coc.txt", "r");
char lineRead[MAX_LINE_LENGTH];
printf("before\n");
while(!feof(inputFile)) {
fgets(lineRead, MAX_LINE_LENGTH, inputFile);
puts(lineRead);
}
fclose(inputFile);
printf("after\n");
}
console
coc.txt
Here's a suggested alternative (not tested yet):
#include <stdio.h>
#define MAX_LINE_LENGTH 300
#define NULL 0
int main(int argc, char *argv[]) {
FILE *inputFile;
char fname[MAX_LINE_LENGTH], lineRead[MAX_LINE_LENGTH];
/* Get filename from cmd-line */
if (argc != 2) {
printf ("USAGE: progname <fname>\n");
return 1;
}
/* Try to open file */
if ((inputFile = fopen("coc.txt", "r")) == NULL) {
perror("Could not open file");
return 2;
}
/* Now read the file, and echo back a line at a time */
printf("before...\n");
while(fgets(lineRead, MAX_LINE_LENGTH, inputFile) != NULL) {
printf ("%s", lineRead);
}
printf("\n...after\n");
/* Cleanup and exit */
fclose(inputFile);
return 0;
}
Changes:
Be sure to have a "return" from main ().
In general, a graceful "return" from main() is preferred over a system call to "exit()".
Read the input, then to check for EOF (fgets() == NULL).
Make sure you've opened the file before reading.
Rather than hard-coding the filename, we're reading it from the command line.
Rather than puts() (which always appends a newline, regardless of whether the string already has a newline), we're using printf().
Make sure that the coc.txt file and the read.c files are in the same folder. I executed your original code and it works fine with VS 2017 on windows 10.
Basically what I want to do is have a program with int main(argc, *argv[]) and instead of writing chars into command line, I want to have my program read those words from a file. How could I accomplish this? Is there a special command in Linux for that?
You can use standard redirect operations in a *nix shell to pass files as input:
./myprogram < inputfile.txt
This statement executes your program (myprogram) and pumps the data inside of inputfile.txt to your program
You can also redirect the output of program to a file in a similar fashion:
./myprogram > outputfile.txt
Instead of doing
for(int i = 1; i < argc; i++)
{
insert(&trie, argv[i]);
}
you could doing something like
FILE *input;
char *line;
....
while (fscanf(input, "%ms", &line) != EOF) {
insert(&trie, line);
/* If you make a copy of line in `insert()`, you should
* free `line` at here; if you do not, free it later. */
free(line);
}
Use redirection
yourprogram < youtextfile
will offer the content of yourtextfile as standard input (stdin) to yourprogram. Likewise
yourprogram > yourothertextfile
will send everything the program writes to standard output (stdout) to yourothertextfile
You'll notice when reading man pages that most system calls have a version that works directly with stdin or stdout
For example consider the printf family:
printf ("hello world\n");
is a shorter version of
fprintf (stdout,"hello world\n");
and the same goes for scanf and stdin.
This is only the most basic usage of redirection, which in my opinion is one of the key aspects of "the unix way of doing things". As such, you'll find lots of articles and tutorials that show examples that are a lot more advanced than what I wrote here. Have a look at this Linux Documentation Project page on redirection to get started.
EDIT: getting fed input via redirection ior interactively "looks" the same to the program, so it will react the same to redirected input as it does to console input. This means that if your program expects data line-wise (eg because it uses gets() to read lines), the input text file should be organized in lines.
By default, every program you execute on POSIX-compliant systems has three file descriptors open (see <unistd.h> for the macros' definition): the standard input (STDOUT_FILENO), the standard output (STDOUT_FILENO), and the error output (STDERR_FILENO), which is tied to the console.
Since you said you want read lines, I believe the ssize_t getline(char **lineptr, size_t *n, FILE *stream) function can do the job. It takes a stream (FILE pointer) as a third argument, so you must either use fopen(3) to open a file, or a combination of open(2) and fdopen(3).
Getting inspiration from man 3 getline, here is a program demonstrating what you want:
#define _GNU_SOURCE
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
size_t len;
char *line;
ssize_t bytes_read;
len = 0;
line = NULL;
if (argc > 1)
{
fp = fopen(argv[1], "r");
if (fp == NULL)
{
perror(*argv);
exit(EXIT_FAILURE);
}
}
else
fp = stdin;
while ((bytes_read = getline(&line, &len, fp)) != -1)
printf("[%2zi] %s", bytes_read, line);
free(line);
exit(EXIT_SUCCESS);
}
Without arguments, this program reads lines from the standard input: you can either feed it lines like echo "This is a line of 31 characters" | ./a.out or execute it directly and write your input from there (finish with ^D).
With a file as an argument, it will output every line from the file, and then exit.
You can have your executable read its arguments on the command line and use xargs, the special Linux command for passing the contents of a file to a command as arguments.
An alternative to xargs is parallel.
I'm having trouble writing a C program that displays a command prompt (no problem here) which allows the user to enter unix commands & then displays the results. I've tried many things but I only started programming a year ago and haven't gone anywhere besides displaying the command prompt; I need help on how to accept unix commands + display their results.
My only constraint is that instead of the user providing an absolute path, I need my program to search the directories specified in the path environment variable and find the location of the command's executable. I don't understand how to do this either but searching online has told me this would be best using "getenv() to access the OS PATH variable and prefix the user-supplied command appropriately". Can anyone help me out here? Thanks for your assistance in advance.
Try popen(), which can be found here in the manpages.
Check this out:
#include <stdio.h>
#include <stdlib.h>
void write_netstat(FILE * stream)
{
FILE * outfile;
outfile = fopen("output.txt","w");
char line[128];
if(!ferror(stream))
{
while(fgets(line, sizeof(line), stream) != NULL)
{
fputs(line, outfile);
printf("%s", line);
}
fclose(outfile);
}
else
{
fprintf(stderr, "Output to stream failed.n");
exit(EXIT_FAILURE);
}
}
int main(void)
{
FILE * output;
output = popen("netstat", "r");
if(!output)
{
fprintf(stderr, "incorrect params or too many files.n");
return EXIT_FAILURE;
}
write_netstat(output);
if(pclose(output) != 0)
{
fprintf(stderr, "Could not run 'netstat' or other error.n");
}
return EXIT_SUCCESS;
}
This prints a netstat to a file. You can do this for all commands. It uses popen(). I wrote it because I needed a log of a netstat.
I am trying to pass arguments to the command line in xCode. I have looked up this issue and have found that I need to set the working directory to the path that the file is in. Also I have to add the arguments to the arguments tab under project- edit activeexecutable. I have also done this.
I added michael.txt twice.
/* This file is saved as readtext.c, compiled as readtext */
#include <stdio.h>
void main(int argc, char *argv[])
{
FILE *fin;
char buffer[100];
printf("Michael Mazur\n");
if (argc != 2) {printf("Usage: %s filename\n", argv[0]); exit(1);}
fin = fopen(argv[1], "r");
if (!fin) {printf("Unable to open %s\n", argv[1]); exit(1);}
while (fgets(buffer, 99, fin)) fputs(buffer, stdout);
fclose (fin);
}
I keep reaching the case that there are not 2 arguments being passed. I also ran a little test program and it keeps returning that I only have 1 argument being passed no matter how many I add. Any help?
argv[0] (path to the executable) counts in argc, so if you add michael.txt twice, argc will be 3. A slightly longer description is here. (In general, when something is misbehaving like this, either use a debugger to check the values of all the variables or print them out.)
Make sure both arguments are checked and on separate lines, like this:
Also, in future please mention what version of Xcode you're using; I think from your description it's 3.x, so that's how I answered the question. The user interface varies pretty substantially between versions.