Perl: Can't pass question mark as command line argument - bash

I can't pass a question mark character into a Perl script. It comes out as the letter "t" instead.
./myscript foo
print $ARGV[0]; # prints foo
./myscript ?
print $ARGV[0]; # prints t
./myscript "?"
print $ARGV[0]; # prints ?
./myscript ??
print $ARGV[0]; # prints ?? (multiple question marks work)
./myscript ^
print $ARGV[0]; # prints ^; other special characters work too
./myscript foo?
print $ARGV[0]; # prints foo?
I can't find an explanation for this anywhere. The reason I want to pass in a question mark is so my script has an easy help option. Other special characters work, as does quoting the question mark, as does a question mark mixed in with another string. It's just a single, naked question mark that doesn't.
I'm calling the script via the Bash shell. I'm not 100% positive this is a Perl issue
Update: now that this question is answered, I'm suddenly realizing: is this the reason that most command line arguments have a dash in front of them?

You have a file named t in the directory in which you are running your script. The shell treats a question mark (?) as a single character and expands it prior to passing the argument to your script. If you have a file named xx, for example, then running your script with ?? would print "xx".
If you quote your script's argument, no shell expansion occurs and passing ? or * or ?? remain uninterpreted.

The question mark ? is a shell metacharacter that is used for glob expansion. It is a placeholder for a single character, similar to . in a regex. E.g. If I have files a1.txt a2.txt b1.txt, then the glob pattern a?.txt will match the a1.txt and a2.txt but not b1.txt.
The following shell metacharacters must always be escaped or quoted when they are part of a command line argument:
| & ; < > ( ) $ ` \ " ' <space> <tab> <newline>
These characters must sometimes be escaped:
* ? [ # ˜ = %
Additionally the symbols ! { } [[ ]] are reserved words.
(taken from the POSIX shell language description. Bash is mostly POSIX-compatible.)

Related

escape character usage in bash, what do we actually escape?

I am reading the bash manual, found the escape character definition pretty surprising, instead of modifying the meaning of every character follows it (as in Java / C):
It preserves the literal value of the next character that follows
Does it mean in bash, we only use it to escape special meaning character like ', ", \, $
And other cases, like \t\e\s\t actually is exactly as test ? I verified that
echo test
echo \t\e\s\t
outputs same result.
Does it mean in bash, we only use it to escape special meaning character like ', ", \, $
Yes. Also, e.g. newline:
echo foo
bar
# foo
# -bash: bar: command not found
echo foo \
bar
# foo
# bar
And other cases, like "\t\e\s\t" actually is exactly as "test"
If unquoted, yes. Quoted, the backslash is preserved. Some UNIX utilities do use backslash for "special meanings", but it is the utility, not bash, that gives those sequences meanings. Examples are printf, and GNU echo when given -e option:
/bin/echo \t\e\s\t
# test
/bin/echo "\t\e\s\t"
# \t\e\s\t
/bin/echo -e "\t\e\s\t" # GNU version (will not do the same thing on Mac)
# s
# (tab)(escape)s(tab)
printf "\t\e\s\t"
# s
# (tab)(escape)s(tab)
As #rici reminds me, bash can interpret C-style escape sequences itself, if you use the special quotes of the form $'...':
/bin/echo $'\t\e\s\t'
# s
Here it really is bash that does it, not echo.

I want to print $ in shell, but it is always deleted

In a Linux shell, I want to print:
$300
$400
But when I do echo -e "$300\n$400" it shows:
00
00
When I do printf "$300\n$400" it shows the same thing!
So why does shell delete my dollar sign and the number right after it? Is there a way to print what I want?
You need to escape dollar $, since you are using double quotes, This will ensure the word is not interpreted by the shell.
$ echo -e "\$300\n\$400"
$300
$400
You may be aware how to access variables,
Example :
$ test="foo"
$ echo "$test"
foo
Suppose if you want to print $test, then you have use either
$ echo "\$test"
$test
OR with single quotes
$ echo '$test'
$test
In the shell, the $ character has a special meaning. It means "replace the $ and the following word or digit or special character with the value of a variable of that name". For example:
currency='EUR'
echo "The currency is $currency"
The variables 0, 1, 2, etc. contain the command line arguments to the program. So if you run your program as my-program Hello, world, you can write this code:
echo "argument 1 is $1"
echo "argument 2 is $2"
echo "both together are $1 $2, and all arguments are $*"
To make the $ character lose this special meaning, it must be written as \$. For example:
price=123
echo "The price is $price\$"
The first $ refers to the variable, and the second $ is escaped.
Alternatively you can surround your string in 'single quotes', which removes the special meaning of all characters.
To learn more about this topic, run the man bash command and read the section about variable expansion.
$ has special meaning to the shell; when it sees a $, it expects an existing shell variable name to follow. For example, $PATH.
In your case, you don't want the shell to think that you're trying to print out the value of shell variables, so you must tell the shell that the $ is indeed what you want to be displayed. This is done by preceding it with a backslash as explained in other answers.
Adding a backslash before characters is called escaping them (yes, not the most obvious terminology), and you are already using some escape characters unknowingly. (\n)
This applies to display other operators too, such as =, :, etc. Hope that helps.
You can use single quote. Enclosing characters in single-quotes (') shall preserve the literal value of each character within the single-quotes, where as enclosing characters in double-quotes(") shall preserve the literal value of all characters within the double-quotes, with the exception of the characters back quote, dollar-sign, and backslash.
echo -e '$'300"\n"'$'400

awk simple calculator-function behaving strangely in a working dir containing a one-letter dir

I defined the following awk function in my .bashrc:
? () { awk "BEGIN{ pi=3.14159265359; printf \"%5.5f\n\", $* }" ; }
When I execute this function in a directory containing a one-letter directory, this will give me an error.
> ls
B
> ? "1+2"
B: command not found # B refers to the one-letter dir?
The function works fine if executed in some other directory that doesn't contain any one-letter directories.
I also did some further test. When I executed this function in a directory containing a one-letter file, this also gives me error:
> ? "1+2"
-bash: ./B: Permission denied # B now refers to a file
Could anyone tell me what's going wrong here?
You've got it wrong. The ? has a special meaning which the shell interprets as, meaning to match a single character. That's why the expansion results in a single character filename, i.e. glob expansion happens before the shell looks up functions or commands. Had there been such files with single character names, it would have expanded to a literal string ?
From man bash
Pathname Expansion
After word splitting, unless the -f option has been set, bash scans
each word for the characters *, ?, and [. If one of these characters
appears, then the word is regarded as a pattern, and replaced with an alphabetically sorted list of filenames matching the pattern
The special pattern characters have the following meanings:
? Matches any single character.
Would recommend a way to use the awk import shell variables option -v to import args than use double-quotes to avoid having to take care of special cases. Also consider using a better function name than any of the shell meta-characters
awkf () { awk -v argv="$*" 'BEGIN{ pi=3.14159265359; printf "%5.5f\n", argv }' ; }
But for a use-case for a simple calculator like using the string 1+2 inside the expression, you could use double-quotes properly
awkf () { awk "BEGIN{ pi=3.14159265359; printf \"%5.5f\n\", "$*" }" ; }

shell script exit with no match with question mark symbol

Why ./script.sh ? throws No match. ./script.sh is running fine.
script.sh
#!/bin/sh
echo "Hello World"
? is a glob character on UNIX. By default, in POSIX shells, a glob that matches no files at all will evaluate to itself; however, many shells have the option to modify this behavior and either pass no arguments in this case or make it an error.
If you want to pass this (or any other string which can be interpreted as a glob) literally, quote it:
./script.sh '?'
If you didn't use quotes, consider what the following would do:
touch a b c
./script.sh ? ## this is the same as running: ./script.sh a b c
That said -- the behavior of your outer shell (exiting when no matches exist, rather than defaulting to pass the non-matching glob expression as a literal) is non-default. If this shell is bash, you can modify it with:
shopt -u failglob
Note, however, that this doesn't really fix your problem, but only masks it when your current directory has no single-character filenames. The only proper fix is to correct your usage to quote and escape values properly.

Semicolons superfluous at the end of a line in shell scripts? [duplicate]

This question already has answers here:
Bash semicolon being equal to newline is not exactly true?
(4 answers)
Closed 2 years ago.
I have a shell script which contains the following:
case $1 in
0 )
echo $1 = 0;
OUTPUT=3;;
1 )
echo $1 = 1;
OUTPUT=4;;
2 )
echo $1 = 2;
OUTPUT=4;;
esac
HID=$2;
BUNCH=16;
LR=.008;
Are semicolons completely superfluous in the snippet above? And is there any reason for some people using double semicolons?
It appears semicolons are only a separator, something you would use instead of a new line.
Single semicolons at the end of a line are superfluous, since the newline is also a command separator. case specifically needs double semicolons at the end of the last command in each pattern block; see help case for details.
According to man bash:
metacharacter
A character that, when unquoted, separates words. One of the following:
| & ; ( ) < > space tab
control operator
A token that performs a control function. It is one of the following symbols:
|| & && ; ;; ( ) | |& <newline>
So, the ; can be metacharacter or control operator, while the ;; is always a control operator (in case command).
In your particular code, all ; at the end of line are not needed. The ;; is needed however.
In the special case of find, ; is used to terminate commands invoked by -exec. See the answer of #kenorb to this question.
#Opensourcebook-Amit
newlines equivalent to single semicolon ; on terminal or in shell script.
See the below examples:
On terminal:
[root#server test]# ls;pwd;
On shell script:
[root#server test]# cat test4.sh
echo "Current UserName:"
whoami
echo -e "\nCurrent Date:";date;
[root#server test]#
But I am not agree with the comment that & is equivalent to newline or single semicolon
& is run commands in background also a command separator but not worked as semicolon or newline.

Resources