Why is "& ;" invalid syntax? - bash

I am trying to run a for loop on the terminal where I want to send each iteration to background process so that all of them run simultaneously.
Following is the command running one by one
for i in *.sra; do fastq-dump --split-files $i ; done # ";" only
I have highlighted the semicolon.
To run simultaneously this works
for i in *.sra; do fastq-dump --split-files $i & done # "&" only
But this gives an error
for i in *.sra; do fastq-dump --split-files $i & ; done # "& ;"
It would be nice if some one explains what is going on here. I know this should be written in a shell script way with proper indentation, but some times I only have this command to run.

& and ; both terminate the command that precedes them.
You can't write & ; any more than you could write ; ; or & &, because the language only allows a command to be terminated once (and doesn't permit a zero-word list as a command).
Thus: for i in *.src; do fastq-dump --split-files "$i" & done is perfectly correct as-is, and does not require an additional ;.

Related

For loop in Cygwin bash shell followed by ">"

When I type the following command in a cygwin bash shell:
for i in $(ls) do echo $i done
I get a ">" asking me to keep typing, as opposed to the expected behavior. Why?
You need to separate your for, do and done statements.. Try this:
for i in $(ls); do echo $i; done
You can also separate the statements with newlines. For exmaple:
cygwin$ for i in $(ls)
> do
> echo $i
> done
Your for loop is still waiting for the semicolon or newline that terminates the list of values. So far, your loop with set i to the list of words produced by ls, the word do, the word echo, the words produced by the expansion of the current value of i, and the word done.
The > is the so-called secondary prompt, which indicates that the shell is still waiting for input to complete the command started by for.

Not able to overwrite the file properly through unix script

Below the script which I am using to overwrite the chk.sas file but unfortunately it's only overwrite the last statement of code "run;" in chk.sas file.
#!/bin/bash
x=$(pwd)
echo "libname sasdata '$x';" > $x/chk.sas
echo "proc print data=sasdata.data ;" > $x/chk.sas
echo "run;" > $x/chk.sas
sas chk.sas
exit 0
Below is the desired result which I am expecting from the script but I am not sure where I am doing mistake in the script.
libname sasdata '/home/usr' ;
proc print data=sasdata.data ;
run;
Thank you in advance for help.
Each time that > is used, the previous file is overwritten. One solution is to use >> which means append. Another solution is to group all the echo commands together and just use > once:
#!/bin/bash
x=$(pwd)
{
echo "libname sasdata '$x';"
echo "proc print data=sasdata.data ;"
echo "run;"
} > "$x/chk.sas"
sas "$x/chk.sas"
Notes
Unless you specifically want word splitting and pathname expansion, put shell variables inside double quotes. Thus, above, we use "$x/chk.sas" in place of $x/chk.sas.
Use exit 0 if you want the script to return a success exit code even if the sas command failed. If you omit the exit command, the script will exit with the exit code of the last command run (in this case, sas). This is likely more informative for the whatever code invokes the script.
Alternative: here-doc
Another way to write several lines to a file is to use a here-doc:
#!/bin/bash
x=$(pwd)
cat >"$x/chk.sas" <<EOF
libname sasdata '$x';
proc print data=sasdata.data ;
run;
EOF
sas "$x/chk.sas"
For more information, look at the section entitled Here Documents in man bash.

how can I pipe two echo in one line?

In a DOS command prompt :
echo b | echo a
results in a being displayed, but not b. Why ? How can I pipe 2 echo in one line ?
My overall goal is to launch several process from a Haskell program, and see when each starts and ends. I would issue commands like :
echo start | myprogram | echo end
See Command Shell Overview article to learn how to run multiple commands in one line. There is a section Using multiple commands and conditional processing symbols
command1 & command2
Use to separate multiple commands on one command line. Cmd.exe runs the first command, and then the second command.
command1 && command2
Use to run the command following && only if the command preceding the symbol is successful*. Cmd.exe runs the first command, and then runs the second command only if the first command completed successfully*.
*) If a command completes an operation successfully, it returns an exit code of zero (0) or no exit code.
[quote compacted by author of answer]
...and several others.
Piping (redirecting command's output into another command's input) is not a solution here, just go with serial execution like shown above.
Wait, there's more :)
If you want to redirect the output for example to log, you would typically do:
echo a > out.txt & echo b >> out.txt & echo c >> out.txt
But there is a shorthand for that which uses parentheses mentioned in the above article:
(echo a & echo b & echo c) > out.txt
or equivalent (more readable with long commands)
> out.txt (echo a & echo b & echo c)
As you can see, this spares you from combining of > and >> which is typically one more thing to check in the command.
As Stephan suggested, I'll use :
echo start & myprogram & echo end

Dash double semicolon (;;) syntax

I'm trying to find a way to run multiple commands in parallel in sh and wait for it completion.
I've found that following doesn't work (sh: 1: Syntax error: ";" unexpected):
sh -c '(sleep 3 && echo 1) & ; (sleep 3 && echo 2) & ; wait'
But this syntax works as expected:
sh -c '(sleep 3 && echo 1) & ;; (sleep 3 && echo 2) & ;; wait'
But I don't understand what is the difference.
What is the meaning of ;; and when it should be used?
;; is only used in case constructs, to indicate the end of an alternative. (It's present where you have break in C.)
case $answer in
yes) echo 'yay!';;
no) echo 'boo!';;
esac
Syntactically, ; and & both mark the end of a command. A newline is equivalent to ;, in a first approximation. The difference between them is that ; or newline indicates that the command must be executed in the foreground, whereas & indicates that the command must be executed in the background.
So here you need & wait. & ; is a syntax error (you can't have an empty command). & ;; is also a syntax error; ash lets it go (as if you'd written just &), but bash complains. Evidently your sh is some ash variant (such as dash, which is /bin/sh on many Debian derivatives).
It should be used in a case statement, between cases. The issue you're having here is that both & and ; are command separators, and you should only be using one of them.

How to launch a huge number of processes in bash

I am trying to run a.out lot of times from command line, but am not able to start the processes in background because bash treats it as a syntax error.
for f in `seq 20`; do ./a.out&; done //incorrect syntax for bash near '&'
How can I place & on command line so that bash doesn't complain, and I am allowed to
run these processes in background, so that I can generate load on the system.
P.S: I don't want to break it into multiple lines.
This works:
for f in `seq 20`; do ./a.out& done
& terminates a command just like ; or &&, ||, |.
This means that bash expects a command between & and ; but can't find one. Hence the error.
& is a command terminator as well as ; ; do not use both.
And use bash syntax instead of using seq, which is not available on all Unix systems.
for f in {1..20} ; do ./a.out& done
Remove the ; after a.out:
for f in `seq 20`; do ./a.out& done
Break that into multiple lines or remove the ; after the &

Resources