Syntax error: end of file unexpected - shell

I define a function in Makefile
define write_file
for i in $( seq 1 10 )
do
echo "1234567" >> "tmp/test.txt"
done
endef
And
pre:
mkdir -p exe tmp
${call write_file}
But when I make pre,I got error:
mkdir -p exe tmp
for i in
/bin/sh: 1: Syntax error: end of file unexpected

Each line of a make-recipe is a single command that make runs in a single
new shell. So you need to make the body of the macro write_file into a single
shell command.
In addition, make expands anything of the unescaped form $(....),
treating .... as a defined or undefined make expression. So in your
case, make expands $( seq 1 10 ) to nothing. To stop make doing
that and let the shell expand $( seq 1 10 ), you need to escape $ for make,
which you do by writing $$ instead. The same goes for any $ in a make-recipe
that you intend to be expanded by the shell.
Putting these points together, you want:
define write_file
for i in $$( seq 1 10 ); \
do \
echo "1234567" >> "tmp/test.txt"; \
done
endef

Related

How do I source a zsh script from a bash script?

I need to extract some variables and functions from a zsh script into a bash script. Is there any way to do this? What I've tried (some are embarrassingly wrong, but covering everything):
. /script/path.zsh (zsh-isms exist, so it fails)
exec zsh
. /script/path.zsh
exec bash
zsh << 'EOF'
. /script/path.zsh
EOF
chsh -s zsh
. /script/path.zsh
chsh -s bash
This thread is the closest I've found. Unfortunately, I have too many items to import for that to be feasible, and neither script is anywhere near a polyglot. However, the functions and variables that I need to import are polyglots.
You can "scrape" the zsh source file for what you need, then execute the code in bash using eval. Here's an example for doing this for a few functions:
File script.zsh:
test1() {
echo "Hello from test1"
}
test2() {
echo $((1 + $1))
}
File script.sh (bash):
# Specify source script and functions
source_filename="script.zsh"
source_functions=" \
test1 \
test2 \
"
# Perform "sourcing"
function_definitions="$(python -B -c "
import re
with open('${source_filename}', mode='r') as file:
content = file.read()
for func in '${source_functions}'.split():
print(re.search(func + r'\(\).*?\n}', content, flags=re.DOTALL).group())
" )"
eval "${function_definitions}"
# Try out test functions
test1 # Hello from test1
n=5
echo "$n + 1 is $(test2 $n)" # 5 + 1 is 6
Run the bash script and it will make use of the functions test1 and test2 defined in the zsh script:
bash script.sh
The above makes use of Python, specifically its re module. It simply looks for character sequences of the form funcname(), and assumes that the function ends at the first }. So it's not very general, but works if you write your functions in this manner.

how using eval in makfile command change macros value with bash variable

I have a bash function inside the makefile command and want to change macros value.
Is it possible?
C_DFLAGS :=
gui :
parse_flags () { echo $$1; for word in $$1; do if [ $${word::2} = -D ] ; then $(eval C_D_FLAGS+=$${word}); fi ; done ; } ; parse_flags "-D/test -D/TEST"
#echo "C_D_FLAGS :$(C_D_FLAGS)"
$(eval) will be interpreted before your actual bash function call. You cannot update make variables from bash - it's a downstream process.
However, the code you try to run is fairly simple to replace with a native syntax, i.e.:
$ cat Makefile
C_D_FLAGS :=
gui: C_D_FLAGS += -D/test -D/TEST
gui:
#echo "C_D_FLAGS: $(C_D_FLAGS)"
$ make gui
C_D_FLAGS: -D/test -D/TEST
If the flags are provided from elsewhere, they can also be filtered, i.e.:
$ cat Makefile
C_D_FLAGS :=
gui: C_D_FLAGS += $(filter -D%,$(EXTRA_FLAGS))
gui:
#echo "C_D_FLAGS: $(C_D_FLAGS)"
$ make gui
C_D_FLAGS:
$ make gui EXTRA_FLAGS="-Isomething -DFOO -m32"
C_D_FLAGS: -DFOO

How preserve words containing spaces while expanding variables in bash script?

My scheme is the following:
I have a shell script that executes a command which calls a C program :
name=$1
StringWithSpaces=$name
command="someprogram.out $StringWithSpaces $otherarguments"
$command
where name is a string with spaces s.a. "String With Spaces" passed to the shell from another python script.
My problem is that when I read that argument in C, it is passed as several arguments instead of just one. I have tried $#, $* and all that stuff. I have also tried to make a function in C that separate the several argv[i] within the StringWithSpaces one, but I am a bit stuck. I wish I could read the variable in C just as a single argument, to make the program as simple as I can.
This is the exact shell code (bash):
#!/bin/bash
#$1 Nombre de la base de datos
#$2 $3 gps coordinates
#$4 geoDistance (radio en km)
#$5 imDownload (a 0: solo se descargan GPS, a 1 también imágenes y tags)
#$Disabled keywords (opcional, lista de keywords separados por comas)
#Generamos el base path
BASE_DIR=`pwd`
#BASE_DIR=${BASE_DIR%/*}
EXE_DIR=$BASE_DIR/FlickrAPI/bin
DB_PATH=$BASE_DIR/data
#Exportamos las librerías, necesario para que funcione el código
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${BASE_DIR}/FlickrAPI/lib
cont=1;
#General DB
name=$1
gps="$2 $3"
geoDistance=$4
numImagesDB=3751
ncores=400;
imDownload=$5
dbDir=$DB_PATH/$name
mkdir "$dbDir";
rm -rf "$dbDir"/imagesDB
rm -rf "$dbDir"/tagsDB
rm -rf "$dbDir"/gps.txt
mkdir "$dbDir"/imagesDB
mkdir "$dbDir"/tagsDB
#tidx=`seq 7 $#`;
#keywords="";
#for ((i=7;i<=$#;i++))
#do
# keywords=$keywords" "${!i};
#done
keywords=$6;
export LD_LIBRARY_PATH=/home/user/anaconda2/lib/
command="$EXE_DIR/get_GPS_bimestral $dbDir $gps z $numImagesDB $ncores $geoDistance $imDownload $keywords"
echo $command
$command
Put the command in an array with:
Command=(someprogram.out "$StringWithSpaces" "$otherarguments")
When array is expanded in quotes using # to request all its members, as in "${Command[#]}", then bash expands its array member to a single word. So the desired strings with spaces are kept as single strings.
Here is a sample script:
#!/bin/bash -e
function ShowArguments()
{
for argument in "$#"; do
echo "Argument: $argument"
done
}
Argument1="abc def"
Argument2="pdq xyz"
Command=(ShowArguments "$Argument1" "$Argument2")
echo "${Command[#]}"
"${Command[#]}"
The output from the above is:
ShowArguments abc def pdq xyz
Argument: abc def
Argument: pdq xyz
You may want "$otherarguments" to be $otherarguments. Or, if it contains a string with spaces that should be kept as a string, you should handle it the same way, as an array that is expanded with ${otherarguments[#]} in quotes. Here is an example with quoting of one of the variables used to hold arguments:
#!/bin/bash -e
function ShowArguments()
{
for argument in "$#"; do
echo "Argument: $argument"
done
}
Argument1="Single argument with multiple words"
Argument2=("Multiple argument" with various "numbers of words")
Command=(ShowArguments "$Argument1" "${Argument2[#]}")
echo "${Command[#]}"
"${Command[#]}"
It produces:
ShowArguments Single argument with multiple words Multiple argument with various numbers of words
Argument: Single argument with multiple words
Argument: Multiple argument
Argument: with
Argument: various
Argument: numbers of words
After that, you might want to replace echo with a fancier command that quotes its arguments, to show the proper quoting to the user. But that is an aesthetic or user-interface choice; it will not affect the command executed.
My problem is that when I read that argument in C, it is passed as several arguments instead of just one.
in your script replace
command="$EXE_DIR/get_GPS_bimestral $dbDir $gps z $numImagesDB $ncores $geoDistance $imDownload $keywords"
echo $command
$command
by
command="$EXE_DIR/get_GPS_bimestral \"$dbDir $gps z $numImagesDB $ncores $geoDistance $imDownload $keywords\""
echo $command
echo $command | bash
Explanations :
if the shell script b is :
./a.out "$* 3 4"
and c.c is
#include <stdio.h>
int main(int argc, char ** argv)
{
printf("argc = %d, argv[1] = '%s'\n", argc, argv[1]);
}
then :
pi#raspberrypi:/tmp $ ./b 1 2
argc = 2, argv[1] = '1 2 3 4'
the C program receives only one arg, that is ok
but if the script is modified to be for instance :
command="./a.out \"$* 3 4\""
echo $command
$command
that doesn't work :
pi#raspberrypi:/tmp $ ./b 1 2
./a.out "1 2 3 4"
argc = 5, argv[1] = '"1'
so if you want to store in a var to echo then execute it you can do :
command="./a.out \"$* 3 4\""
echo $command
echo $command | bash
execution:
pi#raspberrypi:/tmp $ ./b 1 2
./a.out "1 2 3 4"
argc = 2, argv[1] = '1 2 3 4'
of course if you want for example 4 received as a separate argument just put it outside the string :
command="./a.out \"$* 3\" 4"
echo $command
echo $command | bash
execution :
pi#raspberrypi:/tmp $ ./b 1 2
./a.out "1 2 3" 4
argc = 3, argv[1] = '1 2 3'

Write a script to put a series of files in sequence

I am beginning in scripting and I am trying to write a script in bash. I need a script to write a sequence of several file names that are numbered from 1 to 50 inside one file. These are trajectory files from MD simulations. My idea was to write something like:
for valor in {1..50}
do
echo "
#!/bin/bash
catdcd -o Traj-all.dcd -stride 10 -dcd traj-$valor.dcd" > Traj.bash
exit
However, I just got one file with the following line:
#!/bin/bash
catdcd -o Traj-all.dcd -stride 10 -dcd traj-50.dcd
exit
But what I really want is something like:
#!/bin/bash
catdcd -o Traj-all.dcd -stride 10 -dcd traj-1.dcd -dcd traj-2.dcd -dcd traj-3.dcd ... -dcd traj-50.dcd
exit
How can I solve this problem?
You need to read a bit more about bash brace expansion. You can do this:
{
echo "#!/bin/bash"
echo "catdcd -o Traj-all.dcd -stride 10" "-dec traj-"{1..50}".dcd"
# ^^^^^^^^^^^^^^^^^^^^^^^^^
} > Traj.bash
The underlined part is where the brace expansion will get expanded by the shell into
-dec traj-1.dcd -dec traj-2.dcd ... -dec traj-50.dcd
You don't need to explicitly end your script with exit -- the shell will exit by itself when it runs out of commands.
> truncates the file on open. Either only use it once before the loop to create the file and then append (>>) within the loop, or redirect the entire loop.
> foo
for ...
do ...
echo ... >> foo
done
...
{
for ...
do ...
echo ...
done
} > foo

Error converting a bash function to a csh alias

I am writing a csh alias so that I can use the following bash function in my csh :
function up( )
{
LIMIT=$1
P=$PWD
for ((i=1; i <= LIMIT; i++))
do
P=$P/..
done
cd $P
export MPWD=$P
}
(I stole the above bash function from here)
I have written this:
alias up 'set LIMIT=$1; set P=$PWD; set counter = LIMIT; while[counter!=0] set counter = counter-1; P=$P/.. ; end cd $P; setenv MPWD=$P'
However, I am getting the following error:
while[counter!=0]: No match.
P=/net/devstorage/home/rghosh/..: Command not found.
end: Too many arguments.
and my script is not working as intended. I have been reading up on csh from here.
I am not an expert in csh and what I have written above is my first csh script. Please let me know what I am doing wrong.
You can also do this
alias up 'cd `yes ".." | head -n\!* | tr "\n" "\/"`'
yes ".." will repeat the string .. indefinitely; head will truncate it to the number passed as argument while calling the alias ( !* expands to the arguments passed; similar to $# ) and tr will convert the newlines to /.
radical7's answer seems to be more neat; but will only work for tcsh ( exactly wat you wanted ). This should work irrespective of the shell
You can use the csh's repeat function
alias up 'cd `pwd``repeat \!^ echo -n /..`'
No loops needed (which is handy, because while constructs in tcsh seem very finicky)
For multiple lines of code, aliases must be within single quotes, and each end of line must precede a backslash. The end of the last line must precede a single quote to delimit the end of the alias:
alias up 'set counter = $1\
set p = $cwd\
while $counter != 0\
# counter = $counter - 1\
set p = $p/..\
end\
cd $p\
setenv mpwd $p'
By the way, variables set with set are better with the equal sign separated from the variable name and content; setenv doesn't require an equal sign; math functionality is provided by #; control structures make use of parentheses (though aren't required for simple tests); use $cwd to print the current working directory.

Resources