First, I need to execute two commands with system(), for example, I receive an string and open this string with an text editor, like this:
$ ./myprogram string1
And the output should be a command like this:
$ vim string1
But, I cannot find a way to do this like this pseudo code:
system("vim %s",argv[1]); //Error:
test.c:23:3: error: too many arguments to function 'system'
system("vim %s",argv[1]);
Therefore, my solution is store the argv[1] on a char array that already initialized with four characters, like this:
char command[strlen(argv[1])+4];
command[0] = 'v'; command [1] = 'i'; command[2] = 'm'; command[3] = ' ';
And assign the argv[1] to my new char array:
for(int i = 0; i < strlen(argv[1]) ; i++)
command[i+4] = argv[1][i];
And finally:
system(command);
But, if the arguments given to my program has less than 3 characters, its works fine, but if not, some weird characters that I do not expect appear in the output, like this:
./myprogramg 1234
And the output is:
$ vim 12348�M�
How can I solve this bug and why does this happen?
The full code is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc,char **argv) {
char command[strlen(argv[1])+4];
command[0] = 'v'; command [1] = 'i'; command[2] = 'm'; command[3] = ' ';
for(int i = 0; i < strlen(argv[1]) ; i++)
command[i+4] = argv[1][i];
system(command);
return 0;
}
You need to NUL terminate your C-style strings, and that includes allocating enough memory to hold the NUL.
Your array is a byte short (must be char command[strlen(argv[1])+4+1]; to leave space for NUL), and you should probably just use something like sprintf to fill it in, e.g.:
sprintf(command, "vim %s", argv[1]);`
That's simpler than manual loops, and it also fills in the NUL for you.
The garbage you see is caused by the search for the NUL byte (which terminates the string) wandering off into unrelated (and undefined for that matter) memory that happens to occur after the buffer.
The reason you're running into problems is that you aren't terminating your command string with NULL. But you really want to use sprintf (or even better to use snprintf) for something like this. It works similarly to printf but outputs to memory instead of stdout and handles the terminating NULL for you. E.g:
char cmd[80];
snprintf(cmd, 80, "vim %s", argv[1])
system(cmd);
As #VTT points out, this simplified code assumes that the value in argv[1] will be less than 75 characters (80 minus 4 for "vim " minus 1 for the NULL character). One safer option would be to verify this assumption first and throw an error if it isn't the case. To be more flexible you could dynamically allocate the cmd buffer:
char *cmd = "vim ";
char *buf = malloc(strlen(argv[1]) + strlen(cmd) + 1);
sprintf(buf, "%s%s", cmd, argv[1]);
system(buf);
free(buf);
Of course you should also check to be sure argc > 1.
I know that there are already good answers here, but I'd like to expand them a little bit.
I often see this kind of code
system("vim %s",argv[1]); //Error:
and beginners often wonder, why that is not working.
The reason for that is that "%s", some_string is not a feature of the C
language, the sequence of characters %s has no special meaning, in fact it is
as meaningful as the sequence mickey mouse.
The reason why that works with printf (and the other members of the
printf-family) is because printf was designed to replace sequences like
%s with a value passed as an argument. It's printf which make %s special,
not the C language.
As you may have noticed, doing "hallo" + " world" doesn't do string
concatenation. C doesn't have a native string type that behaves like C++'s
std::string or Python's String. In C a string is just a sequence of
characters that happen to have a byte with value of 0 at the end (also called
the '\0'-terminating byte).
That's why you pass to printf a format as the first argument. It tells
printf that it should print character by character unless it finds a %,
which tells printf that the next character(s)1 is/are special and
must substitute them with the value passed as subsequent arguments to printf.
The %x are called conversion specifiers and the documentation of printf
will list all of them and how to use them.
Other functions like the scanf family use a similar strategy, but that doesn't
mean that all functions in C that expect strings, will work the same way. In
fact the vast majority of C functions that expect strings, do not work in that
way.
man system
#include <stdlib.h>
int system(const char *command);
Here you see that system is a function that expects one argument only.
That's why your compiler complains with a line like this: system("vim %s",argv[1]);.
That's where functions like sprintf or snprintf come in handy.
1If you take a look at the printf documentation you will see that
the conversion specifier together with length modifiers can be longer than 1
character.
Related
I've been looking every sscanf post here and I can't find an exact solution suitable for my problem. I was implementing my own Shell and one of the characteristics is that if I find the dollar sign $, I got to replace what is exactly behind with the environmental variable:
cd $HOME should actually be replaced by cd /home/user before I even execute the cd.
My question is what is the code to use sscanf to take out the dollar sign and simply get HOME on the same string? I've been struggling with some null pointers trying this:
char * change;
if (strcmp(argv[1][0],'$')==0){
change = malloc(strlen(argv[y]));
sscanf(argv2[y]+1,"%[_a-zA-Z0-9]",change);
argv2[y]=getenv(change);
}
But this seems to be failing, I'm having a segmentation fault core. (If needed i put more code, my question is specially focused on the sscanf).
Quick explanation argv is an array of pointers to the lines entered and parsed, so actually the content of argv[0] = "cd" and argv[1]="$HOME". I also know that the variable I'm going to receive after the $ has the format %[_a-zA-Z0-9].
Please ignore the non failure treatment.
You asked "is malloc() necessary" in your code snipped and the answer was "no", you could use a simple array. In reality, if you are simply making use of the return of getenv() without modification in the same scope without any other calls to getenv(), all you need is a pointer. getenv() will return a pointer to the value part of the name=value pair within the program environment. However the pointer may be a pointer to a statically allocated array, so any other calls to getenv() before you make use of the pointer can cause the text to change. Also, do not modify the string returned by getenv() or you will be modifying the environment of the process.
That said, for your simple case, you could do something similar to:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char *envp = NULL, /* pointer for return of getenv() */
buf[MAXC]; /* buffer to parse argv[2] w/sscanf */
if (argc < 3) { /* validate at least 2 program arguments given */
printf ("usage: %s cmd path\n", strrchr (argv[0], '/') + 1);
return 1;
}
if (*argv[2] == '$') /* chest 1st char of argv[2] == '$' */
if (sscanf (argv[2] + 1, "%1023[_a-zA-Z0-9]", buf) != 1) {
fputs ("error: invalid format following '$'.\n", stderr);
return 1;
}
if (!(envp = getenv (buf))) { /* get environment var from name in buf */
fprintf (stderr, "'%s' not found in environment.\n", buf);
return 1;
}
printf ("%s %s\n", argv[1], envp); /* output resulting command line */
}
Right now the program just outputs what the resulting command line would be after retrieving the environment variable. You can adjust and build the array of pointers for execv as needed.
Example Use/Output
$ ./bin/getenvhome "cd" '$HOME'
cd /home/david
Look things over and let me know if you have any further questions.
You don't need sscanf here, you can just slide the pointer.
If argv[1] points to the string "$HOME", then argv[1] + 1 points to "HOME", so your example code would just become:
char * change;
if (argv[y][0] == '$')
{
change = argv[y] + 1;
}
(But in this case your variable should not be named change. Call your variables what they represent, for example in this case variable_name, because it contains the name of the shell variable you will be expanding - remember your code is for communicating to other humans your intent and other helpful information about the code.)
To be clear, whether you do sscanf or this trick, you still have to do error checking to make sure the variable name is actually the right characters.
Remember that sscanf won't tell you if there are wrong characters, it'll just stop - if the user writes a variable like $FO)O (because they made a typo while trying to type $FOO) sscanf will just scan out the first valid characters and ignore the invalid ones, and return FO instead.
In this case ignoring bad data at the end would be bad because user interfaces (that includes shells) should minimize the chances that a user mistake silently does an unintended wrong thing.
I have been trying for hours to create a program that outputs something like command[/home/nerdofcode/]: by running something like:
printf("Command[", system("pwd"),"]: ");...
But the problem I am receiving is that when I go to type input, it starts typing at command[... and then once I click enter, it finally outputs the system("pwd");...
Technical Info
I am using the system() function to execute a system command on Linux.
To use printf correctly for a null-terminated string you need to change the parameters:
printf("Command[%s]: ", string_with_result);
In order to get the string_with_result correctly, you need to study the way system() works in your environment. Its return value is implementation specific and therefor does not allow to answer with code which does what you want.
char * string_with_result; /* pointer to null-terminated sequence of char */
This is the declaration for the string result to be used in printf as proposed above.
In case you want to just get the result, but do not insist on using system() check this StackOverflow question and accepted answer:
'pwd' to get path to the current file
This Q/A might be the way on a "unix-ish" environment to actually use system(), it uses popen():
C: Run a System Command and Get Output?
printf does not work as a "concat" operation of comma separated strings. It rather has a format string with placeholders and the arguments for the placeholders.
So you could write:
char *s1 = "hello", *s2 = "world";
printf("%s %s", s1, s2);
But the following code will not concat the both strings; it will rather treat s1 as a format string (without placeholders) and will ignore the argument:
char *s1 = "hello", *s2 = "world";
printf(s1, s2);
Note further that int system(const char* cmd) does not return a string, which you could use in printf then, at all. So I'd recommend to write.
printf("Command[");
system("pwd");
printf("]: ");
This should work on console as long as system-command and your programsstdout` target the same output stream.
How can I make this print properly without using two printf calls?
char* second = "Second%d";
printf("First%d"second,1,2);
The code you showed us is syntactically invalid, but I presume you want to do something that has the same effect as:
printf("First%dSecond%d", 1, 2);
As you know, the first argument to printf is the format string. It doesn't have to be a literal; you can build it any way you like.
Here's an example:
#include <stdio.h>
#include <string.h>
int main(void)
{
char *second = "Second%d";
char format[100];
strcpy(format, "First%d");
strcat(format, second);
printf(format, 1, 2);
putchar('\n');
return 0;
}
Some notes:
I've added a newline after the output. Output text should (almost) always be terminated by a newline.
I've set an arbitrary size of 100 bytes for the format string. More generally, you could declare
char *format;
and initialize it with a call to malloc(), allocating the size you actually need (and checking that malloc() didn't signal failure by returning a null pointer); you'd then want to call free(format); after you're done with it.
As templatetypedef says in a comment, this kind of thing can be potentially dangerous if the format string comes from an uncontrolled source.
(Or you could just call printf twice; it's not that much more expensive than calling it once.)
Use the preprocessor to concatenate the two strings.
#define second "Second%d"
printf("First%d"second,1,2);
Do not do this in a real program.
char *second = "Second %d";
char *first = "First %d";
char largebuffer[256];
strcpy (largebuffer, first);
strcat (largebuffer, second);
printf (largebuffer, 1, 2);
The problem with using generated formats such as the method above is that the printf() function, since it is a variable length argument list, has no way of knowing the number of arguments provided. What it does is to use the format string provided and using the types as described in the format string it will then pick that number and types of arguments from the argument list.
If you provide the correct number of arguments like in the example above in which there are two %d formats and there are two integers provided to be printed in those places, everything is fine. However what if you do something like the following:
char *second = "Second %s";
char *first = "First %d";
char largebuffer[256];
strcpy (largebuffer, first);
strcat (largebuffer, second);
printf (largebuffer, 1);
In this example the printf() function is expecting the format string as well as a variable number of arguments. The format string says that there will be two additional arguments, an integer and a zero terminated character string. However only one additional argument is provided so the printf() function will just use what ever is next on the stack as being a pointer to a zero terminated character string.
If you are lucky, the data that the printf() function interprets as a pointer will a valid memory address for your application and the memory area pointed to will be a couple of characters terminated by a zero. If you are less lucky the pointer will be zero or garbage and you will get an access violation right then and it will be easy to find the cause of the application crash. If you have no luck at all, the pointer will be good enough that it will point to a valid address that is about 2K of characters and the result is that printf() will totally mess up your stack and go into the weeds and the resulting crash data will be pretty useless.
char *second = "Second%d";
char tmp[256];
memset(tmp, 0, 256);
sprintf(tmp, second, 2);
printf("First%d%s", 1,tmp);
Or something like that
I'm assuming you want the output:
First 1 Second 2
To do this we need to understand printf's functionality a little better. The real reason that printf is so useful is that it not only prints strings, but also formats variables for you. Depending on how you want your variable formatted you need to use different formatting characters. %d tells printf to format the variable as a signed integer, which you already know. However, there are other formats, such as %f for floats and doubles, %l% for long integers, and %s for strings, or char*.
Using the %s formatting character to print your char* variable, second, our code looks like this:
char* second = "Second";
printf ( " First %d %s %d ", 1, second, 2 );
This tells printf that you want the first variable formatted as an integer, the second as a string, and the third as another integer.
Given the following program:
#include <stdio.h>
int main()
{
char buf[1024];
scanf("%s", buf);
printf("----> %s", buf);
return 0;
}
which is executed as follows:
grep ....| a.out
or
echo ....| a.out
I get a Segmentation fault error. Can anyone explain why?
Whatever you are echoing or grepping must contain more than 1023 characters. (1024 - 1 for the null terminator.)
Instead of using scanf, use fgets and specify a size. Alternatively, use scanf but specify the field length. You can do scanf("%1023s", buf);. If there's more bytes available, you can always do it again to read in the rest.
Given your test input, you should not receive a segfault. I just tried it locally and it worked fine. If you are on Linux, since you wrote a.out instead of ./a.out, depending on how your path is configured you may be running the wrong program (some sort of a.out in your bin folder?)
Don't ever use scanf with unbounded strings. fgets provides a much safer alternative, especially if you provide an intelligent wrapper function like the one in this answer.
I'm assuming that's just sample code here but, just in case it isn't, you can achieve the same effect with:
WhateverYourCommandIs | sed 's/^/----> '
without having to write your own tool to do the job. In fact, with sed, awk and the likes, you probably never need to write text processing tools yourself.
from scanf man:
s Matches a sequence of non-white-space characters; the next pointer must be a pointer to character array that is long enough to hold the input sequence and the terminating null character ('\0'), which is added automatically. The input string stops at white space or at the maximum field width, whichever occurs first.
specifying maximum field width will prevent stack overrun
scanf("%1023s", buf);
and to ensure stack no overrun on printf use memset:
memset(buf,0,1024);
so, programm will be:
#include <stdio.h>
#include <string.h>
int main()
{
char buf[1024];
memset(buf,0,1024);
scanf("%1023s", buf);
printf("----> %s", buf);
return 0;
}
I have a simple question. I want to write a program in C that scans the lines of a specific file, and if the only phrase on the line is "Atoms", I want it to stop scanning and report which line it was on. This is what I have and is not compiling because apparently I'm comparing an integer to a pointer: (of course "string.h" is included.
char dm;
int test;
test = fscanf(inp,"%s", &dm);
while (test != EOF) {
if (dm=="Amit") {
printf("Found \"Atoms\" on line %d", j);
break;
}
j++;
}
the file was already opened with:
inp = fopen( .. )
And checked to make sure it opens correctly...
I would like to use a different approach though, and was wondering if it could work. Instead of scanning individual strings, could I scan entire lines as such:
// char tt[200];
//
// fgets(tt, 200, inp);
and do something like:
if (tt[] == "Atoms") break;
Thanks!
Amit
Without paying too much attention to your actual code here, the most important mistake your making is that the == operator will NOT compare two strings.
In C, a string is an array of characters, which is simply a pointer. So doing if("abcde" == some_string) will never be true unless they point to the same string!
You want to use a method like "strcmp(char *a, char *b)" which will return 0 if two strings are equal and something else if they're not. "strncmp(char *a, char *b, size_t n)" will compare the first "n" characters in a and b, and return 0 if they're equal, which is good for looking at the beginning of strings (to see if a string starts with a certain set of characters)
You also should NOT be passing a character as the pointer for %s in your fscanf! This will cause it to completely destroy your stack it tries to put many characters into ch, which only has space for a single character! As James says, you want to do something like char ch[BUFSIZE] where BUFSIZE is 1 larger than you ever expect a single line to be, then do "fscanf(inp, "%s", ch);"
Hope that helps!
please be aware that dm is a single char, while you need a char *
more: if (dm=="Amit") is wrong, change it in
if (strcmp(dm, "Amit") == 0)
In the line using fscanf, you are casting a string to the address of a char. Using the %s in fscanf should set the string to a pointer, not an address:
char *dm;
test = fscanf(inp,"%s", dm);
The * symbol declares an indirection, namely, the variable pointed to by dm. The fscanf line will declare dm as a reference to the string captured with the %s delimiter. It will point to the address of the first char in the string.
What kit said is correct too, the strcmp command should be used, not the == compare, as == will just compare the addresses of the strings.
Edit: What kit says below is correct. All pointers should be allocated memory before they are used, or else should be cast to a pre-allocated memory space. You can allocate memory like this:
dm = (char*)malloc(sizeof(char) * STRING_LENGTH);
where STRING_LENGTH is a maximum length of a possible string. This memory allocation only has to be done once.
The problem is you've declared 'dm' as a char, not a malloc'd char* or char[BUFSIZE]
http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/
You'll also probably report incorrect line numbers, you'll need to scan the read-in buffer for '\n' occurences, and handle the case where your desired string lies across buffer boundaries.