Условно направляя вывод команды в /dev/null в bash

У меня есть следующий скрипт:

flag=false
command_name \
$(  flag == false  && printf %s '>/dev/null') 

Я не ожидаю вывода на терминале, но я все еще получаю некоторые. Если я перенаправить вывод на /dev/null на той же линии, что и command-name без этого расширения оно будет подавлено.

Команда является инструментом dx из Android SDK

Редактировать 1: Здесь код из скрипта

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')

Как я запускаю инструмент, и он работает, как ожидалось. Я не думаю, что это может быть потоком ошибок.

1 ответ

Решение

Условно-перенаправляющий стандартный вывод

Перенаправления являются синтаксисом оболочки - они должны распознаваться на этапе синтаксического анализа, который предшествует расширению параметра, поэтому вы не можете сгенерировать их с помощью расширения переменной (без совершения зла).

То, что вы можете сделать (в bash 4.1 или более поздней версии), иметь безусловное перенаправление, но есть то, что он перенаправляет для изменения:

# 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}>&-

Замечания:

  • Сравнение строк выполняется в форме [[ $var = $pattern ]] (или же [[ $var = "$string" ]] сделать точное совпадение). Смотрите вики bash-hackers по условному выражению.
  • В Bash 4.1 или более поздней версии, exec {fd_varname}>file открывает file и помещает номер дескриптора файла, указывающий на этот файл, в переменную fd_varname, exec {fd_varname}>&- закрывает файловый дескриптор, номер которого хранится в fd_varname,
  • В старых версиях bash вы все еще можете использовать эту логику, но вместо того, чтобы автоматически присваивать номер дескриптора файла, вам придется делать это вручную, вручную назначая неиспользуемый номер FD, который не равен 0, 1 или 2 (которые зарезервированы для stdin, stdout и stderr). Таким образом, в этом случае это может быть exec 3>/dev/null или же exec 3>&1 в if ветви, >&3 на dex команда и exec 3>&- закрыть это.

Безопасное создание списков аргументов условно

Смотрите BashFAQ #50 для длительного обсуждения. Короче говоря: для всего, кроме перенаправления на /dev/null есть одно простое изменение, необходимое для приведения его в соответствие с лучшими практиками: использование массива.

#!/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[@]}"
  • Смотрите BashPitfalls #1, объясняющий почему $(find ...) (лайк $(ls ...)) небезопасно, и использование Find входит в лучшие практики.
  • Смотрите BashFAQ #24, чтобы понять, почему while read ...; do ...; done < <(find ...) используется вместо find ... | while read ...; do ...; done,
Другие вопросы по тегам