Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove shell with --exec #160

Merged
merged 8 commits into from
Nov 15, 2017
Merged

Remove shell with --exec #160

merged 8 commits into from
Nov 15, 2017

Conversation

reima
Copy link
Contributor

@reima reima commented Nov 3, 2017

This is a WIP/PoC for removing the intermediary shell when calling a command with --exec. See #155 for the discussion.

command: &'a mut String,
/// The ticket holds the collection of arguments of a command to be executed.
pub struct CommandTicket {
args: Vec<String>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably try to eliminate the Vec allocations between commands by making this a &'a mut Vec<String>, and then implementing Drop for CommandTicket to clear the args buffer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dropped the allocation by removing CommandTicket altogether. There was no need for CommandTicket anymore, as it only collected the arguments which were then immediately used to build and execute the Command. Now the TokenizedCommand builds the Command with the generated arguments directly and uses execute_command (previously CommandTicket::then_execute) to execute it.

@sharkdp
Copy link
Owner

sharkdp commented Nov 8, 2017

Thank you very much! I'll add my review soon.

Does this change imply that we will have to put --exec after the command line arguments (pattern and root directory)?

@reima
Copy link
Contributor Author

reima commented Nov 10, 2017

With this change, --exec takes the subsequent arguments until it either a) runs out of arguments or b) encounters a single ; as an argument. So these calls are all equivalent:

fd pattern root --exec command
fd pattern --exec command \; root
fd --exec command \; pattern root

I haven't had much time to work on this last week, but I'll try to refine this PR further over the weekend.

@reima reima changed the title [WIP] Remove shell with --exec Remove shell with --exec Nov 15, 2017
@reima
Copy link
Contributor Author

reima commented Nov 15, 2017

I refactored some things to clean up and updated the documentation to reflect the new --exec syntax. I think this PR is now mature enough for a review.

.multiple(true)
.min_values(1)
.allow_hyphen_values(true)
.value_terminator(";")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably keep ; as terminator here for consistency with find but I was shortly wondering if we should maybe change this to something that doesn't need to be escaped on the shell. The \; has always looked really awkward to me.

On the other hand, what ever we would choose as an alternative could not be passed to other programs...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm not particularly fond of ; either. I've more than once been puzzled by odd error messages when I forgot to escape it. : would be an alternative that doesn't need escaping (but doesn't convey the same "end of statement" feel).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep ; for now and I'll keep this in mind. I was also thinking of :. Would be great if we (clap) could support both.

src/app.rs Outdated
'{/.}': places the basename of the input, without the extension");
, "Execute a command for each search result"
, "Execute a command for each search result.\n\
All arguments following -exec are are taken to be arguments to the command until the \
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be --exec, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. There's also a duplicated "are".

let _ = stdout.lock().write_all(&output.stdout);
let _ = stderr.lock().write_all(&output.stderr);
}
Err(why) => eprintln!("fd: exec error: {}", why),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great if we could improve this error message here. I'm not sure what all the possible cases are that could trigger this error, but this message will be shown to the user if the given command is not available:

> fd -x foo
fd: exec error: No such file or directory (os error 2)

I encountered this by accident and was definitely confused until I realized that the command I specified did not exist.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably out of scope for this issue. While this code is all marked as new, it's actually what is left from the renamed and modified ticket.rs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll have to construct the error message yourself, by checking the error kind of the error.

if why.kind() == io::ErrorKind::NotFound {
    eprintln!("fd: exec error: command not found");
} else {
    eprintln!("fd: exec error: {}", why);
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @mmstick. @reima Oh, I missed that. I'll do it after merging this PR.

let _ = io::copy(&mut pout, &mut stdout.lock());
let _ = io::copy(&mut perr, &mut stderr.lock());
}
Err(why) => eprintln!("fd: exec error: {}", why),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

@sharkdp
Copy link
Owner

sharkdp commented Nov 15, 2017

@reima Thank you very much (again) - great work!

I added just some minor comments.

@sharkdp sharkdp merged commit 3c5d8a1 into sharkdp:master Nov 15, 2017
@mmstick
Copy link
Contributor

mmstick commented Nov 15, 2017

Maybe also have a benchmark comparison of find versus fd with parallel execution?

@sharkdp
Copy link
Owner

sharkdp commented Nov 15, 2017

Maybe also have a benchmark comparison of find versus fd with parallel execution?

Sounds great!

I imagine there are two cases where fd could potentially be faster:

  • Compared to find -exec.. simply because of parallel execution
  • Compared to find .. | parallel if the limiting factor is the filesystem (??)

Do you have any particular benchmarks in mind?

@reima reima deleted the feature/no-shell branch November 15, 2017 23:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants