Is there an easy way to set nullglob for one glob

烂漫一生 提交于 2019-12-20 12:07:08

问题


In bash, if you do this:

mkdir /tmp/empty
array=(/tmp/empty/*)

you find that array now has one element, "/tmp/empty/*", not zero as you'd like. Thankfully, this can be avoided by turning on the nullglob shell option using shopt -s nullglob

But nullglob is global, and when editing an existing shell script, may break things (e.g., did someone check the exit code of ls foo* to check if there are files named starting with "foo"?). So, ideally, I'd like to turn it on only for a small scope—ideally, one filename expansion. You can turn it off again using shopt -u nullglob But of course only if it was disabled before:

old_nullglob=$(shopt -p | grep 'nullglob$')
shopt -s nullglob
array=(/tmp/empty/*)
eval "$old_nullglob"
unset -v old_nullglob

makes me think there must be a better way. The obvious "put it in a subshell" doesn't work as of course the variable assignment dies with the subshell. Other than waiting for the Austin group to import ksh93 syntax, is there?


回答1:


With mapfile in Bash 4, you can load an array from a subshell with something like: mapfile array < <(shopt -s nullglob; for f in ./*; do echo "$f"; done). Full example:

$ shopt nullglob
nullglob        off
$ find
.
./bar baz
./qux quux
$ mapfile array < <(shopt -s nullglob; for f in ./*; do echo "$f"; done)
$ shopt nullglob
nullglob        off
$ echo ${#array[@]}
2
$ echo ${array[0]}
bar baz
$ echo ${array[1]}
qux quux
$ rm *
$ mapfile array < <(shopt -s nullglob; for f in ./*; do echo "$f"; done)
$ echo ${#array[@]}
0
  • Be sure to glob with ./* instead of a bare * when using echo to print the file name
  • Doesn't work with newline characters in the filename :( as pointed out by derobert

If you need to handle newlines in the filename, you will have to do the much more verbose:

array=()
while read -r -d $'\0'; do
    array+=("$REPLY")
done < <(shopt -s nullglob; for f in ./*; do printf "$f\0"; done)

But by this point, it may be simpler to follow the advice of one of the other answers.




回答2:


Unset it when done:

shopt -u nullglob

And properly (i.e. storing the previous state):

shopt -u | grep -q nullglob && changed=true && shopt -s nullglob
... do whatever you want ...
[ $changed ] && shopt -u nullglob; unset changed



回答3:


This is just a tiny bit better than your original suggestion:

local nullglob=$(shopt -p nullglob) ; shopt -s nullglob

... do whatever you want ...

$nullglob ; unset nullglob



回答4:


This may be close to what you want; as is, it requires executing a command to expand the glob.

$ ls
file1 file2
$ array=( $(shopt -s nullglob; ls foo*) )
$ ls foo*
ls: foo*: No such file or directory
$ echo ${array[*]}
file1 file2

Instead of setting array in the subshell, we create a subshell using $() whose output is captured by array.




回答5:


This is the simplest solution I've found:

For example, to expand the literal **/*.mp3 into a glob for only a particular variable, you can use

VAR=**/*.mp3(N)

Source: https://unix.stackexchange.com/a/204944/56160



来源:https://stackoverflow.com/questions/9126060/is-there-an-easy-way-to-set-nullglob-for-one-glob

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!