Execute a command in specified directories recursively.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
3.8 KiB

  1. // Standard library modules.
  2. use std::process::Stdio;
  3. // Third-party modules.
  4. use clap::{
  5. crate_authors, crate_description, crate_name, crate_version, App, Arg,
  6. };
  7. use execute::command;
  8. use walkdir::WalkDir;
  9. fn main() {
  10. // Define the default CLI values here.
  11. let default_depth = "0";
  12. // Create the CLI with all possible flags and options.
  13. let cli = App::new(crate_name!())
  14. .about(crate_description!())
  15. .author(crate_authors!())
  16. .version(crate_version!())
  17. .args(&[
  18. // Boolean flags.
  19. Arg::with_name("verbose")
  20. .long("verbose")
  21. .long_help("Print extra information while running.")
  22. .takes_value(false),
  23. // Options that are only allowed once.
  24. Arg::with_name("command")
  25. .long("command")
  26. .long_help("The command(s) to execute in each directory.")
  27. .short("c")
  28. .required(true)
  29. .multiple(true)
  30. .takes_value(true),
  31. Arg::with_name("depth")
  32. .default_value(default_depth)
  33. .long("depth")
  34. .long_help(
  35. "How many directories deep we should go.\nUse -1 for unlimited depth.",
  36. )
  37. .takes_value(true),
  38. // Options that are allowed multiple times.
  39. Arg::with_name("directory")
  40. .long("dir")
  41. .long_help("One or more directories to execute the command in.")
  42. .multiple(true)
  43. .short("d")
  44. .required(true)
  45. .takes_value(true),
  46. ])
  47. .get_matches();
  48. // Extract boolean flags.
  49. let verbose = cli.is_present("verbose");
  50. // Extract the commands to execute.
  51. let target_commands = cli
  52. .values_of("command")
  53. .expect("Failed to extract the command to execute from the CLI");
  54. // Extract the target directories.
  55. let dirs = cli
  56. .values_of("directory")
  57. .expect("Failed to extract the target directories from the CLI");
  58. // Extract the maximum depth we want to walk.
  59. let depth = cli.value_of("depth").unwrap_or(default_depth);
  60. // If -1 is specified, don't bother parsing to a usize.
  61. let infinite_depth = depth == "-1";
  62. let depth = if infinite_depth {
  63. 0_usize
  64. } else {
  65. depth
  66. .parse::<usize>()
  67. .expect("Failed to convert --depth to a usize")
  68. };
  69. for dir_root in dirs {
  70. // Create the walker for the specified directory.
  71. let walker = if infinite_depth {
  72. // If infinite depth was specified, don't call `max_depth()`, by default
  73. // WalkDir has no maximum depth.
  74. WalkDir::new(dir_root)
  75. } else {
  76. WalkDir::new(dir_root).max_depth(depth)
  77. };
  78. 'walker_loop: for dir in walker {
  79. // Get the current path and depth.
  80. let dir = dir.unwrap();
  81. let current_depth = dir.depth();
  82. // If the current path isn't a directory, skip it.
  83. let dir_path = dir.path();
  84. if !dir_path.is_dir() {
  85. continue 'walker_loop;
  86. }
  87. for target_command in target_commands.clone() {
  88. if verbose {
  89. // Print what we're doing and where we are.
  90. print!(
  91. "Executing \"{}\" in {:?} (current depth {})",
  92. target_command, dir_path, current_depth
  93. );
  94. }
  95. // Spawn the command at the directory with the specified stdout.
  96. // Print an error and continue if one occurs.
  97. let mut child = match command(target_command)
  98. .stdout(Stdio::inherit())
  99. .current_dir(dir_path)
  100. .spawn()
  101. {
  102. Ok(value) => value,
  103. Err(err) => {
  104. println!("Error executing command: {}", err);
  105. println!();
  106. continue 'walker_loop;
  107. }
  108. };
  109. // Wait for the command to finish before continuing.
  110. child.wait().expect("Failed to wait for child process");
  111. // If verbose was specified, print an extra newline to make the output more readable.
  112. if verbose {
  113. println!();
  114. }
  115. }
  116. }
  117. }
  118. }