Differences between revisions 10 and 11
Revision 10 as of 2017-01-05 05:31:38
Size: 5311
Editor: ormaaj
Comment: seq
Revision 11 as of 2017-01-05 09:14:31
Size: 5285
Editor: ormaaj
Comment: 3rd person
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
This page describes some common utilities that should be avoided, and what you should use instead. This page describes some common utilities that should be avoided, and what to use instead.
Line 9: Line 9:
The `pgrep` utility - part of the [[https://gitlab.com/procps-ng/procps/blob/master/README.md|procps]] suite. pgrep returns pids of processes matching the given regex, and is very preferable to grepping `ps` output. While also nonstandard, pgrep is widely available (if not installed by default) and the utilities are generally good quality. procps also includes a `pidof` implementation, so if you have a choice you should definitely install it and avoid the sysvinit version. The `pgrep` utility - part of the [[https://gitlab.com/procps-ng/procps/blob/master/README.md|procps]] suite. `pgrep` returns pids of processes matching the given regex, and is very preferable to grepping `ps` output. While also nonstandard, `pgrep` is widely available (if not installed by default) and the utilities are generally good quality. procps also includes a `pidof` implementation, so if there's a choice it's better to just install it and avoid the sysvinit version.
Line 11: Line 11:
'''Process names are NOT a reliable way to work with processes!''' Searching by name should only be done interactively in a pinch when the need to follow a process couldn't be anticipated. Never do it in scripts or programs! See: ProcessManagement. '''Process "names" are NOT a reliable way to work with processes!''' Searching by name should only be done interactively in a pinch when the need to follow a process couldn't be anticipated. Never do it in scripts or programs! See: ProcessManagement.
Line 28: Line 28:
What happens here is that `which` searches for `grep` in the `PATH` environment variable, and outputs the path to the first `grep` it finds. If it doesn't find it, it might output nothing, output an error to stdout, or output an error to stderr, and it may or may not return a non-zero exit status. If you happen to have a `which` command that outputs nothing when the command is not found, the above may result in the `GREP` variable being empty, in which case `$GREP rm logfile` expands into `rm logfile`, which does something other than search for the string "rm" in logfile. What happens here is that `which` searches for `grep` in the `PATH` environment variable, and outputs the path to the first `grep` it finds. If it doesn't find it, it might output nothing, an error to stdout, or an error to stderr. It may or may not return a non-zero exit status. If the `which` command outputs nothing when the command is not found, the above may result in the `GREP` variable being empty, in which case `$GREP rm logfile` expands into `rm logfile`, which does something other than search for the string "rm" in logfile.
Line 42: Line 42:
`xargs` is standard and moderately useful, but it has a number of unfortunate legacy warts which makes it somewhat cumbersome and sometimes outright dangerous to use. If you have a choice, consider replacing it with a more modern alternative. `xargs` is standard and moderately useful, but it has a number of unfortunate legacy warts which makes it somewhat cumbersome and sometimes outright dangerous to use. Consider replacing it with a more modern alternative.
Line 46: Line 46:
GNU xargs has a `-0` option that suppresses those misfeatures, but only for NUL-delimited input streams (e.g. those produced by GNU find with the `-print0` option). But if you're doing that, you might as well just use `find ... -exec foo {} +` and skip xargs altogether. GNU `xargs` has a `-0` option that suppresses those misfeatures, but only for NUL-delimited input streams (e.g. those produced by GNU `find` with the `-print0` option). But it's much cleaner and simpler to just use `find ... -exec foo {} +` and skip `xargs` altogether, especially if parallelism is unimportant.

This page describes some common utilities that should be avoided, and what to use instead.

pidof

pidof is a strange program. On some systems it's a symlink to the killall5 program - part of the sysvinit package. It is used by init to send signals to all processes (e.g. to kill them during shutdown). If invoked as pidof, it returns the pid of the given program. pidof Is highly nonstandard, yet seen frequently in examples likely due to its intuitive name.

Alternatives

The pgrep utility - part of the procps suite. pgrep returns pids of processes matching the given regex, and is very preferable to grepping ps output. While also nonstandard, pgrep is widely available (if not installed by default) and the utilities are generally good quality. procps also includes a pidof implementation, so if there's a choice it's better to just install it and avoid the sysvinit version.

Process "names" are NOT a reliable way to work with processes! Searching by name should only be done interactively in a pinch when the need to follow a process couldn't be anticipated. Never do it in scripts or programs! See: ProcessManagement.

killall

On some systems (Linux-based ones, usually) this may try to send a signal to a bunch of processes based on their names (a dodgy practice at best -- see above). On others (Unix System V-based ones) it will try to kill every running process regardless of name. In either case, it's nonstandard.

Alternatives

Use pkill, also from procps. But see ProcessManagement for proper ways to manage processes.

which

which is a non-standard command, though commonly available. It is useless and redundant, both in scripts and interactive shells. Some common bad usages:

# Wrong
GREP=`which grep`
...
$GREP rm logfile

What happens here is that which searches for grep in the PATH environment variable, and outputs the path to the first grep it finds. If it doesn't find it, it might output nothing, an error to stdout, or an error to stderr. It may or may not return a non-zero exit status. If the which command outputs nothing when the command is not found, the above may result in the GREP variable being empty, in which case $GREP rm logfile expands into rm logfile, which does something other than search for the string "rm" in logfile.

# Right
grep rm logfile

Guess what, bash can also search through PATH for commands, in addition to first looking for aliases, functions or builtins by that name. So the which command added nothing useful, only bugs.

The other common use of which is to check if a command exists. The shell can do this already (for example using type), and better, so why use an inferior, external command?

Alternatives

The builtins type, command and hash. See BashFAQ/081

xargs

xargs is standard and moderately useful, but it has a number of unfortunate legacy warts which makes it somewhat cumbersome and sometimes outright dangerous to use. Consider replacing it with a more modern alternative.

Specifically, xargs splits its input on any kind of whitespace, and parses quotes. A filename with spaces or quotes in its name will not be preserved and passed along correctly.

GNU xargs has a -0 option that suppresses those misfeatures, but only for NUL-delimited input streams (e.g. those produced by GNU find with the -print0 option). But it's much cleaner and simpler to just use find ... -exec foo {} + and skip xargs altogether, especially if parallelism is unimportant.

Alternatives

See UsingFind for better ways to execute a command with a whole bunch of files as arguments.

Another alternative is GNU Parallel (which ironically had some odd behaviors to explain in its earlier versions...).

expr

Pure legacy program. Has no real use in a Bash script. The Bourne shell could not do arithmetic, so you had to call this external utility to increment a loop counter. Even POSIX sh can do that much.

Alternatives

For arithmetic, use $((...)), ((...)) or let. For various string manipulations and tests, see FAQ 100 or FAQ 31.

seq

seq is non-standard and almost always used where brace expansion, shell arithmetic, or some other native shell feature would be more appropriate. Don't use seq in a command substitution to produce numbers as arguments - e.g. in a for loop. If a stream of digits produced on stdout is actually needed for some other reason then seq may be an efficient choice especially if the output will be large enough to offset the cost of forking an external utility, it's just not very portable. Don't use $(seq).

Alternatives

  • bash2.04+/zsh/ksh93: ((i = 1; i <= 10; i++)); do ...

  • bash3.0+/ksh93/zsh: for i in {1..10}; do ...

  • POSIX: i=0; while [ "$i" -lt 10 ]; do ...; i=$((i + 1)); done

See also conditional loops.

BadUtils (last edited 2022-01-11 14:21:47 by emanuele6)