Is there a way to print the unexpanded definition of a recursive variable? I have a complicated build system, and a user can set some values. I'd like to echo the user definition to another file, for later use.
For example,
externals = $(HOME)/externals
all:
echo $(externals)
doesn't work, because it echos using the current definition of HOME. I'd like it to echo
the literal string $(HOME)/externals without expanding $(HOME).
The value function is probably what you want here.
8.8 The value Function
The value function provides a way for you to use the value of a variable without having it expanded. Please note that this does not undo expansions which have already occurred; for example if you create a simply expanded variable its value is expanded during the definition; in that case the value function will return the same result as using the variable directly.
The syntax of the value function is:
$(value variable)
Note that variable is the name of a variable, not a reference to that variable. Therefore you would not normally use a ā$ā or parentheses when writing it. (You can, however, use a variable reference in the name if you want the name not to be a constant.)
The result of this function is a string containing the value of variable, without any expansion occurring. For example, in this makefile:
FOO = $PATH
all:
#echo $(FOO)
#echo $(value FOO)
The first output line would be ATH, since the ā$Pā would be expanded as a make variable, while the second output line would be the current value of your $PATH environment variable, since the value function avoided the expansion.
The value function is most often used in conjunction with the eval function (see Eval Function).
Though in addition to this you are going to need to use single quotes on that echo line or the shell will expand things on you.
$ cat Makefile
externals = $(HOME)/externals
all:
echo $(externals)
allv:
echo $(value externals)
allvq:
echo '$(value externals)'
$ make all
echo /home/user/externals
/home/user/externals
$ make allv
echo $(HOME)/externals
/bin/sh: HOME: command not found
/externals
$ make allvq
echo '$(HOME)/externals'
$(HOME)/externals
Related
If I want to know, whether variable v exists in zsh, I can use ${+v}. Example:
u=xxx
v=
print ${+u} ${+v} ${+w}
outputs 1 1 0.
If I want to access the content of a variable, where I have the NAME of it stored in variable v, I can do it with ${(P)v}. Example:
a=xxx
b=a
print ${(P)b}
outputs xxx.
Now I would like to combine the two: Testing whether a variable exists, but the name of the variable is stored in another variable. How can I do this? Example:
r=XXX
p=r
q=s
Here is my approach which does NOT work:
print ${+${(P)p}} # Expect 1, because $p is r and r exists.
print ${+${(P)q}} # Expect 0, because $q is s and s does not exist
However, I get the error message zsh: bad substitution.
Is there a way I can achieve my goal without reverting to eval?
print ${(P)+p}
print ${(P)+q}
The opening parenthesis of of a Parameter Expansion Flag needs to follow immediately after the opening brace. Also, it is not necessary to explicitly substitute p or q as (P) takes care of that. Nevertheless, ${(P)+${p}} and ${(P)+${q}} would also work.
I need macro (variable) for GNU makefile, that searches file/directory by given mask at some of toplevel directories. For example, current working directory is /home/sysop/powerup/native/apps/toopl. Also exists directory /home/sysop/powerup/native/SDK/build. I want to find location of SDK/build directory relative to current one. So, I wrote recursive macros for that:
upfind = $(if $(wildcard $(1)),$(1),$(if $(filter $(abspath $(1)),$(abspath ../$(1))),$(error "can't find $(1)"),$(call upfind,../$(1))))
And I now can use it in following way:
relpath = $(call upfind, ../SDK/build)
And this assigns value "../../SDK/build" to relpath variable.
All fine, but I need propagate such macro to multiple makefiles, so I'am looking way to minimize it (upfind macro). I hope, anybody suggests me how to rewrite this macro in more compact way. For example, it's enought to limit recursion at some level, using of $(abspath) macro isn't necessary. But how can I determine recursion level or measure argument ($(1))length?
Not sure what you are asking here, so this probably isn't an answer.
The recursion limit is simple enough.
First, a bit of tidying:
assert-root = $(if $(filter $(abspath $1),$(abspath ../$1)),$(error Can't find $1))
upfind = $(if $(wildcard $1),$1,${assert-root}$(call upfind,../$1))
(Note how $assert-root is not called, it simply inherits the exisiting $1.)
Makes it a bit clearer how we can limit recursion depth: just pass an ever-lengthening $2.
maxup := 3
assert-depth = $(if $(filter ${maxup},$(words $2)),$(error Can't find [$1] within ${maxup} parents))
upfind = $(if $(wildcard $1),$1,${assert-depth}$(call upfind,../$1,_ $2))
Do both at the same time if you like
upfind = $(if $(wildcard $1),$1,${assert-depth}${assert-root}$(call upfind,../$1,_ $2))
I'm trying to set an array in ZSH (configured using oh-my-zsh).
export AR=(localhost:1919 localhost:1918)
but I'm getting an error like such:
zsh: number expected
If I don't add the export command, it's just fine. I'm not typing the above in a *rc file, just in the zsh prompt. What could be the problem?
You can't export an array in zsh.
For more info: http://zsh.sourceforge.net/Guide/zshguide02.html
Note that you can't export arrays. If you export a parameter, then
assign an array to it, nothing will appear in the environment; you can
use the external command printenv VARNAME (again no $ because the
command needs to know the name, not the value) to check. There's a
more subtle problem with arrays, too. The export builtin is just a
special case of the builtin typeset, which defines a variable without
marking it for export to the environment. You might think you could do
typeset array=(this doesn\'t work)
but you can't --- the special
array syntax is only understood when the assignment does not follow a
command, not in normal arguments like the case here, so you have to
put the array assignment on the next line. This is a very easy mistake
to make. More uses of typeset will be described in chapter 3; they
include creating local parameters in functions, and defining special
attributes (of which the export attribute is just one) for
parameters.
What is the correct / idiomatic way of passing a hash to a function?
I have sort of hit upon this but am not sure how clean this is or if there any pitfalls.
typeset -A hash
hash=(a sometext b moretext)
foo hash
foo() {
typeset -A mhash
mhash=( ${(Pkv)1} )
}
The P flag interprets result (in this case $1 as holding a parameter name). Since this resulted in only getting the values and not the keys, I bolted on the "kv" to get both keys and values.
Is this the correct way, or is there another way. btw, since i am passing an array and a hash in my actual program, I don't want to use "$*" or "$#"
I tried a little and i'm not sure there is an other way than using $# on the function.
Re: Array as parameter - Zsh mailing list
Possible answers in these questions (bash-oriented):
How to pass an associative array as argument to a function in Bash?
Passing arrays as parameters in bash
Passing array to function of shell script
In fact, when you start needing to use an array, or even worse, an associative array in a shell script, maybe it's time to switch to a more powerful script language, like perl or python.
If you don't do it for you, do it for you 6 months from now / for your successors.
I'm trying to teach myself the basics of Bourne shell scripting using a textbook I borrowed from the library, and I'm working through the questions at the end of each chapter. However, I just got to one and I'm stumped...
Write a script that takes zero or more arguments and prints the last argument in the list. For example, given the argument 'myProgram arg1 arg2 arg3', the output would be 'arg3'.
Could anyone give me some advice on how to set this one up? I'm trying to review the section on user input and arguments, but I haven't worked with that much so far, so I don't have much practice yet.
echo ${!#} # bash only
eval echo \${$#} # sh-compatible
Explanation
The number of arguments is $#. Variables can be accessed indirectly via ${!VAR}. For example:
$ VAR="PATH"
$ echo ${!VAR}
/sbin:/bin:/usr/sbin:/usr/bin
Put those together and if we have a variable $n containing an integer we can access the $nth command-line argument with ${!n}. Or instead of $n let's use $#; the last command-line argument is ${!#}!
Additionally, this can be more longwindedly written using array slicing ($# is an array holding all the command-line arguments) as:
echo ${#:$#:$#}
Oddly, you cannot use an array index:
# Does not work
echo ${#[$#]}
I'll just give you some pointers. Since you want to learn bash, you probably don't just want a piece of code that does what the question asks:
1) Do you know how to count how many arguments your bash function has?
2) Do you know how to loop?
3) Do you know how to "pop" one of the arguments?
4) Do you know how to print out the first argument?
If you put all that together, I bet you'll come up with it.