bash: Gunakan variabel untuk menyimpan pengalihan stderr | stdout

17

Apakah ada cara untuk mengarahkan stdout dan stderr melalui variabel seperti menambahkan opsi perintah dalam skrip?

Misalnya saya punya skrip:

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

Saya dapat melihat bahwa OPT diganti dengan -ptanpa masalah dan bash mengartikannya sebagai opsi. Tapi redirection diartikan sebagai nama direktori.

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

Apakah ada cara untuk mengatakan bash, bahwa $ VAR adalah pengalihan, bukan nama dir.

PS. Mungkin saya salah jalan, tapi saya ingin membuat opsional verbose atau non verbose dari skrip saya. Tetapi saya memerlukan beberapa keluaran bahkan dalam mode non-verbose, oleh karena itu saya tidak bisa hanya mengarahkan seluruh stdout dan stderr, hanya dari beberapa perintah di dalam skrip saya.

buru-buru
sumber

Jawaban:

18

Solusi lain bisa sebagai berikut:

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

Kemudian tambahkan 1>&3 2>&4hanya ke perintah yang ingin Anda lihat hasilnya.

enzotib
sumber
Bagus. Ini persis hal yang saya tonton. Terima kasih.
buru
Saya telah menulis sebuah artikel untuk menjelaskan bagaimana ini bekerja
transang
5

"Bagaimana" telah dijelaskan dengan baik dalam jawaban lain; Saya ingin membahas "mengapa" kode OP tidak berfungsi.

Intinya adalah bahwa redirection output ditandai sebelum ekspansi variabel. Pengalihan sebenarnya dilakukan setelah ekspansi variabel (karenanya mengapa Anda dapat mengarahkan output ke nama file yang disimpan dalam variabel), tetapi shell mengidentifikasi pengalihan untuk diproses nanti sebelum variabel diperluas.

Dengan kata lain, begitu variabel diperluas, "terlambat" untuk mempertimbangkan karakter pengalihan ( <atau >, dll.), Karena shell telah mengidentifikasi bagian mana dari string perintah yang akan ditangani sebagai pengalihan.

Untuk bacaan lebih lanjut, lihat langkah 1 dan 3 yang tercantum di bawah:

LESS='+/^SIMPLE COMMAND EXPANSION' man bash
Wildcard
sumber
3

Itu tidak menafsirkannya sebagai "nama direktori", >sedang dikutip, jadi itu diperlakukan secara harfiah (lebih khusus, Anda mengirim string >dev/null 2>&1. Satu-satunya cara Anda untuk mengatasinya adalah menggunakan evalatau menelurkan shell baru.

Adapun masalah "verbose" Anda disinggung dalam pertanyaan Anda, lakukan saja ini:

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

Anda memiliki banyak ruang dalam variabel Anda, yang tidak akan dievaluasi dengan benar. Anda ingin menggunakannya evaluntuk mengaturnya.

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

Ini akan memungkinkan $ OPT dipecah menjadi dua argumen ( -pdan -v) alih-alih satu ( -p -v) dan sama dengan $ TEST. Juga diubah untuk digunakan /dev/nullkarena sangat tidak mungkin bahwa Anda akan memiliki devdirektori di direktori saat ini.

Arcege
sumber
0

Jawaban enzotib ini menggunakan beberapa file yang baik descriptor sihir, tetapi solusi sederhana akan hanya menggunakan evalgaris (yang tidak proses add di atas kepala, namun):

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