tags:

views:

345

answers:

8

i am trying to use the rm command in the main routine for deleting a file that i have taken a command line argument. The value is stored in argv[2] i have tried using

system("rm argv[2]");

system("rm ./argv[2]");

system("rm $HOME/argv[2]");

But it gives me an error saying

"cannot locate file argv[2]"

The filename is stored in argv[2] as I have checked it.

Someone please guide me!

+7  A: 

The "argv[2]" in "rm ./argv[2]" is a literal string, if you want to use what is stored in argv[2], you need to use strcpy() or sprintf() to connect "rm ./" with the string stored in argv[2].

ZelluX
@Zellux +1 great!
TheMachineCharmer
I would recommend using **strncpy()** or **snprintf()** over **strcpy()** or **sprintf()** to restrict the number of characters copied to avoid buffer overflow as the string passed in via `argv[2]` is of arbitrary length.
jschmier
+1  A: 

You need to construct the string first. Just putting argv[2] in quotes won't work.

Example:

char buf[256];

buf[255] = '\0';
if (snprintf(buf, 255, "rm %s", argv[2]) != -1) {
    system(buf);
}
Moron
This has a buffer overflow (the last element is `buf[255]`). That said, snprintf will always write a 0 terminator so no need to manually add one. Also, no need to subtract 1 from the length of the array when calling snprintf.
R Samuel Klatchko
Yeah, typo. I will correct that. I intended buf[255] and that is why calling snprintf with 255. Not sure about snprintf behavior (probably depends on the os/library you are using). It only adds 0 at end if the buffer is long enough, I think. So there are cases where you might still miss it.
Moron
+13  A: 

Why not use the remove or unlink command instead of system("rm ...")?

remove(argv[2]); or unlink(argv[2]);


Update in the case that system("rm ...") must be used

If you must use system("rm ..."), ZelluX is correct in stating that you must retrieve the filename from argv[2]. You can retrieve the string in argv[2] using snprintf or strncpy. Using the variants of the functions that restrict the input size is a good idea as there is no guarantee as to the length of the string in argv[2].

Depending on your application, you may also want to call stat to verify that the string in argv[2] is indeed a file and possibly restrict the type of file.

Example:

This example calls stat to verify that argv[2] is a regular file and asprintf to dynamically allocate space for the buffer.

char *p;
struct stat st;

if (stat(argv[2], &st) == 0 && S_ISREG(st->st_mode))
{
    if (asprintf(&p, "rm %s", argv[2]) != -1)
    {
        system(p);
        free(p);
    }
}
jschmier
I can only assume this got four upvotes before the horrendously bad advice "You will still need to retrieve the string in argv[2] using snprintf or strncpy" was added. What point is there in copying the string to a new string if you're not going to modify it, other than to increase the chance of buffer overflows?
Miles
not sure how there would be a buffer overflow using snprintf or strncpy, but you are correct that this step is not necessary. answer rolled back.
jschmier
The question didn't say so, but you could pass parameters to rm using just argv[2], in which case unlink won't work :-)
Moron
@Moron - the question noted that he tried "rm ./argv[2]" and "rm $HOME/argv[2]" which I infer as meaning he only expects a filename in argv[2].
R Samuel Klatchko
@Samuel: Yes, I was just making a tangential point (hence the smiley).
Moron
But in my opinion the problem lies in that hitesh123 doesn't understand the difference between literal strings and string variables, and advice him to use another function won't help him understand it :P
ZelluX
i know the difference...there is a restriction on me to use the system("rm..") function as i am writing a lex grammar on a fedora 11 gcc compiler...so argv[2] is being taken literally in thw quotes where as its literal meaning has to be escaped ...do u know any way to do so...?
Hitesh Dharmadasani
@hitesh - If you are saying that `"argv[2]"` needs to be converted to the variable `argv[2]`, you would need to make your own data structure to map the string to the corresponding variable. You could then look up the pointer by the name of the variable, dereference it, and you have your variable back.
jschmier
actually i want the reverse..the quotes in the system() function are taking argv[2] literally..i want to take the value in argv[2]...
Hitesh Dharmadasani
@hitesh - I don't believe **C** has the reflection facilities you are looking for.
jschmier
+2  A: 

argv[2] contains the string and it itself is not the string. Please use strcpy or other string manipulation mechanism to get the string from argv[2] into a buffer and then construct a command using rm and then pass it to system. That should work.

Jay
+2  A: 

instead of calling system rm command, do it in C, eg

#include <stdio.h>
#include <dirent.h>
int  main()
{
        struct dirent *d;
        DIR *dir;
        char buf[256];
        dir = opendir("mydir");
        while(d = readdir(dir))
        {               
                sprintf(buf, "%s/%s", "mydir", d->d_name);
                remove(buf);
        }
        return 0;

}
ghostdog74
+2  A: 

See also unlink(2) (defined in unistd.h) if you want to remove files without calling system().

Soiha
A: 

the problem is that argv[2] does not get expanded and is passed just like that, as there is no file named argv[2], there will be an error. A solution would be to produce a string using sprintf() and then pass it to system()

char str[100];
sprintf(str,"rm %s",argv[2]);
system(str);
+1  A: 

Using system like this (in fact, using it at all) is a formula for disaster. Suppose argv[2] contains "-rf /". :-)

R..
+1: you do have a point, although limited user permissions may stop you from doing that =)
rubenvb
That's just one example. The larger issue is that using `system` requires you to consider both (1) how the shell will interpret the string before passing it to the program, and (2) how the program might misinterpret as options part or all of a string which you intended to be interpreted as a filename. It's possible to work around these issues with proper escaping (backslash or quotes for the shell and `--` for `rm`) but is it worth the risk?
R..