bash: ใช้ตัวแปรเพื่อเก็บการเปลี่ยนเส้นทาง stderr | stdout


17

มีวิธีการเปลี่ยนเส้นทาง stdout และ stderr ผ่านตัวแปรเช่นเพิ่มตัวเลือกคำสั่งในสคริปต์ใด ๆ

ตัวอย่างเช่นฉันมีสคริปต์:

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

ฉันเห็นว่า OPT ถูกแทนที่ด้วย-pไม่มีปัญหาใด ๆ และทุบตีตีความมันเป็นตัวเลือก แต่การเปลี่ยนเส้นทางตีความเป็นชื่อไดเรกทอรี

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

มีวิธีใดที่จะบอกว่าทุบตีว่ามีการเปลี่ยนเส้นทาง $ VAR ไม่ใช่ชื่อ dirs

PS อาจเป็นฉันในทางที่ผิด แต่ฉันต้องการที่จะทำให้การส่งออก verbose เสริมหรือไม่ verbose จากสคริปต์ของฉัน แต่ฉันต้องการเอาต์พุตบางอย่างแม้ในโหมดที่ไม่มี verbose ดังนั้นฉันจึงไม่สามารถเปลี่ยนเส้นทางทั้ง stdout และ stderr ได้จากคำสั่งบางอย่างภายในสคริปต์ของฉันเท่านั้น

คำตอบ:


18

โซลูชันอื่นอาจเป็นดังต่อไปนี้:

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

จากนั้นเพิ่ม1>&3 2>&4เฉพาะคำสั่งที่คุณต้องการดูผลลัพธ์


ยิ่งใหญ่ นี่คือสิ่งที่ฉันดู ขอขอบคุณ.
เร่ง

ฉันได้เขียนบทความเพื่ออธิบายวิธีการทำงานนี้
transang

5

"วิธี" ได้รับการอธิบายอย่างดีในคำตอบอื่น ๆ ; ฉันต้องการที่อยู่ "ทำไม" รหัสของ OP ไม่ทำงาน

ประเด็นสำคัญคือการเปลี่ยนเส้นทางผลลัพธ์ถูกทำเครื่องหมายก่อนการขยายตัวของตัวแปร การเปลี่ยนเส้นทางเกิดขึ้นจริงหลังจากการขยายตัวของตัวแปร (เหตุใดคุณจึงสามารถเปลี่ยนเส้นทางเอาต์พุตไปยังชื่อไฟล์ที่เก็บไว้ในตัวแปร) แต่เชลล์จะระบุการเปลี่ยนเส้นทางสำหรับการประมวลผลในภายหลังก่อนที่จะมีการขยายตัวแปร

กล่าวอีกนัยหนึ่งเมื่อมีการขยายตัวแปรมันเป็น "สายเกินไป" สำหรับการเปลี่ยนเส้นทางอักขระ ( <หรือ>ฯลฯ ) ที่จะต้องพิจารณาเพราะเชลล์ได้ระบุว่าส่วนใดของสตริงคำสั่งที่จะจัดการเป็นการเปลี่ยนเส้นทาง

สำหรับการอ่านเพิ่มเติมดูขั้นตอนที่ 1 และ 3 ที่ระบุไว้ใน:

LESS='+/^SIMPLE COMMAND EXPANSION' man bash

3

มันไม่ได้ตีความว่ามันเป็น "ชื่อไดเรกทอรี" >กำลังถูกยกมาดังนั้นจึงได้รับการปฏิบัติอย่างแท้จริง (โดยเฉพาะอย่างยิ่งคุณกำลังส่งสตริง>dev/null 2>&1วิธีเดียวที่คุณจะหลีกเลี่ยงปัญหานี้คือการใช้evalหรือวางไข่เชลล์ใหม่

สำหรับปัญหา "verbose" ที่กล่าวถึงในคำถามของคุณให้ทำดังนี้แทน:

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

1

คุณมีช่องว่างจำนวนมากในตัวแปรของคุณซึ่งจะไม่ได้รับการประเมินอย่างเหมาะสม คุณจะต้องใช้evalเพื่อตั้งค่า

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

สิ่งนี้จะอนุญาตให้ $ OPT แบ่งออกเป็นสองอาร์กิวเมนต์ ( -pและ-v) แทนหนึ่ง ( -p -v) และเหมือนกันกับ $ TEST เปลี่ยนไปใช้/dev/nullเนื่องจากมันไม่น่าเป็นไปได้มากที่คุณจะมีdevไดเรกทอรีในไดเรกทอรีปัจจุบัน


0

คำตอบของ enzotib ใช้ magic descriptor ไฟล์ที่ดี แต่วิธีแก้ปัญหาที่ง่ายกว่าคือการใช้evalบรรทัด (ซึ่งจะเพิ่มโอเวอร์เฮดของกระบวนการ):

$ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.