2

From this question: Will ls always list the files that rm will remove? @fkraiem commented and said:

I am somewhat surprised that rm does not have a --dry-run flag...

This reminded me of a scenario I had sometime back using the seq command to generate a sequence of numbers from 1 - 100. The problem was that the list had a prefix Item

seq 100

Generated:

1
2
3
...
100

Whereas I wanted:

Item 1
Item 2
Item 3
...
Item 100

Using the seq command as an example, is there anyway I can add a --prefix=[PREFIX] flag that automatically adds a prefix to the generated sequence list?

I am not talking about bash aliases

Parto
  • 15,325
  • 24
  • 86
  • 117
  • 2
    Generally, you don't do this for compiled commands. And in cases where you have source code or it's a script, there's two problems: one, this might be against licensing terms; two, in the environment where you have others using same command, you're adding a non-standard item and this may be messing up their workflow. If there's also commands depending on it, it may mess up portability. Hence, it's best to define a function with same name for your environment or add a script/function with different name. – Sergiy Kolodyazhnyy Sep 06 '18 at 00:45
  • @SergiyKolodyazhnyy I agree with your comment. This is more in the line of what the question is really asking. Kindly post the same as an answer below. – Parto Sep 06 '18 at 09:18
  • Do you have an example where adding an option would be worth the effort? – dessert Sep 06 '18 at 09:39
  • @dessert The example of being worth the effort would be convenience to yourself, or you're developing for FreeBSD on Ubuntu, and the command is missing a flag in FreeBSD. In fact a good example is find, the FreeBSD version doesn't have some flags the GNU one has – Sergiy Kolodyazhnyy Sep 09 '18 at 18:44

2 Answers2

7

In general, short of modifying the command's code (and recompiling, in the case of a compiled executable) you would need to write a wrapper function - either as a shell function or an executable script located ahead of the original executable in your PATH. It would need to:

  • parse the command line arguments
  • intercept your additional option (and any option-specific argument)
  • perform the new action
  • otherwise pass the remaining arguments to the original command

However your seq should already have an option to specify the number format, which may be (ab)used to get the kind of list you want

   -f, --format=FORMAT
          use printf style floating-point FORMAT

Ex.

$ seq -f 'Item %.0f' 1 10
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10

More generally, you could use printf e.g. printf 'Item %d\n' $(seq 1 100) or (using bash's built-in brace expansion) printf 'Item %d\n' {1..100}


BTW rm does have a sort of dry-run flag, -i

-i     prompt before every removal

or (less portably)

-I     prompt once before removing  more  than  three  files,  or  when
      removing recursively; less intrusive than -i, while still giving
      protection against most mistakes
steeldriver
  • 136,215
  • 21
  • 243
  • 336
  • I appreciate the response and examples using already inbuilt flags for the seq command though this is not really answering the question on integrating custom flags to commands. +1 though. – Parto Sep 06 '18 at 09:21
4

Adding flags and existing commands

Commands that exist as already compiled binary files cannot be modified, so adding flags is impossible. Of course, one could attempt to modifying the binary data of the executable itself, but that's obscenely difficult and impractical. More practical would be to obtain source code, modify the source, and recompile the command. In case the command is a script (Perl, Python, shell,etc), you can modify it directly with the root privileges. However, all these methods have issues in common:

  1. Modifying a command might be against licensing terms. Very often readers of the answers on this site assume everything is open source and libre under Ubuntu, but proprietary software is also used. Of course, you can modify any software - that's not the question of ability or coding competence. Very often the question is whether you may modify it.
  2. Portability, in the sense that this flag works on this machine where you modified it but not another. Tangentially, documentation. Imagine you wrote a script which uses the modified version of seq, but whatever flag you added is not in the man page. Your coworker or another person who will be reading this script will be very confused as to why that flag isn't in the manual, and maybe even more confused as to why the flag doesn't work; even more fun is when you have whole set of utilities that may depend on that particular command with that particular flag. This may make your coworkers very unhappy. And if you redistribute (that is give them) the copy of your modified command, now you have licensing problems (IANAL, but this can lead to lawsuits).
  3. Going back to the topic of coworkers, modifying a command that should be standard may break their workflow and mess with the development environment.

Better solution

General idea for when you want to have a custom flag is to have a wrapper around the original command. That can be function in your ~/.bashrc, a compiled program, a script - whatever. The key point is that it should be used only within your environment and you should be aware of when you're writing commands for yourself and when you're writing for commands to be used by others.

So here's some options/suggestions:

  1. Define function within your ~/.bashrc. Functions take precedence over commands that are located in any directory in your PATH variable.
  2. Naming should be unambiguous. For instance, naming your own seq the same would be ambiguous since functionality of two will differ. Better way would be to have my_seq or seq_f for function. Scripts typically have .sh extension, so that should help with ambiguity.
  3. If you're modifying a compiled binary, let it live in your ~/bin and maybe rename it.
  4. Consider using ~/bin/mycommand instead of just mycommand. As said in the presentation by Hendrik Jan Thomassen, original Unix commands were short because the keys on old PDP-11 terminals were hard to press. We don't have to worry about that nowadays, plus if you're putting that command in a script this means you won't have to retype it.

About rm --dry-run

GNU Coreutils are well known for rejecting features that already have certain similar implementations or can be achieved via combining with other utilities; for instance in df the --no-header flag is rejected because it can be done with sed or other utility that can remove first line of text. And this makes sense.

As far as rm goes, there already exists -i flag for interactive, so it should prompt the user. Another frequent technique is to use echo instead of any command for dry run. Thus the --dry-run flag could be implemented via:

# Prompts for y/n answer every time
rm -i ./*

# Automates answering "n"
for f in ./*; do rm -i "$f" <<< "n"; done

# or for non-bash shells:
for f in ./*; do echo "n" | rm -i "$f"; done

# prints the command that would be executed
echo rm "$file"

All of these approaches can be combined with what I mentioned before - wrap a custom function or script around the original command. Something like:

saferm(){
    case "$@" in
        *--dry-run*) echo rm "$@";;
        *) rm "$@";;
}

See also

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497