Stifling "file or directory not found" messages in popen - c

I'm using popen to run a system script, like so:
snprintf(cmd, MAX_PATH, "/myscript -q | grep -i %s", deviceName);
FILE *res = popen(cmd, "r");
If the script is not found, the program carries on its merry way. However, a "file or directory not found" message is displayed, which seems like an error even though in my intended usage it isn't.
Is there a way to silence this message, or should I just call ls | grep -i myscript before running this line?

Assuming your /bin/sh is a POSIX shell, or a reasonably recent Bourne shell variant, you can redirect standard output within the command before executing the actual command. You only need to prepend exec 2>/dev/null ; before the command you wish to execute.
Here is how I'd personally do this:
/* Shell syntax for redirecting standard error to /dev/null, to
* silence any errors. If /bin/sh does not support this, you can
* simply replace it with an empty string.
*/
#define POPEN_STDERR_NULL "exec 2>/dev/null ;"
...
snprintf(cmd, MAX_PATH, POPEN_STDERR_NULL "/myscript -q | grep -i -e '%s'", deviceName);
FILE *res = popen(cmd, "r");
The popen() command uses /bin/sh internally to run the specified command. The above works for all /bin/sh variants I can test, including Linux and SunOS 5.10, so it should be quite portable. (In other words, dash, bash, and SunOS 5.10 sh all work fine with it.)
Since you'll need to recompile the application for any nonstandard systems, you can always edit the macro to omit the prefix. (You can easily add a test to Makefile magic to automatically omit it if necessary, if you ever find such a system.)
Note that I modified the parameter substitution in the snprintf() call. It will work for any deviceName that does not contain a single quote. Any single quotes in deviceName should be replaced with the string '"'"' before the snprintf() call.
Questions?

I'm not too sure if you can stifle the message. The error is a standard Linux error which is printed onto the standard error stream. You can keep FD '2' which is the file descriptor for Standard error. So maybe you can close this FD.
However, I must warn you that this will prevent any errors from being printed for the rest of your program.
A better way would be to do this:
snprintf(cmd, MAX_PATH, "/myscript -q | grep -i %s 2> dummyfile", deviceName);
This will redirect the error to a dummy file which you delete immediately.
So exercise caution and decide what you would like to do...
Cheers,
VSN

Related

Use of echo and system to run a software in C

I am trying to run a biological program called BLASTP which takes in two strings (fasta_GWIDD and fasta_UNIPROT in the code) and compares them. The problem that I am encountering is the use of echo/system in the code. Can anyone suggest what am I missing out??
for(i=0;i<index1;i++)
{
sprintf(fasta_GWIDD,">%s\\n%s\n",fasta_name1[i],fasta_seq1[i]);
setenv("GwiddVar", fasta_GWIDD, 1) ;
sprintf(fasta_UNIPROT,">%s\\n%s\n",fasta_name2[i],fasta_seq2[i]);
setenv("UniprotVar", fasta_UNIPROT, 1) ;
system("blastp -query <(echo -e $GwiddVar) -subject<(echo -e $UniprotVar)");
}
The error is:
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `blastp -query <(echo -e $GwiddVar) -subject<(echo -e $UniprotVar)'
It seems that the shell does not understand the
<(echo -e $GwiddVar)
syntax. Mind that the system command may use different shell than the one you are used to (like csh instead of bash, and so on). It's everything in somewhere in your OS config files and profile, but I can't guess what you have out there.
Btw. I think that you should be able to check which shell is being used by the system() command by either of these:
system("echo $SHELL") // should simply write the path to current shell
system("ps -aux") // look at it and find what is the parent of the PS
etc.
Considering that this was correct on some shell:
blastp -query <(echo -e $GwiddVar) -subject<(echo -e $UniprotVar)
The syntax cited above apparently is meant only to pass the variable as intput. I think you are overdoing it. You are using echo -e $GwiddVar to print and capture the data, which you already have in a vairable at hand. Have you tried something as simple as:
blastp -query $GwiddVar -subject $UniprotVar
I don't know which shell you are trying to use, but considering that echo got its data, then it should be exactly the same.
If you are worried about spaces, then various shells usually allow you to use quotation marks:
blastp -query "$GwiddVar" -subject "$UniprotVar"
Of course it depends on the shell. If your program uses a shell that does not like quotation marks, well, you have to adapt it. Not to your shell, but to the shell the system() has used.
Another thing is that using system is quite rough. When you have arguments that are difficult to escape correctly, you should be using other functions like execve that are able to take an array of real raw direct strings and pass them directly as ARGV to the process. Using these, you will not need (and you should not) add any quotes or escape any spaces in the strings to be passed.
sprintf(fasta_GWIDD,">%s\\n%s\n",fasta_name1[i],fasta_seq1[i]);
sprintf(fasta_UNIPROT,">%s\\n%s\n",fasta_name2[i],fasta_seq2[i]);
char** args = .....; // allocate an array of char*[5], malloc, or whatever
args[0] = "blastp";
args[1] = "-query";
args[2] = fasta_GWIDD;
args[3] = "-subject";
args[4] = fasta_UNIPROT;
int errcode = execve(4, args, null);
if( errcode ) ... // check the error (if any) and react
However! Note that the execve comes from the exec family, so it replaces your current process. This is why I write only a sketch and don't show the whole ready-to-run code. You will probably need to fork() before it and then wait for the children in the outer loop.
So, I'd first check the shell and syntax ;)
From man 3 system:
DESCRIPTION
system() executes a command specified in command by calling /bin/sh -c
command, and returns after the command has been completed.
On many systems, /bin/sh is not bash, and even when it is, it is a different configuration of bash (bash typically operates differently if it is invoked as /bin/sh). So, you are passing bash syntax to a shell that is either not bash, or doesn't allow the full set of bash-isms... Also, there's a space missing after -system that might be confusing things as well... And, I'm not entirely sure environment variables are expanded within system() strings...

Executing ssh command in c program using popen()

When I try executing this using popen it returns this error but when I run this in terminal it works!
popen("ssh -n -f *.*.*.* 'sshfs -o nonempty *.*.*.*:/home/foo/bar/ /foo1/foo2/foo3'", "r");
error:
ssh_exchange_identification: Connection closed by remote host
I use public and private key to ssh without passwords and they work properly as this command run flawlessly in terminal.
I changed it to this :
popen("ssh -n -f *.*.*.* `sshfs -o nonempty *.*.*.*:/home/foo/bar/ /foo1/foo2/foo3`", "r");
It return errors too.
error :
fuse: bad mount point `/foo1/foo2/foo3': No such file or directory
Cannot fork into background without a command to execute.
I also tried escipping the internal "" this way : \" \" but it hangs!
Replace ssh with /usr/bin/ssh, do the same with other commands, like sshfs. Specify the full path of the command, /usr/sbin/foo or whatever the case may be. popen does not necessarily use the same shell you have at the command line to execute commands. Check your documentation.

difference between "sh -c cmd" and just "cmd"?

What's the difference between "sh -c cmd" and "cmd", when executed from a shell commnad line, and when executed from a c exec() function, respectively? Thanks.
It depends on what 'cmd' represents. In the basic case where it is a simple command name (say ps or ls), then there is no difference at the shell command line, and precious little difference when executed by execvp(). The 'non-p' exec*() functions do have slightly different semantics; they don't use the PATH variable, so the command must exist and be executable in the current directory or it will fail.
However, if cmd is more complex, then it can make a big difference. For example:
$ echo $$
17429
$ sh -c 'echo $$'
76322
$ sh -c "echo $$"
17429
$
The first reports the process ID of the original shell; the second reports the process ID of the shell run as sh; the third is an expensive way of reporting the process ID of the original shell. Note that the single quotes vs double quotes are significant too. Here, the quotes would not be present in the C invocation (the shell removes the quotes from around the arguments), and the value of $$ would be that of the child shell:
char *argv[] = { "sh", "-c", "echo $$", 0 };
execvp(argv[0], argv);
(I said the quotes are not present in the C invocation; they are needed around the string in the C code, but the value passed to sh doesn't contain any quotes — so I meant what I said, though it might not be quite as blindingly obvious as all that.)
From the man page:
-c string If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0.
Just cmd will run through bash (or whatever your default) shell is. You need the sh to explicitly call -c argument.
exec shouldn't make a difference here.

sed command works fine under shell terminal, but fails in 'system()' call under C code

I'm trying to delete some special lines in a log file, so I use sed of busybox on an embeded linux system.
# sed
BusyBox v1.18.4 (2013-01-16 16:00:18 CST) multi-call binary.
Usage: sed [-efinr] SED_CMD [FILE]...
Options:
-e CMD Add CMD to sed commands to be executed
-f FILE Add FILE contents to sed commands to be executed
-i Edit files in-place (else sends result to stdout)
-n Suppress automatic printing of pattern space
-r Use extended regex syntax
If no -e or -f, the first non-option argument is the sed command string.
Remaining arguments are input files (stdin if none).
execute the following command under shell and everything works fine:
export MODULE=sshd
sed "/$MODULE\[/d" logfile
but if I try to use the following C code to accomplish this:
char logfile[] = "logfile";
char module_str[] = "sshd";
char env_str[64] = {0};
int offset = 0;
strcpy(env_str, "MODULE=");
offset += strlen("MODULE=");
strcpy(env_str + offset, module_str);
putenv(env_str);
system("sed \"/$MODULE\[/d\" logfile");
when executing the a.out, I got the error message:
sed: unmatched '/'
what's wrong with my 'system()' call? I'm totally a newbie in text processing, so anybody can give me some clue? Thanks.
Best regards,
dejunl
straight off I can see that the \ before the [ is going to be swallowed by 'C'
so you'll need to double it,
system("sed \"/$MODULE\\[/d\" logfile");
But the shell might want to swallow the one that's left swallow that one so double it again
system("sed \"/$MODULE\\\\[/d\" logfile");
of course system("sed \"/$MODULE\\[/d\" logfile"); can't be sure I'm reading the question you posed. try it with echo instead of sed and adjust it until the string comes out as you want sed to see it.

How to get input file name from Unix terminal in C?

My program gets executed like:
$./sort 1 < test.txt
sort is the program name
1 is the argument (argv[1])
and test.txt is the file I am inputting from
Is it possible to extract the name file from this? if so how?
The problem is I already wrote my whole program as if I could extract the name from the input line, so I need to be able to pass it into arguments.
Any help is appreciated,
Thanks!
You can't. The shell opens (open(2)) that file and sets up the redirect (most likely using dup2).
The only possible way would be for the shell to explicitly export the information in an environment variable that you could read via getenv.
But it doesn't always make sense. For example, what file name would you expect from
$ echo "This is the end" | ./sort 1
Though this can't be done portably, it's possible on Linux by calling readlink on /proc/self/fd/0 (or /proc/some_pid/fd/0).
eg, running:
echo $(readlink /proc/self/fd/0 < /dev/null)
outputs:
/dev/null
No you can't: the shell sends the content of test.txt to the standard input of your program.
Look at this:
sort << _EOF
3
1
2
_EOF
The < > | operators are processed by the shell, they alter standard input,output,error of the programs in the cmd line.
If you happen to run Solaris, you could parse pfiles output to get the file associated, if any, with stdin.
$ /usr/bin/sleep 3600 < /tmp/foo &
[1] 8430
$ pfiles 8430
8430: /usr/bin/sleep 3600
Current rlimit: 65536 file descriptors
0: S_IFREG mode:0600 dev:299,2 ino:36867886 uid:12345 gid:67890 size=123
O_RDONLY|O_LARGEFILE
/tmp/foo
1: S_IFCHR mode:0600 dev:295,0 ino:12569206 uid:12345 gid:67890 rdev:24,2
...
On most Unix platforms, you will also get the same information from lsof -p if this freeware is installed.

Resources