I would expect the unexport make directive to unset a variable. From reading online it seems like that's what it's supposed to do:
https://www.gnu.org/software/make/manual/html_node/Variables_002fRecursion.html
If you want to prevent a variable from being exported, use the unexport directive, like this:
unexport variable …
So I made a small example. In my first run BAR isn't set and as expected nothing is printed; however, then I set an environment variable. I would expect nothing to be printed because I'm using the unexport directive, except the value dogs still gets printed.
$ cat Makefile
unexport BAR
foo:
#echo BAR=$(BAR)
$ make
BAR=
$ export BAR=dogs
$ make
BAR=dogs
$ uname -a
Darwin Maxs-MacBook-Pro-Work.local 19.2.0 Darwin Kernel Version 19.2.0: Sat Nov 9 03:47:04 PST 2019; root:xnu-6153.61.1~20/RELEASE_X86_64 x86_64
$ make --version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program built for i386-apple-darwin11.3.0
$ man make | grep unexport
What's going on here? Why is dogs still printed?
Related
My make version on macOS High Sierra Version 10.13.6 looks like this:
$ make --version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program built for i386-apple-darwin11.3.0
My current directory looks like this:
$ ls -1
Makefile
a.txt
b.txt
c.txt
My Makefile looks like this:
*.txt: FORCE
echo Target $# invoked
FORCE:
I am able to use this Makefile like this:
$ make a.txt
echo Target a.txt invoked
Target a.txt invoked
$ make b.txt
echo Target b.txt invoked
Target b.txt invoked
$ make c.txt
echo Target c.txt invoked
Target c.txt invoked
Where is this behavior of *.txt expanding to matching targets in the current directory documented? I tried searching the man pages and documents but could not find anything that specifies that we can use glob patterns as target. Can I rely on this behavior in my Makefiles?
See the GNU make manual right in the introduction to rule syntax:
The targets are file names, separated by spaces. Wildcard characters may be used (see Using Wildcard Characters in File Names)
and the entire section Using Wildcard Characters in File Names.
You cannot just look at man pages: the man pages only describe the command line interface and possible the barest summary of makefile syntax. They don't attempt to describe everything about how to write a makefile.
If you looked in the GNU make manual but didn't find this I'd be interested to understand what you searched for and where you expected to find the information so we can think about improving the docs.
I have installed some GNU packages on my macOS Sierra, which include bash, coreutils, which, etc. Now I can use which -a bash | xargs -I % echo % "--version" | sh to check all version info of bash, but there is no separation between two version info:
$ which -a bash | xargs -I % echo % "--version" | sh
GNU bash, version 4.4.12(1)-release (x86_64-apple-darwin16.3.0)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
# There should be one or more blank lines as separation.
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
Copyright (C) 2007 Free Software Foundation, Inc.
I tried ... echo -e % "--version\n" ..., but it can't work. Please help me, thanks.
For a bit more control of the output, use a loop:
which -a bash |
while read cmd; do
printf '%s\t----------\n' "$cmd"
command "$cmd" --version
echo
done
I'd write this as follows:
IFS=: read -r -a path_entries <<<"$PATH"
find "${path_entries[#]}" -maxdepth 1 -name bash -type f -exec '{}' --version ';'
Note:
No use of which. This isn't a shell builtin in bash, so it gives you none of the benefits you'd get from type (ie. awareness of functions and aliases); and because its output is line-oriented, it can't unambiguously emit all possible filenames (such as those containing newlines -- which, yes, are valid).
No reliance on line-oriented content, generally. When the placeholder ({}) is passed as a single token, find -exec substitutes the exact filename returned for that token. Because the domain of possible argv values (all valid C strings, which is to say, no strings containing NULs) matches the domain of possible filenames on common systems, this avoids introducing potential bugs.
No generating text and piping that to an interpreter. This is an extremely error-prone technique, inducing shell-injection vulnerabilities: Consider what would happen if a PATH contained /tmp/$(rm -rf ~), and that directory contained a bash executable.
I am working with: s3-bash, when I run it in my local environment (OS X 10.10.1) I don't have any problems, when I try to run it on a ubuntu server 14.04.1 I get the following error:
./s3-common-functions: line 66: temporaryFiles: unbound variable
./s3-common-functions: line 85: temporaryFiles: unbound variable
I've looked at the s3-common-functions script and the variable looks to be initialized properly (as an array):
# Globals
declare -a temporaryFiles
But there is a note in the comment, and I'm sure if it's related:
# Do not use this from directly. Due to a bug in bash, array assignments do not work when the function is used with command substitution
function createTemporaryFile
{
local temporaryFile="$(mktemp "$temporaryDirectory/$$.$1.XXXXXXXX")" || printErrorHelpAndExit "Environment Error: Could not create a temporary file. Please check you /tmp folder permissions allow files and folders to be created and disc space." $invalidEnvironmentExitCode
local length="${#temporaryFiles[#]}"
temporaryFiles[$length]="$temporaryFile"
}
There appears to be a bash behaviour change at play here.
Found by kojiro: CHANGES
hhhh. Fixed a bug that caused `declare' and `test' to find variables that
had been given attributes but not assigned values. Such variables are
not set.
$ bash --version
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
$ set -u
$ declare -a tF
$ echo "${#tF[#]}"
0
vs.
$ bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ set -u
$ declare -a tF
$ echo "${#tF[#]}"
0
vs.
$ bash --version
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ set -u
$ declare -a tF
$ echo "${#tF[#]}"
-bash: tF: unbound variable
You can use declare -a tF=() on the newer bash versions to work around this.
$ declare -a tF=()
$ echo "${#tF[#]}"
0
Bash can substitute an empty value into unset variables using a dash.
set -u
my_array=()
printf "${my_array[#]-}\n"
This specific example will print nothing, but it will not give you an unbound variable error either.
Stolen from Bash: Error `Unbound variable' when appending to empty array1.
However, when used for a loop, you get one iteration (not zero) then:
for i in "${my_array[#]-}"
do
echo $i
done
Expectation might be to get zero iterations.
It could be fixed this way then:
[ ${#my_array[#]} -gt 0 ] &&
for i in "${my_array[#]}"
do
echo $i
done
Changed declaration of array for temporaryfiles
declare -a temporaryFiles
to:
temporaryFiles=()
Why this is different / non-function in ubuntu 14.04.1 Linux 3.13.0-32-generic x86_64 versus OS X I am not sure ?
find $fullfolder -type f |
while read fullfile
do
filename=$(basename "$fullfile")
ext=$([[ $filename = *.* ]] && printf %s ${filename##*.} || printf 'NONE')
arr+=($ext)
echo ${#arr[#]}
done
echo ${#arr[#]}
Why does the ${#arr[#]} inside the for loop produce the correct result but the one outside gives 0?
Let's say my Makefile is something like:
DIR :=#
foobar:
ls ${DIR}
When i type
mak[tab] f[tab]
it gives correctly
make foobar
But
make foobar D[tab]
doesn't do the magic
make foobar DIR=
So my question is: is there a way to autocomplete Makefile variables too (aside targets) in bash?
This answer is far from complete. To grep all variables in a Makefile we use make -p to print the Makefile database:
# GNU Make 3.81
# Copyright (C) 2006 Free Software Foundation, Inc.
# This is free software; see the source for copying conditions.
# There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
# This program built for x86_64-pc-linux-gnu
# Make data base, printed on Mon Oct 13 13:36:12 2014
# Variables
# automatic
<D = $(patsubst %/,%,$(dir $<))
# automatic
?F = $(notdir $?)
# environment
DESKTOP_SESSION = kde-plasma
# ...
# makefile (from `Makefile', line 1)
DIR :=
We are looking for lines starting with # makefile (from 'Makefile', line xy) and extract the name of the following variable:
$ make -p | sed -n '/# makefile (from/ {n; p;}'
MAKEFILE_LIST := Makefile
DIR :=
In the next step we remove everything but the variable name (everything after :=):
$ make -p Makefile | sed -n '/# makefile (from/ {n; s/^\([^.#:= ]\+\) *:\?=.*$/\1/p;}'
MAKEFILE_LIST
DIR
The following lines demonstrate how it could be done:
_make_variables()
{
# get current completion
local cur=${COMP_WORDS[COMP_CWORD]}
# get list of possible makefile variables
local var=$(make -p Makefile | sed -n '/# makefile (from/ {n; s/^\([^.#:= ]\+\) *:\?=.*$/\1=/p;}')
# don't add a space after completion
compopt -o nospace
# find possible matches
COMPREPLY=( $(compgen -W "${var}" -- ${cur}) )
}
# use _make_variables to complete make arguments
complete -F _make_variables make
Now make D[tab] results in make DIR=.
Sadly you will loose all the file and target completion with this approach. Also it would be useful to remove some more variables (e.g. MAKEFILE_LIST) from the completion output.
Maybe it is worth to fill a wish/bug report against the bash-completion project to add this feature.
While attempting to debug a job submission script, I ended up narrowing down the bug to this:
[testuser#bes ~]$ var=( 1 foo1*bar4 echo 1*4=4 )
[testuser#bes ~]$ echo "${var[#]}"
1 foo1*bar4 echo 1*4=4
[testuser#bes ~]$ cd /data/testuser/jobs/example/a16162/
[testuser#bes a16162]$ var=( 1 foo1*bar4 echo 1*4=4 )
[testuser#bes a16162]$ echo "${var[#]}"
1 foo1-bar4 foo1*bar4 echo 1*4=4
[testuser#bes a16162]$
That is an uncut transcript of a fresh bash session. Anyone have any idea how that one works? Is this some archaic feature of bash that I've never heard of before, or just a really weird bug?
Versions (yes I know it's out dated):
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
Linux bes 2.6.18-194.11.3.el5 #1 SMP Mon Aug 30 16:19:16 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux
EDIT: This is for something that needs to process a user-passed array, and I'd rather use this method than a triplet of rather awkward awk hacks. They're trivial "extract element 2" sorts of things, which is why using the array seems nicer.
Globs are still globbed when the array is formed. If you don't want this then you need to quote or escape them.
$ var=( 1 "foo1*bar4" echo "1*4=4" )
What does ls /data/testuser/jobs/example/a16162/foo1* reveal?
You can disable filename globbing with set -f and re-enable it with set +f