I was working on an example in the K&R C book where it asks you to essentially build an RPN calculator that takes input through command line arguments. My solution essentially iterates through the given arguments and spits out the answer, but I noticed something:
If I were to give the multiplication character (an asterisk) '*' without single quotes, gcc assumes that to be a wildcard input, so my input of
$./rpn 5 10 *
gives me an output of
read 5
read 10
read rpn
read rpn.c
= 0
Wrapping the asterisk with single quotes remedies the issue
$./rpn 5 10 '*'
read 5
read 10
read *
= 50
My question is would there be a way to sanitize input so that my program does not require the asterisk to be wrapped in single quotes, or is this behavior caused by something more fundamental (e.g. Linux/POSIX/UNIX binary execution and argument handling)?
The shell is expanding the glob before executing the program. You quote the glob not because of GCC, but because of the shell. If you don't want this behavior then use a shell that does not honor globs.
Give input as
$./rpn "5 10 *"
All argument in "" and in program you will get all argument under argv[1] then parse that string by space separation.
By this way you do need to handle any wildcard/special character at special way.
Related
This question already has answers here:
Convert integers to strings to create output filenames at run time
(9 answers)
Closed 2 years ago.
As there are lots of folders and files, I wish to use loop to open the folder and read them.
The names of the folders to open and read them are rt20, rt30, rt40 and so on. Whereas, the names of the folders to save some text files are sphere20, sphere30, etc.
I created an array of characters and concatenate with the name of the folders but failed.
The actual code that I created is:
PROGRAM TEST1
IMPLICIT NONE
INTEGER ::lskip,lread,count
INTEGER :: n,I,j
REAL l,b,c,d,e,ReThe,ImThe,RePhi,ImPhi
CHARACTER(len=100) :: FN,FNL
character*2,parameter :: A(*) = ['20','30','40','50','60','70']
N=256
do j = 1,6
OPEN(11,file='rt',A(j),'\\output.dat',form='formatted')
!--allocate your vectors here
DO I=1,N
WRITE(FN,10)I
WRITE(6,*)FN
OPEN(1,FILE=FN)
!skip first 17 lines
do lskip = 1,18
READ(1,*)
end do
! Now read actual lines
do lread = 1,1
READ(1,*)l,b,ReThe,ImThe,RePhi,ImPhi,c,d,e
end do
write(11,20)ReThe,ImThe,RePhi,ImPhi
CLOSE(1)
END DO
10 format ('pm\\vertical\\sphere',A(j),'\\n_FarField',I0,'.ffe')
20 format (E14.7,4X,E14.7,4X,E14.7,4X,E14.7)
end do
END PROGRAM
The errors that are appeared in the program:
In file C:\Users\Hamsalekha\Desktop\far_field_sphere\Source1.f:12
OPEN(11,file='rt',A(j),'\\output.dat',form='formatted')
1
Error: Syntax error in OPEN statement at (1)
In file C:\Users\Hamsalekha\Desktop\far_field_sphere\Source1.f:33
10 FORMAT('pm\\vertical\\sphere',A(j),'\\n_FarField',I0,'.ffe')
1
Error: Unexpected element in format string at (1)
I couldn't find a solution on any websites. I really appreciate if someone can help me to solve this problem or give any hint.
You are concatenating your strings incorrectly:
OPEN(11,file='rt',A(j),'\\output.dat',form='formatted')
This only asks for file "rt" and than adds some other strange arguments. You need
OPEN(11,file='rt'//A(j)//'/output.dat',form='formatted')
Be aware that \ does not work as an escaping character in Fortran strings and that / is the proper multiplatform character to separate directories in the hierarchy. Yes, even in MS Windows.
The // is the Fortran string concatenation operator.
Jean-Claude Arbaut mentions in the comment the reason for the other error. One needs a constant string in the FORMAT statement. I personally strongly prefer to use format strings in the write statements directly and also to put what I can in the output list:
WRITE(FN,'(3A,I0,A)') 'pm/vertical/sphere',A(j),'/n_FarField',I,'.ffe'
but there are many other ways to write it. You can concatenate the string. You can also use the FORMAT statement.
WRITE(FN,10) A(j), I
10 FORMAT('pm/vertical/sphere',A,'/n_FarField',I0,'.ffe')
Many examples can be found here: Convert integers to strings to create output filenames at run time
I have written a small program that takes some input parameters from *argv[] and prints them. In almost all use cases my code works perfectly fine. A problem only arises when I use more than one exclamation mark at the end of the string I want to pass as an argument ...
This works:
./program -m "Hello, world!"
This does NOT work:
./program -m "Hello, world!!!!"
^^ If I do this, the program output is either twice that string, or the command I entered previous to ./program.
However, what I absolutely don't understand: The following, oddly enough, DOES work:
./program -m 'Hello, world!!!!'
^^ The output is exactly ...
Hello, world!!!!
... just as desired.
So, my questions are:
Why does this strange behavior occur when using multiple exclamation marks in a string?
As far as I know, in C you use "" for strings and '' for single chars. So why do I get the desired result when using '', but not when using "" as I should (in my understanding)?
Is there a mistake in my code or what do I need to change to be able to enter any string (no matter if, what, and how many punctuation marks are used) and get exactly that string printed?
The relevant parts of my code:
// this is a simplified example that, in essence, does the same
// as my (significantly longer) code
int main(int argc, char* argv[]) {
char *msg = (char *)calloc(1024, sizeof(char));
printf("%s", strcat(msg, argv[2])); // argv[1] is "-m"
free(msg);
}
I already tried copying the content of argv[2] into a char* buffer first and appending a '\0' to it, which didn't change anything.
This is not related to your code but to the shell that starts it.
In most shells, !! is shorthand for the last command that was run. When you use double quotes, the shell allows for history expansion (along with variable substitution, etc.) within the string, so when you put !! inside of a double-quoted string it substitutes the last command run.
What this means for your program is that all this happens before your program is executed, so there's not much the program can do except check if the string that is passed in is valid.
In contrast, when you use single quotes the shell does not do any substitutions and the string is passed to the program unmodified.
So you need to use single quotes to pass this string. Your users would need to know this if they don't want any substitution to happen. The alternative is to create a wrapper shell script that prompts the user for the string to pass in, then the script would subsequently call your program with the proper arguments.
The shell does expansion in double-quoted strings. And if you read the Bash manual page (assuming you use Bash, which is the default on most Linux distributions) then if you look at the History Expansion section you will see that !! means
Refer to the previous command.
So !!!! in your double-quoted string will expand to the previous command, twice.
Such expansion is not made for single-quoted strings.
So the problem is not within your program, it's due to the environment (the shell) calling your program.
In addition to the supplied answers, you should remember that echo is your shell friend. If you prefix your command with "echo ", you will see what shell is actually sending to your script.
echo ./program -m "Hello, world!!!!"
This would have showed you some strangeness and might have helped steer you in the right direction.
I'm translating a GNU Makefile into MSBuild XML.
I have the following sed command (part of a larger command):
... | sed 's/^Q(.*)/"&"/' | ...
When I execute just that sed portion in Cygwin, it "works" in the sense that it doesn't error out.
However, after I've translated it to MSBuild XML - replaced the XML-sensitive symbols with ", &, ' - I get the error
sed: unsupported command '
I'm sure it's not an issue with XML escaping issues; the Visual Studio build log says
Task Parameter:Command="C:\Program Files\GNU ARM Eclipse\Build Tools\2.8-201611221915\bin\sed" 's/^Q(.*)/"&"/' (TaskId:21)
sed: unsupported command ' (TaskId:21)
The command ""C:\Program Files\GNU ARM Eclipse\Build Tools\2.8-201611221915\bin\sed" 's/^Q(.*)/"&"/' " exited with code 1. (TaskId:21)
The command was translated into the originally intended sed 's/^Q(.*)/"&"/'
However, there now appears to be an issue with cmd.exe.
With respect to cmd.exe, what part of that command doesn't it like?
Is it the single/double quote? The ampersand?
I'm using the table from here.
The shell interpreters on Unix/Linux/Mac support at least three types of argument strings:
None quoted argument strings for simple strings not containing a space character or a character with a special meaning for shell interpreter.
" quoted argument strings for strings containing a space character or a character with a special meaning for shell interpreter not containing ".
' quoted argument strings for strings containing a space character or a character with a special meaning for shell interpreter not containing '.
The Windows command line interpreter supports only two types of argument strings.
None quoted argument strings for simple strings not containing a space character or one of these characters: &()[]{}^=;!'+,`~|<>.
" quoted argument strings for strings containing a space character or a character with a special meaning for Windows command interpreter not containing ".
For that reason a regular expression string as argument string containing " is problematic on Windows as there is no alternate solution for quoting the argument string as on *nix/Mac shells.
A solution for regular expressions in Perl syntax is using the hexadecimal notation \x22 for double quote character " in the regular expression.
So "s/^Q(.*)/\x22&\x22/" could work on Windows as well as on Linux instead of 's/^Q(.*)/"&"/'.
However, the command line argument strings are finally processed by the called application and it depends on the application and the used compiler how the application interprets the arguments. Therefore it depends always also on the application what finally works. See C code in this answer and the two different results on running this code compiled with three different compilers with a misquoted argument string. Another example for application dependent interpretation of argument string is explained in answer on How to set environment variables with spaces?
For that reason using just s/^Q(.*)/"&"/ without enclosing the regular expression argument string in the single quotes ' works for sed on Windows too.
I'm trying to send input from the command line to my main function. The input is then sent to the functions checkNum etc.
int main(int argc, char *argv[])
{
int x = checkNum(argv[1]);
int y = checkNum(argv[3]);
int o = checkOP(argv[2]);
…
}
It is supposed to be a calculator so for example in the command line when I write:
program.exe 4 + 2
and it will give me the answer 6 (code for this is not included).
The problem is when I want to multiply and I type for example
program.exe 3 * 4
It seems like it creates a pointer (or something, not quite sure) instead of giving me the char pointer to the char '*'.
The question is can I get the input '*' to behave the same way as when I type '+'?
Edit: Writing "*" in the command line works. Is there a way where I only need to type *?
The code is running on Windows, which seems to be part of the problem.
As #JohnBollinger wrote in the comments, you should use
/path/to/program 3 '*' 4
the way it's written at the moment.
But some explanation is clearly required. This is because the shell will parse the command line before passing it to your program. * will expand to any file in the directory (UNIX) or something similar (windows), space separated. This is not what you need. You cannot fix it within your program as it will be too late. (On UNIX you can ensure you are in an empty directory but that probably doesn't help).
Another way around this is to quote the entire argument (and rewrite you program appropriately), i.e.
/path/to/program '3 * 4'
in which case you would need to use strtok_r or strsep to step through the (single) argument passed, separating it on the space(s).
How the shell handles the command-line arguments is outside the scope and control of your program. There is nothing you can put in the program to tell the shell to avoid performing any of its normal command-handling behavior.
I suggest, however, that instead of relying on the shell for word splitting, you make your program expect the whole expression as a single argument, and for it to parse the expression. That will not relieve you of the need for quotes, but it will make the resulting commands look more natural:
program.exe 3+4
program.exe "3 + 4"
program.exe "4*5"
That will also help if you expand your program to handle more complex expressions, such as those containing parentheses (which are also significant to the shell).
You can turn off the shell globbing if you don't want to use single quote (') or double quote (").
Do
# set -o noglob
or
# set -f
(both are equivalent).
to turn off the shell globbing. Now, the shell won't expand any globs, including *.
I want to control the printf() functions output format with dynamic parameter, just as the code showed below:
#include<stdio.h>
int main(int argc,char ** argv)
{
printf(argv[1],"hello,world");
return 0;
}
Then I compile and run it:
$ gcc -o test test.c
$ ./test "\t%s\n"
The result is strange:
\thello,world\n$
Why "\n" and "\t" has no effect?
Because the escapes you use (\t and \n) are interpreted inside string literals by the C compiler, not by printf(). This:
const char *newline1 = "\n", newline2[] = { '\n', 0 };
would generate the exact same content in newline1 and newline2, regardless of whether or not these are ever passed to printf(); the strings are there anyway.
Your code behaves just like this would:
printf("\\t%s\\n", "hello,world");
Here, I've double-escaped the special characters to generate a string with the same actual content as your command-line argument, i.e. "\t%s\n" (six characters rather than four).
The proper way to dynamically control printf() is to build the format string in code. If you want C-like escapes at runtime, you need to interpret them yourself in some way.
The sequence \n in a string or character literal in C/C++ is a single byte with the numeric value 10 (on an ASCII system). When output on a terminal (try putchar(10)!) it just sets the output position for the next character on the terminal to the beginning of the next line (on *nix; on MacOS, I think, you need an additional \r, or 13 for carriage return to have the output position at the beginning of the line).
Similarily, a \t is the notation for a single byte with the value 9, which makes most terminals advance their cursor to the next tabulator position.
What you need is to insert a single byte of these values in the command line. How that can be done depends on your shell; in bash you can keep the shell from interpreting special characters by pressing Ctrl-V beforehand. That outputs e.g. a tab, displayed by showing some empty space (instead of making the shell show possible string continuations or whatever tab does in bash). bash strings in single or double quotes can include newlines without further efforts -- simply press enter.
Here is a sample run in a cygwin terminal with bash. I pressed the indicated keys at the indicated positions; I finished the command as usual with [return] after the closing single quote on the second line.
pressed Ctrl-v,[TAB] here | pressed [return] there
v v
$ ./printf-arg.exe ' %s
> '
hello,world
The > in the second line was output by the shell after I pressed enter within the string delimited by single quotes. (Which inserts a newline in the string). It is an indication that the string being edited is continued on that line.
As an aside, it is probably unsafe to use command line arguments this way in potentially hostile environments. Carefully crafted strings could access memory which is not meant to be accessed and e.g. redirect return addresses, thus corrupting the program.
It's because the compiler handles the escape sequences like "\n" etc., and it does it in string or character literals only.
if you pass interpreted "\t%s\n" to command it will work. However it is tricky to construct such string in shell. The easiest way I know is:
./test $'\t%s\n'
See ANSI quoting in man bash for the $'magick'