Johan
Johan

Reputation: 40520

Add command arguments using inline if-statement in bash

I'd like to add an argument to a command in bash only if a variable evaluates to a certain value. For example this works:

test=1
if [ "${test}" == 1 ]; then
    ls -la -R
else
    ls -R   
fi

The problem with this approach is that I have to duplicate ls -R both when test is 1 or if it's something else. I'd prefer if I could write this in one line instead such as this (pseudo code that doesn't work):

ls (if ${test} == 1 then -la) -R

I've tried the following but it doesn't work:

test=1
ls `if [ $test -eq 1 ]; then -la; fi` -R

This gives me the following error:

./test.sh: line 3: -la: command not found

Upvotes: 51

Views: 27620

Answers (5)

Vetsin
Vetsin

Reputation: 2592

I can't say how acceptable this is, but:

test=1
ls ${test:+'-la'} -R

See https://stackoverflow.com/revisions/16753536/1 for a conditional truth table.

To be clear, it is checking for any value (0 will be truthy, so you must set test= to have the negative case.

Upvotes: 6

fozzybear
fozzybear

Reputation: 157

The following example is a supplemental variant of the solution from Vetsin, which achieves a conditional if-else argument substitution.

By default, the parameter from var _condArg2 ('-la') is applied to the ls command in the function ls_conditional_subst_arg(). If a directory argument is provided, instead the parameter '-d' is used, together with the function's argument.

#!/bin/bash

ls_conditional_subst_arg() {
    local _arg1 _condArg2='-la'
    [[ $1 ]] && _arg1="-d $1"
    ls ${_arg1:=$_condArg2} >/dev/tty
}

Try:

ls_conditional_subst_arg 

ls_conditional_subst_arg ~/.config

Upvotes: 0

mklement0
mklement0

Reputation: 438083

A more idiomatic version of svlasov's answer:

ls $( (( test == 1 )) && printf %s '-la' ) -R

Since echo understands a few options itself, it's safer to use printf %s to make sure that the text to print is not mistaken for an option.
Note that the command substitution must not be quoted here - which is fine in the case at hand, but calls for a more robust approach in general - see below.

However, in general, the more robust approach is to build up arguments in an array and pass it as a whole:

# Build up array of arguments...
args=()
(( test == 1 )) && args+=( '-la' )
args+=( '-R' )

# ... and pass it to `ls`.
ls "${args[@]}"

Update: The OP asks how to conditionally add an additional, variable-based argument to yield ls -R -la "$PWD". In that case, the array approach is a must: each argument must become its own array element, which is crucial for supporting arguments that may have embedded whitespace:

(( test == 1 )) && args+= ( '-la' "$PWD" ) # Add each argument as its own array element.

As for why your command,

 ls `if [ $test -eq 1 ]; then -la; fi` -R

didn't work:

A command between backticks (or its modern, nestable equivalent, $(...)) - a so-called command substitution - is executed just like any other shell command (albeit in a sub-shell) and the whole construct is replaced with the command's stdout output.

Thus, your command tries to execute the string -la, which fails. To send it to stdout, as is needed here, you must use a command such as echo or printf.

Upvotes: 67

anubhava
anubhava

Reputation: 785246

Another answer without using eval and using BASH arrays:

myls() { local arr=(ls); [[ $1 -eq 1 ]] && arr+=(-la); arr+=(-R); "${arr[@]}"; }

Use it as:

myls
myls "$test"

This script builds whole command in an array arr and preserves the original order of command options.

Upvotes: 4

svlasov
svlasov

Reputation: 10455

Print the argument with echo:

test=1
ls `if [ $test -eq 1 ]; then echo "-la"; fi` -R

Upvotes: 9

Related Questions