This behavior makes passing arbitrary arguments using one variable very difficult [...]
Perhaps. But having the results of expansions go through all the usual command line processing would make it impossible to pass even one arbitrary argument intact.
Consider e.g. a script that gets a filename from somewhere, and tries to pass it to a command. Let's say we get the filename with read
:
echo -n "please enter filename: "
read -r filename
some command "$filename"
Now, if the user enters a filename like don't stop me now.txt
, running some command
will crash with a syntax error due to the single quote.
Similarly if the script is run e.g. as myscript don*.txt
and gets the filename from a command line argument:
filename=$1
some command "$filename"
Again, $filename
(or $1
already) would contain that single quote.
Worse, the filename or user input string could contain a command substitution, making it possible for merely using the variable to run arbitrary commands. The script writer would have to go through hoops to add escapes to every string read from outside the script, and some ways of doing that might already trigger the expansion processing. Plus people just wouldn't do that, and the shell would be even more unsafe a tool to use.
(For what you want it wouldn't be necessary to process expansions, just quotes and backslashes, but the issues with unpaired quotes would still be there.)
Of course, you could also say that read
should just add the necessary escapes, but would they need to be added for all other sorts of input too? How would string operations work, would they need to process the quotes too? Even something as simple as ${#var}
for the length of a variable would turn much more expensive to implement. And what would the length even mean for a variable that contained multiple distinct quoted strings?
In the end, it's best to consider the code of the script distinct from the data the script processes, and to organize it so that they don't get mixed up, so that the data only gets processed in ways explicitly set in the code.
That's what the shell pretty much does, if you remember to quote the variable expansions.
Using data in variables as-is is what every other programming language also does. E.g. in this C snippet, the string that gets printed is "foo bar"
, with the quotes, they're not parsed by the runtime environment:
char *s = "\"foo bar\"";
printf("%s\n", s);
Similarly, if it was s = "foo()"
instead, that printf()
call would not call the function foo()
, but would just print the string foo()
. (If you want to object about interpreted vs. compiled languages, we could change the example to Perl or Python.)
Now, that's just an argument as to why what you suggest doesn't seem a good idea, to me, in 2022. But really, you asked about the "why" and the design rationale. Those didn't happen in 2022, but in the 1970's and 1980's or so. Wikipedia mentions the initial release of the Bourne Shell as having happened in 1979. That's a long time ago, and the existing history of computing was a lot shorter back then than it is now. We now have the some benefit of hindsight, which probably has helped in the creation of other tools, like those shell arrays. Faster computers and more memory too.
I wouldn't reject the idea that the actual explanation behind the design might be something like "that's what they came up with when they were first figuring all this out, and for some reason it stuck". Backward compatibility goes two ways. At least now you have those shells with arrays, and completely different shells too.
eval
equivalents. With this proposal implemented, the amount of mischief that could be done by any unquoted expansion would be multiplied.eval
with untrusted data safely: unix.stackexchange.com/a/444949/542651eval
it demonstrates are safe? (It certainly describes circumstances under which they would be unsafe, and if the data is truly untrusted you have no control over whether those circumstances are present). To be clear, I don't claim that all uses ofeval
handling initially untrusted data are unsafe --${var@Q}
is a thing, as isprintf %q
, allowing one to transform untrusted data into safely-escaped content -- but I don't see where the linked answer discusses those mechanisms.eval
in the first place, as those mechanisms transform quotes to a form that makes them explicitly literal when substituted into code, ensuring you get the same behavior you do with current shells' unquoted expansion)