How do I apply code to all files within a folder? - c

I'm relatively new to programming, but created a c script to rotate images and create a copy of them. However, I was hoping to apply it to a large number of files, so I was wondering if there was a way to apply a program / function to all images within a specific folder. Thanks!

If the program gets the filenames to process in its command line arguments (the argv parameter to main()), you can use a wildcard in the command line when you run it:
yourprogram /path/to/directory/*
The program should simply loop through all the filenames in argv.
If it can only process one file at a time, you can use a shell loop:
for file in /path/to/directory/*
do
yourprogram "$file"
done

You could pass wildcards to the executable. The shell would expand the wildcard to all files it matches. Your main program could look like this:
int main(argc, char ** argv)
{
for(int arg=1; arg<argc; arg++)
doStuffToFile(argv[arg]);
}
That is the best solution if you plan to use this quite a lot. If you just need a quick fix you'd be better of with a shell script. This is how it would be done in bash:
for file in /path/*
do
[ -e "$file" ] || continue
program "$file"
done

Related

Can't read file generated by bash line

I have written a C code where the bash script lines are used inside this C code, and this is how I wrote it:
printf("wc -l < smallerthan > number_lines\n");
if( (fr=fopen(fname_rcut,"r"))==NULL ) { printf("error in rcut file: %s\n",fname_rcut); exit(1); }
I need to read the file "number_lines" which is generated from "smallerthan" file, the problem is when I source the C code to run automatically like:
$gcc myC_code.c -lm
$./a.out > run.sh
$source run.sh
Then if I view the run.sh
& vi run.sh
I get this inside run.sh:
wc -l < smallerthan > number_lines
ls
error in rcut file: /home/number_lines
which mean the code upto this point didn't find my "number_lines" file yet since the number_lines file is yet to appear, but if I copy the line and run it separately, instead of "automatically", then it works because the file is there now.
My question is, how to make my code run automatically and my C code to read the file which is generated by bash line or how to generate the file and read it properly?
Any idea please because I'm really new to programming and I have to use bash inside C for my work.
Note: the above is only small part of my C code but I used several bash lines inside my C code.
There are a number of observations in your code. I assume that char *fname_rcut indeed points to "/home/number_lines".
First observation: if you write commands to a file, they will not be executed.So the file number_lines is created only after you run run.sh. Therefore, the file will not exist during the execution of your C program. You might look into int system(const char *command) (man 3 system).
Second observation: /home/number_lines is probably not the correct filename. It would probably be /home/your_name/number_lines; try a pwd to see what the exact directory name is.
Third observation: Why do you want to source run.sh? Source executes the file in the current shell. There is usually no need for that.
I have solved it :
what we need actually is using system(command) after each shell command
for example :
printf("wc -l < smallerthan > number_lines\n");
will be after solving :
sprintf(command1,"wc -l < smallerthan > number_lines\n");
system(command1);

Executing binary files using exec* in c

I am creating a toy shell. I want to execute a binary file which is either located in the PATH variable or the current directory. This is what I am doing to achieve it:
execl(filePath," -e ",com.arguments,NULL); //e.g of filePath: /home/dino/programs/mywrapper
Now it works fine for some executables like which command. But for commands like tar, a whole bunch of error throws up.
Basically all I want is the execl to execute the executable mentioned in filePath in my shell. How do I do it?
EDIT:
com.arguments is the arguments list. For example in which bash, bash becomes my argument. In tar -zvcf bazinga.tar.gz bazinga/, -zvcf bazinga.tar.gz bazinga/ becomes my arguments etc.
From execl's documentation
The first argument, by convention, should point to the
filename associated with the file being executed.

File pointers in an array

Very raw with C. I'm writing a program that takes files as it's arguments, but this is rather annoying for debugging (GDB). Rather than have to re-type the file list each time that I start off in GDB, I'd rather store the file names in an array and modify the program to read this array rather than the argv[] values.
I started out with
FILE*[5] inpFiles;
inpFiles[0] = &file1.txt;
but this is all wrong. I need to get some sort of reference to each of the input files so that I can get its memory address.
How can I do this? Thanks.
You can define a GDB command in .gdbinit so you don't need to modify your production code.
For example, add the following lines in your ~/.gdbinit or .gdbinit in your working directory.
define do
run file1.txt file2.txt file3.txt file4.txt file5.txt
end
Then, in GDB, just type the command do, and GDB runs run file1.txt file2.txt file3.txt file4.txt file5.txt for you.
You can parse your input files, containing each of your files, by reading on the standard file stream 0.
so that you could do this:
./your_program < your_input_file
and in gdb,
run/r < your_input_file
but if you want to keep your args method, you can also do this:
./your_program `cat your_input_file`
Hope this helps.
An array of FILE * would be written:
FILE *inpFiles[5];
This can store the values returned from fopen() or a similar functions; it does not store file names.
You might store the file pointer into the structure that &file1 represents, or you might create a new structure that stores the name and the opened file pointer (though you may need to specify a mode; presumably "r" or "rb" by default).
So, clarify to yourself what exactly you want to do. You can create an array of file pointers, or an array of structures containing, amongst other things, a file pointer. But you have to decide how you're going to use it and what the semantics are going to be.
This presumes that modifying the program is a better idea than using GDB better. If you can learn to use the facilities of GDB more powerfully, then that's a better idea.
For example, can you make it easy to specify the files by using a metacharacter:
run debug?.txt
where your files are debug0.txt, debug1.txt, ...?
The other answers also suggest alternatives.

How to execvp ls *.txt in C

I'm having issues execvping the *.txt wildcard, and reading this thread - exec() any command in C - indicates that it's difficult because of "globbing" issues. Is there any easy way to get around this?
Here's what I'm trying to do:
char * array[] = {"ls", "*.txt", (char *) NULL };
execvp("ls", array);
you could use the system command:
system("ls *.txt");
to let the shell do the globbing for you.
In order to answer this question you have to understand what is going on when you type ls *.txt in your terminal (emulator). When ls *.txt command is typed, it is being interpreted by the shell. The shell then performs directory listing and matches file names in the directory against *.txt pattern. Only after all of the above is done, shell prepares all of the file names as arguments and spawns a new process passing those file names as argv array to execvp call.
In order to assemble something like that yourself, look at the following Q/A:
How to list files in a directory in a C program?
Use fnmatch() to match file name with a shell-like wildcard pattern.
Prepare argument list from matched file names and use vfork() and one of the exec(3) family of functions to run another program.
Alternatively, you can use system() function as #manu-fatto has suggested. But that function will do a little bit different thing — it will actually run the shell program that will evaluate ls *.txt statement which in turn will perform steps similar to one I have described above. It is likely to be less efficient and it may introduce security holes (see manual page for more details, security risk are stated under NOTES section with a suggestion not to use the above function in certain cases).
Hope it helps. Good Luck!

Writing a portable command line wrapper in C

I'm writing a perl module called perl5i. Its aim is to fix a swath of common Perl problems in one module (using lots of other modules).
To invoke it on the command line for one liners you'd write: perl -Mperl5i -e 'say "Hello"' I think that's too wordy so I'd like to supply a perl5i wrapper so you can write perl5i -e 'say "Hello"'. I'd also like people to be able to write scripts with #!/usr/bin/perl5i so it must be a compiled C program.
I figured all I had to do was push "-Mperl5i" onto the front of the argument list and call perl. And that's what I tried.
#include <unistd.h>
#include <stdlib.h>
/*
* Meant to mimic the shell command
* exec perl -Mperl5i "$#"
*
* This is a C program so it works in a #! line.
*/
int main (int argc, char* argv[]) {
int i;
/* This value is set by a program which generates this C file */
const char* perl_cmd = "/usr/local/perl/5.10.0/bin/perl";
char* perl_args[argc+1];
perl_args[0] = argv[0];
perl_args[1] = "-Mperl5i";
for( i = 1; i <= argc; i++ ) {
perl_args[i+1] = argv[i];
}
return execv( perl_cmd, perl_args );
}
Windows complicates this approach. Apparently programs in Windows are not passed an array of arguments, they are passed all the arguments as a single string and then do their own parsing! Thus something like perl5i -e "say 'Hello'" becomes perl -Mperl5i -e say 'Hello' and Windows can't deal with the lack of quoting.
So, how can I handle this? Wrap everything in quotes and escapes on Windows? Is there a library to handle this for me? Is there a better approach? Could I just not generate a C program on Windows and write it as a perl wrapper as it doesn't support #! anyway?
UPDATE: Do be more clear, this is shipped software so solutions that require using a certain shell or tweaking the shell configuration (for example, alias perl5i='perl -Mperl5i') aren't satisfactory.
For Windows, use a batch file.
perl5i.bat
#echo off
perl -Mperl5i %*
%* is all the command line parameters minus %0.
On Unixy systems, a similar shell script will suffice.
Update:
I think this will work, but I'm no shell wizard and I don't have an *nix system handy to test.
perl5i
#!bash
perl -Mperl5i $#
Update Again:
DUH! Now I understood your #! comment correctly. My shell script will work from the CLI but not in a #! line, since #!foo requries that foo is a binary file.
Disregard previous update.
It seems like Windows complicates everything.
I think your best there is to use a batch file.
You could use a file association, associate .p5i with perl -Mperl5i %*. Of course this means mucking about in the registry, which is best avoided IMO. Better to include instructions on how to manually add the association in your docs.
Yet another update
You might want to look at how parl does it.
I can't reproduce the behaviour your describe:
/* main.c */
#include <stdio.h>
int main(int argc, char *argv[]) {
int i;
for (i = 0; i < argc; i++) {
printf("%s\n", argv[i]);
}
return 0;
}
C:\> ShellCmd.exe a b c
ShellCmd.exe
a
b
c
That's with Visual Studio 2005.
Windows is always the odd case. Personally, I wouldn't try to code for the Windows environment exception. Some alternatives are using "bat wrappers" or ftype/assoc Registry hacks for a file extension.
Windows ignores the shebang line when running from a DOS command shell, but ironically uses it when CGI-ing Perl in Apache for Windows. I got tired of coding #!c:/perl/bin/perl.exe directly in my web programs because of portability issues when moving to a *nix environment. Instead I created a c:\usr\bin directory on my workstation and copied the perl.exe binary from its default location, typically c:\perl\bin for AS Perl and c:\strawberry\perl\bin for Strawberry Perl. So in web development mode on Windows my programs wouldn't break when migrated to a Linux/UNIX webhost, and I could use a standard issue shebang line "#!/usr/bin/perl -w" without having to go SED crazy prior to deployment. :)
In the DOS command shell environment I just either set my PATH explicitly or create a ftype pointing to the actual perl.exe binary with embedded switch -Mperl5i. The shebang line is ignored.
ftype p5i=c:\strawberry\perl\bin\perl.exe -Mperl5i %1 %*
assoc .pl=p5i
Then from the DOS command line you can just call "program.pl" by itself instead of "perl -Mperl5i program.pl"
So the "say" statement worked in 5.10 without any additional coaxing just by entering the name of the Perl program itself, and it would accept a variable number of command line arguments as well.
Use CommandLineToArgvW to build your argv, or just pass your command line directly to CreateProcess.
Of couse, this requires a separate Windows-specific solution, but you said you're okay with that, this is relatively simple, and often coding key pieces specifically to the target system helps integration (from the users' POV) significantly. YMMV.
If you want to run the same program both with and without a console, you should read Raymond Chen on the topic.
On Windows, at the system level, the command-line is passed to the launched program as a single UTF-16 string, so any quotes entered in the shell are passed as is. So the double quotes from your example are not removed. This is quite different from the POSIX world where the shell does the job of parsing and the launched program receives an array of strings.
I've described here the behavior at the system level. However, between your C (or your Perl) program there is usually the C standard library that is parsing the system command line string to give it to main() or wmain() as argv[]. This is done inside your process, but you can still access the original command line string with GetCommandLineW() if you really want to control how the parsing is done, or get the string in its full UTF-16 encoding.
To learn more about the Windows command-line parsing quirks, read the following:
http://www.autohotkey.net/~deleyd/parameters/parameters.htm#WIN
http://blogs.msdn.com/b/oldnewthing/archive/2006/05/15/597984.aspx
You may also be interested by the code of the wrapper I wrote for Padre on Win32: this is a GUI program (which means that it will not open a console if launched from the Start menu) called padre.exe that embeds perl to launch the padre Perl script. It also does a small trick: it changes argv[0] to point it to perl.exe so that $^X will be something usable to launch external perl scripts.
The execv you are using in your example code is just an emulation in the C library of the POSIX-like behavior. In particular it will not add quotes around your arguments so that the launched perl works as expected. You have to do that yourself.
Note that due to the fact that the client is responsible for parsing, each client client can do it the way it wants. Many let the libc do it, but not all. So generic command-line generation rules on Windows can not exist: the rule depend on the program launched.
You may still be interested in "best effort" implementation such as Win32::ShellQuote.
If you were able to use C++ then perhaps Boost.Program_options would help:
http://www.boost.org/doc/libs/1_39_0/doc/html/program_options.html

Resources