3 ms·
> such as the ability to embed a shell command in backquotes and get its output easily. Unless thing have changed in the last 20 years - and they might well ha
by eesmith 3y ago
> such as the ability to embed a shell command in backquotes and get its output easily.
Unless thing have changed in the last 20 years - and they might well have! - that was also a very error-prone tool.
For example, filenames may have spaces in them, but most don't. Experience shows that people forget about that possibility during development and testing. But backticks parse the string after interpolation, causes the spaces to be interpreted as distinct terms.
Here's an example using backticks to call md5sum to compute the MD5 of the empty file "test.txt":
% touch "test.txt"
% perl
$fname = 'test.txt';
$a = `md5sum $fname`;
print("Got: $a\n");
^D
Got: 8b8db3dfa426f6bdb1798d578f5239ae test.txt
Looks good, right? Now try it with "test me.txt":
% touch "test me.txt"
% perl
$fname = 'test me.txt';
$a = `md5sum $fname`;
print("Got: $a\n");
md5sum: test: No such file or directory
md5sum: me.txt: No such file or directory
This leads to all sort of fun (in the sarcastic sense) when people use variable names with shell metacharacters:
% touch 'test.txt|echo hello'
% perl
$fname = 'test.txt|echo hello';
$a = `md5sum $fname`;
print("Got: $a\n");
Got: hello
There are ways to avoid this, but as the FAQ points out at https://perldoc.perl.org/perlfaq8#How-can-I-call-backticks-without-shell-processing%3F https://perldoc.perl.org/perlfaq8#How-can-I-call-backticks-w... , the solution is to avoid using backticks. (The example there is of passing an arbitrary search string to grep, without dealing with shell expansion.)
You might be interested to know Julia's take on the same operation. It parses the backtick-quoted string itself (without a shell), and handles interpolation in a shell-like way, with awareness of the underlying variable (eg, string, or enumerating a list or Cartesian product of strings). The result is a Cmd object, which can be passed to other functions to describe how to use or process the Cmd.
See https://docs.julialang.org/en/v1/manual/running-external-programs/#Running-External-Programs https://docs.julialang.org/en/v1/manual/running-external-pro... .
- tyingq 3y agoThat's a bit of a tradeoff though. If you expect that backticks pass things to the shell unchanged, like Perl does, you know what's happening. And as you mention, Perl offers other ways to skip passing to the shell and set args directly. Julia's approach of parsing things in backticks does remove some footguns, but it also means you have to do things like this: julia> run(`echo hello \| sort`); Else the pipe character is interpreted as a plain argument.
- sundarurfriend 3y agoThat just echoes "hello | sort", the backtick execution in Julia is only for single commands. For pipelines, there's the `pipeline` function, like so: run(pipeline(`echo hello`, `sort`))
- tyingq 3y agoAh, yeah, I had misread the docs there. Though I imagine it creates that unexpected for beginner's thing where `ls | sort` comes back with "no such file |" or similar. Basically, I'm saying there is a place for an interface that does pass a single string to the shell, and an interface where you specify args explicitly.
- mst 3y agouse IPC::System::Simple qw(capture); my $md5sum = capture(md5sum => $fname); (discovering this exists is the tricky part, of course, but once you know it exists it works nicely)
- hatsuseno 3y agoI'd like to fat comma shame you for this, but I've done the same on occasion, so meh. The thing that irks me about it is that it appears as if the function takes a hash on first read, and I'll happily cargo cult the call style until I finally read the docs for capture, in this case. I don't know, would you put this in prod for other devs to maintain?
- mst 3y agoI tend to use it consistently to mean "the thing on the left hand side of this is a name", where in this case it's the name of a program. The commonest case is absolutely the name of a hash key, but it's well worth learning to read it as "is a name of -some- sort" rather than this is a hash because there's a lot of other cases where it's used for different sorts of names, and that's been considered pretty standard for quite a while now. Examples: - DBIx::Class column and relationship definitions use it for the name of the thing (and if you generate your DBIx::Class code from the database schema using DBIx::Class::Schema::Loader that's what you're going to get). - Moo/Moose/Mojo::Base code uses it in has() calls for the name of the attribute, and in before/after/around modifiers for the name of the method being wrapped. - Mojolicious::Lite and Dancer2 both use it to separate the route name and the route handling subroutine when defining your app's routes. - Code that can take a method name as the first argument, such as Mojo::Promise, curry.pm, Mojo::Base/Object::Tap's ->tap, and many others, are usually written with the method name followed by => as well. I could think of more but I think that's quite sufficient to show that it's idiomatic at least in applications perl written using the common greenfield Web Framework(s), Object System(s) and ORM tooling of the time for at least the past 15 years. I might not put it in production for non-perl-writing -sysadmins- to understand and maintain but for anybody whose job title is developer or who wants to be able to write perl code larger than a single file, I'd much rather teach them how => gets used for names in general since they're almost certainly going to run into it All The Damn Time in documentation, examples, libraries and the application code itself.
- throw0101a 3y ago> For example, filenames may have spaces in them, but most don't. This happens in shell scripts as well, and which is why ShellCheck has a warning for it: * https://www.shellcheck.net/wiki/SC2086 https://www.shellcheck.net/wiki/SC2086 Even with something like find … | xargs … can trip people up if they're not expecting it. It's certainly why GNU (?) added -print0 / -0, but find … -exec … \; works just as well.