I found one thing I can not explain in bash.
Command:
$(3-1)
3-1: command not found
If I do it step by step, it will show as follows:
3-1
3-1: command not found
3-1: command not found
3-1:: command not found
Double colons there. Why is the difference?
3-1 is not found. The error message you get is sent to standard error, not standard output. So, $(3-1) is not running
3-1: command not found
It is running 3-1 and failing. If you try to run the error message, the first word is taken as the command, the rest are the arguments. The command 3-1: is not found either, hence the error message with two colons.
what are you going to do with 3-1? if you want to execute it I doubt you have program 3-1 or 2. try like this:
n=$((3-1))
echo $((3-1))
I think explanation is needed here:
if you write in bash new line
do_smth
it assumes you want to execute a program. The $(do_smth) mean: execute a program do_smth and catch the output. so, if you write in bash new line
$(do_smth)
it assumes you want to execute do_smth, take an output from command and execute command with name it get. $(( $do_smth )) is another. It assumes $do_smth is a mathematical expression and affords you to calculate c-like math expression.
Your "step by step" breakdown is wrong because you assumed that the first "command not found" error message would become the result of the $() and therefore be re-parsed as a command. This doesn't happen because $() only captures standard output (fd 1) and the "command not found" message is printed to standard error (fd 2).
If you run
$(3-1 2>&1)
you might get something more like what you're expecting. (I can't reproduce your result exactly because my bash error messages don't look quite like yours.)
you input:
foo
output:
foo: command not found
you input :
foo bar
output:
foo: command not found
so bash thinks the space separating commands/parameters. If the command you gave with :, you get it in input too:
input:
foo:
output:
foo:: command not found
replace foo with 3-1, you find the answer.
When you type any command which bash cannot find, you get an error message of the form
bash: <cmd>: command not found
So if you type 3-1, you get
$ 3-1
bash: 3-1: command not found
If you type 3-1:, you get
$ 3-1:
bash: 3-1:: command not found
The shell sees this:
situation 1
stdin- "$(3-1)" ( run non existent command "3-1" in a subshell ... )
Command- "3-1"
Arguments-
error- 3-1
reply- "3-1" ": command not found"
situation 2
stdin- "3-1:" "command" "not" "found"
Command- "3-1:"
(note that the ":" you typed is seen as part of the command)
Arguments- "command" "not" "found"
error- 3-1:
reply- "3-1:" ": command not found"
(^---^ and this is where the double colon comes from)
As you can see, the shell does not reply with a double colon at all!
it echoes your "3-1:" and it adds ": reply"
Related
I am a beginner of bash. I encounter a problem like this:
$ "make -p"
when I type the above in bash command line, there is nothing to happen, no error, no result msg.
I have searched double quotes syntax of bash in many websites. All of these materials give similar interpretation as below:
https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
and give examples like:
echo "argument"
I do not find something like "echo argument". Moreover, I find a strange difference between bash command line and bash scripts.
If I type a non-existing command in command line:
$ "holy shit"
$ "look that"
there is nothing to happen. But if I type it in bash scripts:
#!/bin/bash
"holy shit"
"look that"
and execute this script, an error msg will be throw out:
$ ./myshell
./myshell: line 2: holy shit: command not found
./myshell: line 3: look that: command not found
Would someone can help give a detailed interpretation about the effect of double quotes when they enclosed the whole command?
Why there is no output in command-line?
Why it is different between command line and scripts?
If you enter a command foo, the shell searches the directories listed in your PATH variable until it finds a command of this name. If there is none, you get the error message command not found.
If you enter a command, which contains at least one slash - for example ./foo or foo/bar -, the shell does not search the PATH, but assumes that you have already entered the correct path to your command. If it does not exist, you get the error message No such file or directory.
In your case,
"cd home"
searches for a file with name cd home somewhere along your PATH, but there is no file of this name, and you get command not found. If you enter
"cd /home"
the shell bypasses PATH-search and assumes, that there exists a directory named cd (i.e. the 3 letters c,d,space) in your current directory, and below it a file named home, with x-bit set. There is no such file (and no such directory) on your system, and you get the error message No such file or directory.
If you are in the mood of experimenting around, you could try the following:
mydir="cd "
mkdir "$mydir"
echo "echo Hello Stranger" >"$mydir/home"
chmod +x "$mydir/home"
"cd /home"
This should print Hello Stranger. Pay attention that in the assignment to mydir, there must be a single space between the cd and the closing quote.
The double quotes mean it is a string. You can do something like:
echo "Hello everybody"
either at the command line or the shell. Sometimes when people put stuff in quotes. you are supposed to replace what is in quotes with your own variable (removing the quotes), and sometimes people put quotes around the whole command you are supposed to type to show the what exactly you should type. For your example of "make -p" just type it without the quotes and it should work in both the command line and as a script.
a=whatever
f(){return 1}
echo $a # this gives whatever
a=$(f)
echo $a # now a is empty
Is this because command returned non zero, so command substitution failed, thus a is set to empty? Is this a well defined behavior? Can't find it anywhere. Could you point me to the doc describing it?
I tried to grep something from a file and assign the result to the variable. So I did a=$(grep pattern file_not_exist). Then I see grep complained that the file does not exist. I wonder why this complaint message is not assigned to the variable rather than being printed out? Is it because of stdout and stderr?
No, it's not because the command failed that a is empty.
The command didn't produce any output data, so there is no data to be captured in a.
Revise the function to read:
f(){ echo Hello; return 1; }
Now run the command substitution. You'll find that a contains Hello. If you check $? immediately after the assignment, it contains 1, the status returned from the function. Exit statuses and command outputs are separate.
The documentation is in the GNU Bash manual for Command Substitution.
I am writing a shell script where I am setting few variables, whose value is the output of commands.
The errors I get are:
$ $tag_name="proddeploy-$(date +"%Y%m%d_%H%M")"
-bash: =proddeploy-20141003_0500: command not found
now, I did read other similar questions and based on it, I tried various things:
spliting command into two calls
$ $deploy_date=date +"%Y%m%d_%H%M"
bash: =date: command not found
$ $tag_name="proddeploy-$deploy_date"
bash: proddeploy- command not found
tried using backticks
$ $tag_name=`proddeploy-$(date +"%Y%m%d_%H%M")`
bash: proddeploy-20141003_1734: command not found
bash: =: command not found
tried using $()
$ $tag_name=$(proddeploy-$(date +"%Y%m%d_%H%M"))
bash: proddeploy-20141003_1735: command not found
bash: =: command not found
But in every case the command output is getting executed. how do I make it to stop executing command output and just store as a variable? I need this to work on ZSH and BASH.
You define variables with var=string or var=$(command).
So you have to remove the leading $ and any other signs around =:
tag_name="proddeploy-$(date +"%Y%m%d_%H%M")"
deploy_date=$(date +"%Y%m%d_%H%M")
^^ ^
From Command substitution:
The second form `COMMAND` is more or less obsolete for Bash, since it
has some trouble with nesting ("inner" backticks need to be escaped)
and escaping characters. Use $(COMMAND), it's also POSIX!
Also, $() allows you to nest, which may be handy.
The accepted answer shows corrected code, but does not clarify that one of your problems is accessing a variable (using $) while assigning it, which is illegal.
For example:
$foo=4
should be
foo=4
See the difference? foo is being assigned, so you should not use $foo, which is not foo but the value of foo.
If I try following:
varReturn=$(ls)
echo $varReturn
it shows me the correct output of the listed elements in the directory.
But if I try this one:
varReturn=$(/opt/vc/bin/tvservice -n)
echo $varReturn
it doesn't show me the expected output :/
My goal is to check if an HDMI Port is connected or not.
It' very curious for me, why it works only for some commands.
I'm looking forward to getting some help here. I didn't figure out, what the problem is.
EDIT:
Now I've found another way and tried following:
varReturn=`tvservice -s`
echo $varReturn
this shows me the correct output:
But if I use another command, like this one:
varReturn=`tvservice -n`
echo $varReturn
It shows me no output at echo, but the output from the var (confusing).
It still shows me the output if I use following code:
varReturn=`tvservice -n`
#echo $varReturn
The output is shown without the blank space.
There is at least one problem with this code:
varReturn=$(/opt/vc/bin/tvservice -n)
echo $varReturn
# ^ missing double quotes around this variable
Adding those quotes will ensure that the variable is passed as a single argument to echo. Otherwise, echo sees a list of arguments and outputs each one, separated by a space.
The next possible issue is that the command is outputting to standard error, rather than standard output, so it won't be captured by $() or the old-fashioned equivalent ` `. To correct this, try:
output=$(/opt/vc/bin/tvservice -n 2>&1)
# ^ redirect standard error to standard output
echo "$output"
When you execute a shell command like varReturn=$(/opt/vc/bin/tvservice -n)
it will store the output to the variable only when the command executed successfully, else it will not hold any information because error/unsuccessful message will be redirected to standard error. Hence you have to redirected it to standard output like below:-
varReturn=$(/opt/vc/bin/tvservice -n 2>&1)
Now in both successful and unsuccessful execution case output will store in variable varReturn.
I'm trying to write a simple script in tcsh (version 6.12.00 (Astron) 2002-07-23), but I am getting tripped up by the if-then-else syntax. I am very new to script writing.
This script works:
#!/bin/tcsh -f
if (1) echo "I disagree"
However, this one does not:
#!/bin/tcsh -f
if ( 1 ) then
echo "I disagree"
else
echo "I agree"
endif
For one thing, this code, when run, echoes both statements. It seems to me it should never see the else. For another, the output also intersperses those echoes with three iterations of ": Command not found."
Edited to add: here is the verbatim output:
: Command not found.
I disagree
: Command not found.
I agree
: Command not found.
I know that the standard advice is to use another shell instead, but I am not really in a position to do that (new job, new colleagues, everyone else uses tcsh, want my scripts to be portable).
When I copy-and-paste your script and run it on my system, it correctly prints I disagree.
When I change the line endings to Windows-style, I get:
: Command not found.
I disagree
: Command not found.
I agree
: Command not found.
So, your script very likely has Windows-style line endings. Fix the line endings, and it should work. The dos2unix command is one way to do that (man dos2unix first; unlike most UNIX text-processing commands, it replaces its input file.)
The problem is that tcsh doesn't recognize ^M ('\r') as an end-of-line character. It sees the then^M at the end of the line as a single command, and prints an error message then^M: Command not found. The ^M causes the cursor to return to the beginning of the line, and the rest of the message overwrite the then.