REBOL metaprogramming questions - metaprogramming

I'm very new to REBOL (i.e. yesterday).
I am using the term "metaprogramming" here, but I'm not sure if it is accurate. At any rate, I'm trying to understand how REBOL can execute words. To give an example, here is some code in TCL:
> # puts is the print command
> set x puts
> $x "hello world"
hello world
I've tried many different ways to do something similar in REBOL, but can't get quite the same effect. Can someone offer a few different ways to do it (if possible)?
Thanks.

Here's a few ways:
x: :print ;; assign 'x to 'print
x "hello world" ;; and execute it
hello world
blk: copy [] ;; create a block
append blk :print ;; put 'print in it
do [blk/1 "hello world"] ;; execute first entry in the block (which is 'print)
hello world
x: 'print ;; assign 'x to the value 'print
do x "hello world" ;; execute the value contained in 'x (ie 'print)
hello world
x: "print" ;; assign x to the string "print"
do load x "hello world" ;; execute the value you get from evaluating 'x
hello world

Related

How do I assign a File into a variable to work with that variable? [duplicate]

I have this script called test.sh:
#!/bin/bash
STR = "Hello World"
echo $STR
when I run sh test.sh I get this:
test.sh: line 2: STR: command not found
What am I doing wrong? I look at extremely basic/beginners bash scripting tutorials online and this is how they say to declare variables... So I'm not sure what I'm doing wrong.
I'm on Ubuntu Server 9.10. And yes, bash is located at /bin/bash.
You cannot have spaces around the = sign.
When you write:
STR = "foo"
bash tries to run a command named STR with 2 arguments (the strings = and foo)
When you write:
STR =foo
bash tries to run a command named STR with 1 argument (the string =foo)
When you write:
STR= foo
bash tries to run the command foo with STR set to the empty string in its environment.
I'm not sure if this helps to clarify or if it is mere obfuscation, but note that:
the first command is exactly equivalent to: STR "=" "foo",
the second is the same as STR "=foo",
and the last is equivalent to STR="" foo.
The relevant section of the sh language spec, section 2.9.1 states:
A "simple command" is a sequence of optional variable assignments and redirections, in any sequence, optionally followed by words and redirections, terminated by a control operator.
In that context, a word is the command that bash is going to run. Any string containing = (in any position other than at the beginning of the string) which is not a redirection and in which the portion of the string before the = is a valid variable name is a variable assignment, while any string that is not a redirection or a variable assignment is a command. In STR = "foo", STR is not a variable assignment.
Drop the spaces around the = sign:
#!/bin/bash
STR="Hello World"
echo $STR
In the interactive mode everything looks fine:
$ str="Hello World"
$ echo $str
Hello World
Obviously(!) as Johannes said, no space around =. In case there is any space around = then in the interactive mode it gives errors as
No command 'str' found
I know this has been answered with a very high-quality answer. But, in short, you cant have spaces.
#!/bin/bash
STR = "Hello World"
echo $STR
Didn't work because of the spaces around the equal sign. If you were to run...
#!/bin/bash
STR="Hello World"
echo $STR
It would work
When you define any variable then you do not have to put in any extra spaces.
E.g.
name = "Stack Overflow"
// it is not valid, you will get an error saying- "Command not found"
So remove spaces:
name="Stack Overflow"
and it will work fine.

How do I collect arguments from the command-line? [duplicate]

This question already has answers here:
How do I parse command line arguments in Bash?
(40 answers)
Closed 3 years ago.
I am very new to shell scripting and using CLI all together. However, I want to create a simple program that can collect arguments from a user.
Optimally, I want my shell script to get a network name and instance name from the user, and assign the values to variables.
I know that $# can be used to gather arguments, but are there any other ways? I often notice when I make a command, there is, for example, something like this: $create --instance_name NAME . Can I gather an argument by using -- to specify the parameter? If so, here is an example of the command:
$collect_variable.sh --network NETWORK_NAME --instance_name INSTANCE_ID
Once again, thank you for any help. I am very new to stack overflow and unix all together, and any help is appreciated.
You can using a variable to assign to for example:
while getopts "x:y:z:" res
do
case "$res" in
x ) paramX="$OPTARG" ;;
y ) paramY="$OPTARG" ;;
z ) paramX="$OPTARG" ;;
? ) echo "not a valid value"
esac
done
Check the rest of the links that other StackO users sent, they provide great examples!
Hope that can help you solve your issue.
Peace!
There are lots of ways of gathering arguments. You can name their position, e.g. $1 is the first argument and $14 is the fourteenth.
You can refer to the argument list like $# (this preserves spacing) or like $* (this collapses spacing):
test_args() {
echo '# as "$#"'
for argument in "$#"; do
echo "$argument"
done
echo '# as "$*"'
for argument in "$*"; do
echo "$argument"
done
echo '# as $*'
for argument in $*; do
echo "$argument"
done
}
$ test_args "1 2" three four
# as "$#"
1 2
three
four
# as "$*"
1 2 three four
# as $*
1
2
three
four
Since you're exclusively using long options separated from their arguments by spaces,
while [ "$#" -gt 0 ]; do
case "$1" in
( --network ) NETWORK="$2"; shift ;;
( --network=?* ) NETWORK="${1#*=}" ;;
( --instance_name ) INSTANCE_NAME="$2"; shift ;;
( --instance_name=?* ) INSTANCE_NAME="${1#*=}" ;;
( -* ) echo "Illegal option -- $1" >&2; exit 2 ;;
( * ) break ;; # perhaps non-options are used later
esac
shift
done
This loops on each option and parses it. There are two conditions for each option, which lets us handle when the arguments are spaced from the options (--network bobnet) or when they're assigned to the options (--network=bobnet). Spaced means we need to refer to the next argument (thus the extra shift) while assigned means we need to use parameter subsitution to remove the characters at the front of the string up until (and including) the first =.
The final shift pulls the first item off of the argument list so we can operate on the next one. Shifting separately for the two-argument clauses (rather than ending with shift 2) also allows for binary options like --verbose, which doesn't contain an argument.
You can also use a while getopts loop to support a mix of short and long arguments; see my more detailed post on getopts.

shell script function return a string

I am new to shell scripts, I am trying to create a simple function which will return the concatenated two strings that are passed as parameters. I tried with below code
function getConcatenatedString() {
echo "String1 $1"
echo "String2 $2"
str=$1/$2
echo "Concatenated String ${str}"
echo "${str}"
}
//I am calling the above function
constr=$(getConcatenatedString "hello" "world")
echo "printing result"
echo "${constr}"
echo "exit"
I see the below output when running the script with above code,
printing result
String1 hello
String2 world
Concatenated String hello/world
hello/world
exit
If you look at the code I am first calling the function and then I am echoing "printing result" statement, but the result is first comes the "printing result" and echos the statement inside the function. Is the below statement calling the function
constr=$(getConcatenatedString "hello" "world")
or
echo ${constr}
is calling the function ?
Because if I comment out #echo ${constr} then nothing is getting echoed !!! Please clarify me.
The first is calling the function and storing all of the output (four echo statements) into $constr.
Then, after return, you echo the preamble printing result, $constr (consisting of four lines) and the exit message.
That's how $() works, it captures the entire standard output from the enclosed command.
It sounds like you want to see some of the echo statements on the console rather than capturing them with the $(). I think you should just be able to send them to standard error for that:
echo "String1 $1" >&2
paxdiablo's solution is correct. You cannot return a string from a function, but you can capture the output of the function or return an integer value that can be retrieved by the caller from $?. However, since all shell variables are global, you can simply do:
getConcatenatedString() { str="$1/$2"; }
getConcatenatedString hello world
echo "Concatenated String ${str}"
Note that the function keyword is redundant with (), but function is less portable.
A more flexible, but slightly harder to understand approach is to pass a variable name, and use eval so that the variable becomes set in the caller's context (either a global or a function local). In bash:
function mylist()
{
local _varname=$1 _p _t
shift
for _p in "$#"; do
_t=$_t[$_p]
done
eval "$_varname=\$_t"
}
mylist tmpvar a b c
echo "result: $tmpvar"
On my Linux desktop (bash-3.2) it's approx 3-5x faster (10,000 iterations) than using ``, since the latter has process creation overheads.
If you have bash-4.2, its declare -g allows a function to set a global variable, so you can replace the unpretty eval with:
declare -g $_varname="$_t"
The eval method is similar to TCL's upvar 1, and declare -g is similar to upvar #0.
Some shell builtins support something similar, like bash's printf with "-v", again saving process creation by assigning directly to a variable instead of capturing output (~20-25x faster for me).

How to use if statment in UNIX

I was trying to execute the following commands.
de="hello world"
if [ $de -eq "hi" ]; then
....
....
because of the space between hello and the world, it out put an error. but if I define de="helloworld" it works fine. can you please tell me if there is a way I can use if statement with sentences that have spaces in it?
Quote the variable name,
de="hello world"
if [ "$de" = "hi" ]; then
-eq is for comparing numbers, so use = for text. See here for a nice overview on how to do various comparisions in bash.

How can I pass a complete argument list in bash while keeping mulitword arguments together?

I am having some issues with word-splitting in bash variable expansion. I want to be able to store an argument list in a variable and run it, but any quoted multiword arguments aren't evaluating how I expected them to.
I'll explain my problem with an example. Lets say I had a function decho that printed each positional parameter on it's own line:
#!/bin/bash -u
while [ $# -gt 0 ]; do
echo $1
shift
done
Ok, if I go decho a b "c d" I get:
[~]$ decho a b "c d"
a
b
c d
Which is what I expect and want. But on the other hand if I get the arguments list from a variable I get this:
[~]$ args='a b "c d"'
[~]$ decho $args
a
b
"c
d"
Which is not what I want. I can go:
[~]$ echo decho $args | bash
a
b
c d
But that seems a little clunky. Is there a better way to make the expansion of $args in decho $args be word-split the way I expected?
You can use:
eval decho $args
You can move the eval inside the script:
#!/bin/bash -u
eval set -- $*
for i;
do
echo $i;
done
Now you can do:
$ args='a b "c d"'
$ decho $args
a
b
c d
but you'll have to quote the arguments if you pass them on the CL:
$ decho 'a b "c d"'
a
b
c d
Have you tried:
for arg in "$#"
do
echo "arg $i:$arg:"
let "i+=1"
done
Should yield something like:
arg 1: a
arg 2: c d
in your case.
Straight from memory, no guarantee :-)
hmmm.. eval decho $args works too:
[~]$ eval decho $args
a
b
c d
And I may be able to do something with bash arrays using "${array[#]}" (which works like "$#"), but then I would have to write code to load the array, which would be a pain.
It is fundamentally flawed to attempt to pass an argument list stored in a variable, to a command.
Presumably, if you have code somewhere to create a variable containing the intended args. for a command, then you can change it to instead store the args into an array variable:
decho_argv=(a b 'c d') # <-- easy!
Then, rather than changing the command "decho" to accommodate the args taken from a plain variable (which will break its ability to handle normal args) you can do:
decho "${decho_argv[#]}" # USE DOUBLE QUOTES!!!
However, if you are the situation where you are trying to take arbitrary input which is expected to be string fields corresponding to intended command positional arguments, and you want to pass those arguments to a command, then you should instead of using a variable, read the data into an array.
Note that suggestions which offer the use of eval to set positional parameters with the contents of an ordinary variable are extremely dangerous.
Because, exposing the contents of a variable to the quote-removal and word-splitting on the command-line affords no way to protect against shell metachars in the string in the variable from causing havoc.
E.g., imagine in the following example if the word "man" was replaced with the two words "rm" and "-rf" and the final arg word was "*":
Do Not Do This:
> args='arg1 ; man arg4'
> eval set -- $args
No manual entry for arg4
> eval set -- "$args" # This is no better
No manual entry for arg4
> eval "set -- $args" # Still hopeless
No manual entry for arg4
> eval "set -- '$args'" # making it safe also makes it not work at all!
> echo "$1"
arg1 ; man arg4

Resources