bash: użyj zmiennej do przechowywania przekierowania stderr | stdout

17

Czy jest jakiś sposób na przekierowanie stdout i stderr przez zmienną, na przykład poprzez dodanie opcji poleceń w skrypcie?

Na przykład mam skrypt:

#!/bin/bash -x
TEST=">/dev/null 2>&1"
OPT='-p -v'
mkdir $OPT 123/123/123 $TEST

Widzę, że OPT został zastąpiony -pbez żadnych problemów i bash interpretuje to jako opcję. Ale przekierowanie interpretuje jako nazwę katalogów.

$ ./test.sh 
+ TEST='>/dev/null 2>&1'
+ OPT='-p -v'
+ mkdir -p -v 123/123/123 '>/dev/null' '2>&1'
mkdir: created directory `123/123'
mkdir: created directory `123/123/123'
mkdir: created directory `>/dev'
mkdir: created directory `>/dev/null'
mkdir: created directory `2>&1'

Czy jest jakikolwiek sposób powiedzieć bash, że $ VAR to przekierowanie, a nie nazwy katalogów.

PS. Być może jestem w złym kierunku, ale chcę tworzyć opcjonalne pełne lub nieokreślone dane wyjściowe z mojego skryptu. Ale potrzebuję trochę danych wyjściowych nawet w trybie nieokreślonym, dlatego nie mogę po prostu przekierować całego stdout i stderr, tylko z niektórych poleceń w moim skrypcie.

wysypka
źródło

Odpowiedzi:

18

Innym rozwiązaniem może być:

#!/bin/bash

verbose=0

exec 3>&1
exec 4>&2

if ((verbose)); then
  echo "verbose=1"
else
  echo "verbose=0"
  exec 1>/dev/null
  exec 2>/dev/null
fi

echo "this should be seen if verbose"
echo "this should always be seen" 1>&3 2>&4

Następnie dodaj 1>&3 2>&4tylko do poleceń, których wynik chcesz wyświetlić.

enzotib
źródło
Świetny. Właśnie tego szukałem. Dziękuję Ci.
spieszy się
Napisałem artykuł wyjaśniający, jak to działa
przetłumacz
5

„Jak” zostało dobrze wyjaśnione w innych odpowiedziach; Chcę odpowiedzieć na pytanie „dlaczego” kod OP nie działa.

Myślą przewodnią jest to, że przekierowania wyjściowe są zaznaczane przed rozwinięciem zmiennej. Przekierowania są faktycznie wykonywane po rozwinięciu zmiennej (dlatego dlaczego można przekierować wyjście do nazwy pliku, która jest przechowywana w zmiennej), ale powłoka identyfikuje przekierowania do późniejszego przetworzenia przed rozwinięciem zmiennych.

Innymi słowy, po rozwinięciu zmiennych jest „za późno” na rozważenie znaku przekierowania ( <lub >itp.), Ponieważ powłoka już określiła, które części łańcucha poleceń będą obsługiwać jako przekierowania.

Więcej informacji można znaleźć w krokach 1 i 3 wymienionych w:

LESS='+/^SIMPLE COMMAND EXPANSION' man bash
Dzika karta
źródło
3

Nie jest interpretowany jako „nazwa katalogu”, >jest cytowany, więc jest traktowany dosłownie (dokładniej, wysyłasz ciąg >dev/null 2>&1. Jedynym sposobem na obejście tego jest użycie evallub odrodzenie nowej powłoki.

Jeśli chodzi o kwestię „pełnego”, o której mowa w pytaniu, po prostu zrób to zamiast tego:

verbose=1
if (( verbose )); then
    mkdir -v -p /foo
else
    mkdir -p /foo > /dev/null 2>&1
fi
Chris Down
źródło
1

W twoich zmiennych jest dużo spacji, które nie zostaną właściwie ocenione. Będziesz chciał użyć tego evaldo skonfigurowania.

#!/bin/bash -x
TEST=">/dev/null 2>&1"
OPT='-p -v'
eval mkdir $OPT 123/123/123 $TEST

Umożliwi to podzielenie $ OPT na dwa argumenty ( -pi -v) zamiast jednego ( -p -v) i to samo z $ TEST. Zmieniono również użycie, /dev/nullponieważ jest bardzo mało prawdopodobne, że będziesz mieć devkatalog w bieżącym katalogu.

Arcege
źródło
0

Odpowiedź enzotib używa dobrej magii deskryptora pliku, ale prostszym rozwiązaniem byłoby po prostu użycie evalwiersza (co jednak powoduje dodatkowe obciążenie procesu):

$ rm /tmp/foo
$ ll /tmp/foo
ls: cannot access /tmp/foo: No such file or directory
$ FOO=">/tmp/foo"
$ date $FOO
date: invalid date '>/tmp/foo'
$ cat /tmp/foo
cat: /tmp/foo: No such file or directory
$ eval date $FOO
$ cat /tmp/foo
Thu Dec 13 14:08:17 EST 2018
Joe Casadonte
źródło