I use Bash 4.3.48(1) in Ubuntu 16.04 (xenial) with a LEMP stack.
I try to create a php.ini
overridings file in a version agnostic way with printf
.
1) The version agnostic operation fails:
printf "[PHP]\n post_max_size = 200M\n upload_max_filesize = 200M\n cgi.fix_pathinfo = 0" > /etc/php/*/fpm/zz_overrides.ini
The following error is given:
bash: /etc/php/*/zz_overrides.ini: No such file or directory
2) The version gnostic operation succeeds:
printf "[PHP]\n post_max_size = 200M\n upload_max_filesize = 200M\n cgi.fix_pathinfo = 0" > /etc/php/7.0/fpm/zz_overrides.ini
As you can see, both are basically identical besides *
vs 7.0
.
- I didn't find a clue about this (regex?) problem in
man printf
. - I searched in Google and found nothing about "allowing regex in printf".
Why does the version agnostic operation fails and is there any bypass?
Edit: If possible, it is most important for me to use a one-line operation.
Best Answer
The behavior of a pattern match in a redirection appears to differ between shells. Of the ones on my system, dash and ksh93 don't expand the pattern, so you get a file name with a literal
*
. Bash expands it(1), but only if the pattern matches one file. It complains if there are more filenames that match. Zsh works as if you gave multiple redirections, it redirects the output to all matching files.(1) except when it's non-interactive and in POSIX mode
If you want the output to go to all matching files, you can use
tee
:If you want it to go to only one file, the problem is to decide which one to use. If you want to check that there's only one file that matches the pattern, expand the whole list and count them.
In bash/ksh:
In standard shell,
set
the positional parameters and use$#
for the count.Of course, if the pattern doesn't match any file, it's left as-is, and since the glob is in the middle, the result points to a nonexisting directory. It's the same as trying to create
/path/to/file
, before/path/to
exists, it's just here we have/path/*
instead, literally, with the asterisk.To deal with that, you'd have to expand the directory name(s) without the filename, and then append the file name to all the directories. This is a bit ugly...
and then we can use that array:
Or we could take the easy way out and loop over the filename pattern. It's a bit unsatisfactory in the more general case, since it requires running the main command once for each output file, or using a temporary file to hold the output.