I am trying to create a lot of files using C. I have done mkdir -p directory && touch directory/filename. When my loop reached a file with a special character in it's name, I get the error

syntax error :" unexpected "("

Can you help me?

closed as unclear what you're asking by Michael Homer, Romeo Ninov, Anthon, Anthony Geoghegan, Satō Katsura Aug 30 '17 at 9:05

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • Why "using c"? You don't seem to use C, but the shell (probably bash) – Philippos Aug 30 '17 at 6:18
  • All ready I wrote logic using c .but when program reach that point that time error like this .so can you merge bash script with c ? Can you please evaporate it ? – Pandya chinna Aug 30 '17 at 6:45
  • My answer below shows how to quote a file name with special characters in bash. – Philippos Aug 30 '17 at 7:13

Surround the file path with quotes like this:

touch "directory/file"
  • That doesn't work for file names that contain double quotes or backslashes or $ or `. – Stéphane Chazelas Aug 30 '17 at 9:10

You problem seems to be, that you are trying to create your files/directories using something like system(). Use of system() is highly discouraged, as it spawns a shell to execute the given command. This also means, that the spawned shell will evaulate the full command using its own syntax, hence you'll have to escape characters that are treated specially by the shell.

A more robust (and less error-prone) way to create files and directories from C is using the real C functions offered for this task, namely mkdir() and open()/creat():

#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

  char dir[] = "some(directory)with*special[characters]";
  char file[] = "some(file)with*the[same]characters";
  int fd;

  if (mkdir(dir, 0755) == -1)
    err(EXIT_FAILURE, "creating directory failed");
  if ((fd = open(file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
    err(EXIT_FAILURE, "creating file failed");

  /* ... */

  return 0;

You need to "quote" the filename:

touch "directory/file with whitespace (and special characters)"

Please read about quoting and special characters in the bash manual.

  • That doesn't work for file names that contain double quotes or backslashes or $ or `. – Stéphane Chazelas Aug 30 '17 at 9:10

If you're using system() within C to invoke shell commands that take arguments, best is to use the environment to pass those arguments to avoid having to quote them.

With the path of the file (containing any character) stored at file_path:

if (setenv("FILE", file_path, 1) < 0) {
system("mkdir -p -- \"$(dirname -- \"$FILE\")\" && touch -- \"$FILE\"");

That way, you're passing a fixed string to sh -c (called by system() internally) and don't have to worry with arbitrary code injection vulnerabilities as long as your shell code is correct (note the quotes and -- above).

(the above has the usual sh command substitution issue whereby all trailing newline characters are stripped, so it would fail on a file_path like foo/bar\n/file. I'll leave it to you to decide if you want to make the effort to work around that).

If you wanted to pass the file names in the shell code passed to system(), do not do:

sprintf(cmd, "mkdir -p -- \"%s\" && touch -- \"%s\"", dir_path, file_path);

as that would still be a command injection vulnerability (for instance with a filename like $(reboot) or ";reboot;:". Same if you used single quotes instead of double quotes.

You'd need:

sprintf(cmd, "mkdir -p -- %s && touch -- %s",
  shquote(dir_path), shquote(file_path));

where shquote() would be a function (whose implementation is left as an exercise) that replaces every ' character with '\'' and encloses the result in single quotes. So for instance returns 'foo'\''bar' for foo'bar (bear in mind that the length of the quoted file can be up to 4 * length + 2, which you'll need to take into account when allocating space for the cmd buffer).

That's the safest quoting approaches. Quoting approaches that use double quotes or backslashes all have issues in some locales using exotic charsets that can cause arbitrary code injection vulnerabilities.

Not the answer you're looking for? Browse other questions tagged or ask your own question.