Bash: Verwenden Sie eine Variable, um die Umleitung stderr | stdout zu speichern

17

Gibt es eine Möglichkeit, stdout und stderr über Variablen umzuleiten, wie das Hinzufügen von Befehlsoptionen im Skript?

Zum Beispiel habe ich ein Skript:

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

Ich kann sehen, dass OPT -pohne Probleme durch ersetzt wird und Bash interpretiert es als Option. Die Umleitung wird jedoch als Verzeichnisname interpretiert.

$ ./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'

Gibt es eine Möglichkeit, bash zu sagen, dass $ VAR eine Umleitung ist, keine Verzeichnisnamen.

PS. Vielleicht bin ich auf dem falschen Weg, aber ich möchte eine optionale ausführliche oder nicht ausführliche Ausgabe von meinem Skript machen. Aber ich brauche eine Ausgabe auch im nicht-ausführlichen Modus, daher kann ich nicht nur ganze stdout- und stderr-Befehle umleiten, sondern nur einige Befehle innerhalb meines Skripts.

eilen
quelle

Antworten:

18

Eine andere Lösung könnte sein:

#!/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

Fügen Sie dann 1>&3 2>&4nur Befehle hinzu, deren Ausgabe Sie sehen möchten.

Enzotib
quelle
Groß. Genau darauf habe ich geachtet. Vielen Dank.
Ansturm
Ich habe einen Artikel geschrieben , um zu erklären, wie das funktioniert
17.
5

Das "Wie" ist in anderen Antworten gut erklärt worden; Ich möchte das "Warum" ansprechen, dass der OP-Code nicht funktioniert.

Das Wichtigste ist, dass Ausgabeumleitungen vor der Variablenerweiterung markiert werden. Umleitungen sind tatsächlich durchgeführt , nachdem variable Expansion (daher , warum Sie die Ausgabe auf einen Dateinamen umleiten können , die in einer Variablen gespeichert ist), aber die Schale identifiziert die Umleitungen für die spätere Verarbeitung vor Variablen erweitert.

Mit anderen Worten, sobald Variablen erweitert sind, ist es "zu spät", ein Umleitungszeichen ( <oder >usw.) zu berücksichtigen, da die Shell bereits identifiziert hat, welche Teile der Befehlszeichenfolge als Umleitungen behandelt werden sollen.

Weitere Informationen finden Sie in den Schritten 1 und 3 unter:

LESS='+/^SIMPLE COMMAND EXPANSION' man bash
Platzhalter
quelle
3

Es wird nicht als "Verzeichnisname" interpretiert, sondern >in Anführungszeichen gesetzt, sodass es wörtlich behandelt wird (genauer gesagt, Sie senden die Zeichenfolge >dev/null 2>&1. Sie können dies nur umgehen, indem Sie evaleine neue Shell verwenden oder erstellen.

Gehen Sie stattdessen wie folgt vor, um Ihr in Ihrer Frage angesprochenes "ausführliches" Problem zu lösen:

verbose=1
if (( verbose )); then
    mkdir -v -p /foo
else
    mkdir -p /foo > /dev/null 2>&1
fi
Chris Down
quelle
1

Sie haben viele Leerzeichen in Ihren Variablen, die nicht richtig ausgewertet werden. Sie werden es verwenden wollen, um evaldas einzurichten.

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

Auf diese Weise kann $ OPT mit $ TEST in zwei Argumente ( -pund -v) anstelle von einem ( -p -v) und demselben Argument aufgeteilt werden . Auch zur Verwendung geändert, /dev/nullda es sehr unwahrscheinlich ist, dass Sie ein devVerzeichnis im aktuellen Verzeichnis haben.

Arcege
quelle
0

Die Antwort von enzotib verwendet eine gute Dateideskriptormagie, aber eine einfachere Lösung wäre, nur evaldie Zeile zu verwenden (was jedoch den Prozessaufwand erhöht):

$ 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
quelle