I have following bash script:
flag=false
command_name \
$( flag == false && printf %s '>/dev/null')
I expect no output at Terminal but I still get some. If I redirect output to /dev/null
on the same line as command-name
without that expansion then it gets suppressed.
Command is dx tool from android SDK
Edit 1: Here the code from script
dx \
--dex \
$( ( (( flag_v == 1 )) || (( flag_v == 'd' ))) && printf %s '--verbose') \
--no-strict \
--output="../"$app_name.jar \
$(find . -type f -name '*.class') \
$( $dexflag == false && printf %s '>/dev/null')
As I run the tool and it works as expected. I do not think that it could be an error stream.
Redirections are shell syntax -- they have to be recognized at a parsing phase that comes before parameter expansion, so you can't generate them via variable expansion (without committing evil).
What you can do (in bash 4.1 or later) is have an unconditional redirection, but have the thing it redirects to change:
# Create an out_fd variable that points to stdout (FD 1) if dexflag != "false", or to a new
# handle on /dev/null otherwise
if [[ $dexflag = false ]]; then
exec {out_fd}>/dev/null # maybe put 2>&1 as well to suppress stderr
else
out_fd=1 # use FD 1 (stdout)
fi
# run dex with its stdout redirected to the FD number in "out_fd"
dex ... >&"$out_fd"
# if out_fd is not stdin/stdout/stderr, then go ahead and close it when done.
(( out_fd > 2 )) && exec {out_fd}>&-
Note:
[[ $var = $pattern ]]
(or [[ $var = "$string" ]]
to do an exact match). See the bash-hackers' wiki on the conditional expression.exec {fd_varname}>file
opens file
, and puts the file descriptor number pointing to that file in the variable fd_varname
. exec {fd_varname}>&-
closes the file descriptor whose number is stored in fd_varname
.exec 3>/dev/null
or exec 3>&1
in the if
branches, >&3
on the dex
command, and exec 3>&-
to close it.See BashFAQ #50 for a long discussion. In short, though: For everything but the redirection to /dev/null
, there's one simple change needed to bring this in line with best practices: Use an array.
#!/bin/bash
args=( )
case $flag_v in
1|d) args+=( --verbose ) ;;
esac
while IFS= read -r -d '' filename; do
args+=( "$filename" )
done < <(find . -type f -name '*.class' -print0)
dx --dex --no-strict --output="../$app_name.jar" "${args[@]}"
$(find ...)
(like $(ls ...)
) is unsafe, and Using Find going into best practices.while read ...; do ...; done < <(find ...)
is used instead of find ... | while read ...; do ...; done
.