echo ที่ส่งออกไปยัง stderr


1115

มีเครื่องมือทุบตีมาตรฐานที่ทำหน้าที่เหมือนเสียงสะท้อน แต่ส่งออกไปยัง stderr แทนที่จะเป็น stdout หรือไม่?

ฉันรู้ว่าฉันสามารถทำได้echo foo 1>&2แต่มันก็น่าเกลียดและฉันสงสัยว่ามีแนวโน้มผิดพลาด (เช่นมีแนวโน้มที่จะได้รับการแก้ไขผิดเมื่อสิ่งต่าง ๆ เปลี่ยนแปลง)


ที่เกี่ยวข้อง: stackoverflow.com/a/12704488/194894
Flow

คำตอบ:


1453

คุณสามารถทำสิ่งนี้ซึ่งอำนวยความสะดวกในการอ่าน:

>&2 echo "error"

>&2คัดลอกไฟล์ descriptor # 2 ไปยัง file descriptor # 1 ดังนั้นหลังจากดำเนินการเปลี่ยนเส้นทางนี้ตัวอธิบายไฟล์ทั้งสองจะอ้างถึงไฟล์เดียวกัน: ตัวอธิบายไฟล์ # 2 หนึ่งตัวเดิมอ้างถึง สำหรับข้อมูลเพิ่มเติมโปรดดูที่แฮกเกอร์สอนทุบตีภาพประกอบการเปลี่ยนเส้นทาง


2
ฉันเรียนรู้เคล็ดลับนี้เมื่อไม่นานมานี้ หน้านี้มีข้อมูลที่ดีบางอย่าง tldp.org/LDP/abs/html/io-redirection.html
Marco Aurelio

46
@BCS ฉันไม่รู้เกี่ยวกับการใช้aliasในเชลล์สคริปต์ มันอาจจะปลอดภัยกว่าในการใช้errcho(){ >&2 echo $@; }
Braden Best

3
> & 2 ปกติจะถูกวางไว้ที่ท้าย นี้จะใช้งานได้ แต่จะใช้น้อยกว่า
Iskren Ivov Chernev

159
ในเกือบ 40 ปีที่ฉันใช้ระบบยูนิกซ์มันไม่เคยเกิดขึ้นกับฉันเลยว่าคุณสามารถเปลี่ยนเส้นทางได้ทุกที่ แต่ในตอนท้าย การวางไว้ด้านหน้าแบบนี้จะทำให้ชัดเจนยิ่งขึ้น (หรือ "อำนวยความสะดวกในการอ่าน" ตามที่ @MarcoAurelio พูด) +1 สำหรับสอนสิ่งใหม่ให้ฉัน
Hephaestus

FYI: ถ้าคุณต้องการจัดรูปแบบหรือทำอะไรนอกจากเพียงแค่สะท้อนสตริงคุณจะต้องย้ายการเปลี่ยนเส้นทางกลับไปยังจุดสิ้นสุด เช่นerrcho(){ >&2 echo $@|pr -To5;}จะไม่ทำงาน ในการทำบางสิ่งเช่นนี้คุณจะต้องทำการเปลี่ยนเส้นทางที่ไหนสักแห่งหลังจากท่อสุดท้ายเช่น:errcho(){ echo $@|>&2 pr -To5;}
Jon Red

423

คุณสามารถกำหนดฟังก์ชั่น:

echoerr() { echo "$@" 1>&2; }
echoerr hello world

นี่จะเร็วกว่าสคริปต์และไม่มีการอ้างอิง

คำแนะนำเฉพาะของ bash Camilo Martin ใช้ "here string" และจะพิมพ์สิ่งที่คุณส่งไปให้รวมถึงอาร์กิวเมนต์ (-n) ที่เสียงก้องปกติจะกลืน:

echoerr() { cat <<< "$@" 1>&2; }

วิธีการแก้ปัญหาของ Glenn Jackman ยังช่วยหลีกเลี่ยงปัญหาการกลืนอาร์กิวเมนต์:

echoerr() { printf "%s\n" "$*" >&2; }

7
ฉันต้องบอกว่าเสียงสะท้อนนั้นไม่น่าเชื่อถือ echoerr -ne xtจะไม่พิมพ์ "-ne xt" ใช้ดีกว่าprintfสำหรับสิ่งนั้น
Camilo Martin

10
โอ้คุณสามารถใช้แมวได้เช่นกัน:echoerr() { cat <<< "$@" 1>&2; }
Camilo Martin

2
ฉันไม่ทราบว่า ที่เพิ่ม
James Roth

4
หรือ,printf "%s\n" "$*" >&2
เกล็นแจ็คแมน

4
@GKFX แน่นอนมันทำงานได้อย่างถูกต้องเมื่อมีการอ้างอิง ทำไมผู้คนถึงไม่พูดถึงสายของพวกเขานั้นเกินกว่าฉัน (เมื่อคุณไม่อ้างอิงทุกอย่างที่คั่นด้วย$IFSช่องว่างอย่างน้อยหนึ่งช่องจะถูกส่งเป็นอาร์กิวเมนต์แยกซึ่งในกรณีที่echoหมายถึงการต่อเชื่อมกับ0x20s แต่อันตรายที่ไม่ได้กล่าวถึงความสะดวกสบายของตัวอักษรน้อยกว่า 2 ตัว) .
Camilo Martin

252

เนื่องจาก1เป็นเอาต์พุตมาตรฐานคุณไม่จำเป็นต้องตั้งชื่อไว้ด้านหน้าการเปลี่ยนเส้นทางเอาต์พุตอย่างชัดเจน>แต่สามารถพิมพ์เพียง:

echo ข้อความนี้ไปที่ stderr> & 2

เนื่องจากคุณดูเหมือนเป็นห่วงว่า1>&2จะเป็นเรื่องยากสำหรับคุณที่จะพิมพ์ได้อย่างน่าเชื่อถือการกำจัดของซ้ำซ้อน1อาจเป็นกำลังใจเล็กน้อยสำหรับคุณ!


59

ตัวเลือกอื่น

echo foo >>/dev/stderr

4
ตัวเลือกนี้พกพาได้หรือไม่? มีใครรู้บ้างไหมว่าสิ่งนี้ไม่ได้ผลกับรสชาติของยูนิกซ์บ้างไหม?
Dacav

7
มันไม่ทำงานในบาง chroots ซึ่งไม่สามารถเข้าถึง / dev / stderr
Zachary Vance

12
หากสคริปต์ที่รันบรรทัดนี้ - ลองเรียกมันว่าfoo- เปลี่ยน stderr ของตัวเอง - เช่นfoo >foo.log 2>&1- จากนั้นecho foo >/dev/stderrจะปิดกั้นเอาต์พุตทั้งหมดก่อนหน้า >>ควรใช้แทน:echo foo >>/dev/stderr
doshea

/dev/fd/2ในทำนองเดียวกันคุณมี
jbruni

@Dacav /proc/self/fd/2นี้คือการตรวจสอบแบบพกพา: ดูคำตอบของฉันด้านล่าง :)
เซบาสเตียน

31

ไม่นั่นเป็นวิธีมาตรฐานในการทำ ไม่ควรทำให้เกิดข้อผิดพลาด


9
มันไม่ควรทำให้เกิดข้อผิดพลาด แต่ผมอาจจะมีแนวโน้มที่จะ OTOH มันไม่ได้เป็นเรื่องใหญ่
BCS

6
@ ไมค์ DeSimone: ถ้าคนอื่นยุ่งกับรหัสฟืรอบการส่งออกและไม่ได้รู้จริงทุบตีพวกเขาได้อย่างง่ายดายวาง (หรือพิมพ์ผิด) 1>&2เราทุกคนหวังว่าสิ่งนี้จะไม่เกิดขึ้น แต่ฉันแน่ใจว่าเราทุกคนล้วนเป็นสถานที่ที่เหมาะสม
Cascabel

2
( echo something 1>&2 ; something else ) > log-> (echo something; cp some junk 1>&2 ; something else) > logโอ๊ะโอ
BCS

29
IMHO หากใครบางคนยุ่งกับรหัสและไม่รู้ทุบตีนี่อาจเป็นปัญหาของคุณ
Mike DeSimone

7
ฉันคิดว่าถ้ามันน่าจะมีปัญหาคุณควรเริ่มใช้ภาษาอื่น: การพยายามทำให้ความผิดพลาดที่ไม่ถูกต้องนั้นเป็นเรื่องของคนโง่
สัญชาตญาณ

17

หากคุณไม่สนใจการบันทึกข้อความไปยัง syslog ด้วยวิธี not_so_ugly คือ:

logger -s $msg

ตัวเลือก -s หมายถึง: "ส่งข้อความออกไปยังข้อผิดพลาดมาตรฐานเช่นเดียวกับบันทึกของระบบ"


1
นี่มันเยี่ยมมาก! มันพกพาได้อย่างไร?
code_monk

@code_monk: คำสั่ง logger คาดว่าจะเข้ากันได้กับ IEEE Std 1003.2 ("POSIX.2") คำสั่ง logger เป็นส่วนหนึ่งของแพ็กเกจ util-linux และหาได้จาก Linux Kernel Archive ⟨ kernel.org/pub/linux/utils / util-linux
miku

12

นี่เป็นฟังก์ชั่นพื้นฐานที่เรียบง่ายซึ่งจะเปลี่ยนทิศทางอินพุตไพพ์ไปยัง STDERR

#!/bin/bash
# *************************************************************
# This function redirect the pipe input to STDERR.
#
# @param stream
# @return string
#
function STDERR () {

cat - 1>&2

}

# remove the directory /bubu
if rm /bubu 2>/dev/null; then
    echo "Bubu is gone."
else
    echo "Has anyone seen Bubu?" | STDERR
fi


# run the bubu.sh and redirect you output
tux@earth:~$ ./bubu.sh >/tmp/bubu.log 2>/tmp/bubu.err

2
ฉันคิดว่าคุณสามารถทำสิ่งเดียวกันได้ด้วยนามแฝงและกระชับยิ่งขึ้น
BCS

หรือคุณเพียงแค่echo what | /dev/stderr
ไพพ์

11

หมายเหตุ: ฉันกำลังตอบคำถามไม่สะท้อน / ทำให้เข้าใจผิด "echo ที่ส่งออกไปยังคำถาม stderr" (ตอบโดย OP แล้ว)

ใช้ฟังก์ชั่นเพื่อแสดงความตั้งใจและแหล่งที่มาของการใช้งานที่คุณต้องการ เช่น

#!/bin/bash

[ -x error_handling ] && . error_handling

filename="foobar.txt"
config_error $filename "invalid value!"

output_xml_error "No such account"

debug_output "Skipping cache"

log_error "Timeout downloading archive"

notify_admin "Out of disk space!"

fatal "failed to open logger!"

และerror_handlingเป็น:

ADMIN_EMAIL=root@localhost

config_error() { filename="$1"; shift; echo "Config error in $filename: $*" 2>&1; }

output_xml_error() { echo "<error>$*</error>" 2>&1; }

debug_output() { [ "$DEBUG"=="1" ] && echo "DEBUG: $*"; }

log_error() { logger -s "$*"; }

fatal() { which logger >/dev/null && logger -s "FATAL: $*" || echo "FATAL: $*"; exit 100; }

notify_admin() { echo "$*" | mail -s "Error from script" "$ADMIN_EMAIL"; }

เหตุผลที่จัดการกับข้อกังวลใน OP:

  • ไวยากรณ์ที่ดีที่สุดที่เป็นไปได้ (คำที่มีความหมายแทนสัญลักษณ์ที่น่าเกลียด)
  • ทำให้เกิดข้อผิดพลาดได้ยากขึ้น (โดยเฉพาะถ้าคุณใช้สคริปต์ซ้ำ)
  • ไม่ใช่เครื่องมือทุบตีมาตรฐาน แต่สามารถเป็นเชลล์ไลบรารีมาตรฐานสำหรับคุณหรือ บริษัท / องค์กรของคุณ

เหตุผลอื่น ๆ :

  • ชัดเจน - แสดงความตั้งใจต่อผู้ดูแลคนอื่น
  • ความเร็ว - ฟังก์ชั่นนั้นเร็วกว่าเชลล์สคริปต์
  • reusability - ฟังก์ชั่นสามารถเรียกใช้ฟังก์ชั่นอื่น
  • configurability - ไม่จำเป็นต้องแก้ไขสคริปต์ต้นฉบับ
  • การดีบัก - ง่ายต่อการค้นหาบรรทัดที่รับผิดชอบต่อข้อผิดพลาด (โดยเฉพาะอย่างยิ่งถ้าคุณกำลังหยุดชะงักกับตันของการเปลี่ยนเส้นทาง / กรองผลลัพธ์)
  • ความทนทาน - หากฟังก์ชั่นขาดหายไปและคุณไม่สามารถแก้ไขสคริปต์คุณสามารถถอยกลับไปใช้เครื่องมือภายนอกที่มีชื่อเดียวกัน (เช่น log_error สามารถใช้นามแฝงเป็นคนตัดไม้บน Linux)
  • การเปลี่ยนการใช้งาน - คุณสามารถสลับไปยังเครื่องมือภายนอกได้โดยลบแอตทริบิวต์ "x" ของไลบรารี
  • ผู้ไม่เชื่อเรื่องพระเจ้าเอาท์พุท - คุณไม่ต้องสนใจว่ามันจะไปที่ STDERR หรือที่อื่น ๆ
  • การปรับให้เป็นแบบส่วนตัว - คุณสามารถกำหนดค่าพฤติกรรมด้วยตัวแปรสภาพแวดล้อม

10

คำแนะนำของฉัน:

echo "my errz" >> /proc/self/fd/2

หรือ

echo "my errz" >> /dev/stderr

echo "my errz" > /proc/self/fd/2จะมีประสิทธิภาพในการส่งออกไปstderrเพราะ/proc/selfคือการเชื่อมโยงกับกระบวนการในปัจจุบันและ/proc/self/fdถือเป็นขั้นตอนการเปิดอธิบายไฟล์แล้ว0, 1และ2ยืนstdin, stdoutและstderrตามลำดับ

/proc/selfลิงค์ไม่ทำงานบน MacOS แต่/proc/self/fd/*มีอยู่ใน Termux บน Android /dev/stderrแต่ไม่ วิธีการตรวจสอบระบบปฏิบัติการจากสคริปต์ทุบตี? สามารถช่วยได้หากคุณต้องการทำให้สคริปต์ของคุณพกพาได้มากขึ้นโดยกำหนดตัวแปรที่จะใช้


5
/proc/selfลิงค์ไม่ทำงานบน MacOS ดังนั้นฉันจะยึดติดอยู่กับที่มากกว่าที่ตรงไปข้างหน้า/dev/stderrวิธีการ นอกจากนี้ตามที่ระบุไว้ในคำตอบ / ความคิดเห็นอื่น ๆ มันอาจจะดีกว่าที่จะใช้>>ผนวก
MarkHu

4
/proc/self/fd/*ที่มีอยู่ใน Termux บน Android /dev/stderrแต่ไม่
go2null

9

อย่าใช้catตามที่บางคนกล่าวถึงที่นี่ catเป็นโปรแกรม ในขณะที่echoและprintfเป็น bash (เชลล์) ในตัว การเปิดตัวโปรแกรมหรือสคริปต์อื่น ๆ (ดังกล่าวข้างต้น) หมายถึงสร้างกระบวนการใหม่ด้วยค่าใช้จ่ายทั้งหมด การใช้ builtins ฟังก์ชั่นการเขียนค่อนข้างถูกเพราะไม่จำเป็นต้องสร้าง (ดำเนินการ) กระบวนการ (- สภาพแวดล้อม)

opner ถามว่า "มีเครื่องมือมาตรฐานในการส่งออก ( ไปป์ ) ถึง stderr" หรือไม่คำตอบที่ได้คือ: ไม่ ... ทำไม? ... rediredcting pipes เป็นแนวคิดที่ยอดเยี่ยมในระบบเช่น unix (Linux ... ) และ bash (sh) สร้างขึ้นบนแนวคิดเหล่านี้

ฉันเห็นด้วยกับที่เปิดที่เปลี่ยนเส้นทางด้วยสัญลักษณ์เช่นนี้: &2>1ไม่พอใจมากสำหรับโปรแกรมเมอร์ที่ทันสมัย ​​แต่นั่นเป็นเรื่องที่น่าตกใจ Bash ไม่ได้ตั้งใจที่จะเขียนโปรแกรมขนาดใหญ่และมีประสิทธิภาพ แต่ก็มีไว้เพื่อช่วยให้ผู้ดูแลระบบทำงานด้วย keypresses น้อยกว่า ;-)

และอย่างน้อยคุณสามารถเปลี่ยนเส้นทางได้ทุกที่ในบรรทัด:

$ echo This message >&2 goes to stderr 
This message goes to stderr

1
การบอก devs ไม่ให้ใช้โปรแกรมเพียงอย่างเดียวเนื่องจากเหตุผลด้านประสิทธิภาพคือการปรับให้เหมาะสมก่อนกำหนด วิธีที่สวยงามและง่ายต่อการติดตามควรใช้รหัสที่เข้าใจยากซึ่งทำงานได้ดีกว่า (ตามลำดับมิลลิวินาที)
GuyPaddock

@GuyPaddock ขออภัยคุณยังไม่ได้อ่านอย่างถูกต้อง Firs; มันเกี่ยวกับการเปลี่ยนเส้นทางท่อที่จัดการอย่างดีโดยทุบตี ถ้าใครไม่ชอบไวยากรณ์ (น่าเกลียด) วิธีการเปลี่ยนเส้นทางทุบตีเขาควรหยุดการใช้สคริปต์ทุบตีหรือเรียนรู้วิธีทุบตี ที่สอง; คุณควรรู้ว่ามันแพงแค่ไหนที่จะเปิดตัว prozess ใหม่เมื่อเทียบกับ yust call the bash builtin
ตอบแทน 42

1
มีความแตกต่างระหว่างการให้ใครบางคนรู้ว่าการแลกเปลี่ยนประสิทธิภาพของ Bash ในตัวกับการcatสั่งสอนใครบางคนไม่ควรใช้แมวเพราะมันช้า มีกรณีการใช้งานนับไม่ถ้วนที่แมวเป็นตัวเลือกที่ถูกต้องดังนั้นฉันจึงคัดค้านคำตอบของคุณ
GuyPaddock

@GuyPaddock ที่เปิดขอการechoเปลี่ยน แม้ว่าเขาจะใช้catเขาจะต้องใช้การเปลี่ยนเส้นทางทุบตี อย่างไรก็ตาม. ดังนั้นจึงไม่มีเหตุผลที่จะใช้catที่นี่ BTW ฉันใช้cat100 ครั้งต่อวัน แต่ไม่เคยอยู่ในบริบทที่ผู้เปิดร้องขอ ... คุณเข้าใจหรือไม่
return42

8

ตัวเลือกอื่นที่ฉันเพิ่งพบคือ:

    {
        echo "First error line"
        echo "Second error line"
        echo "Third error line"
    } >&2

สิ่งนี้ใช้ Bash ในตัวเท่านั้นในขณะที่ทำให้ข้อผิดพลาดหลายบรรทัดมีข้อผิดพลาดน้อยลง (เนื่องจากคุณไม่จำเป็นต้องเพิ่ม&>2ทุกบรรทัด)


1
ไม่อยากเชื่อเลยคุณลงคะแนนฉันเมื่อฉันแนะนำให้ใช้ bash-redirect และในคำตอบของคุณเองที่คุณใช้ bash-redirect
return42

1
@ return42 ฉันลงคะแนนให้คำตอบของคุณเพราะทั้งหมดนั้นก็บอก OP ว่าไม่มีคำตอบที่ดีไปกว่าที่พวกเขาเริ่มต้นด้วย .. มันไม่ใช่คำตอบจริงๆ ฉันยังไม่เห็นคำแนะนำย่อยของเชลล์ในคำตอบของคุณ ... คำตอบของคุณจริงๆแค่แนะนำให้ OP ไม่ใช้catหรือยูทิลิตี้อื่น ๆ ซึ่งเป็นหัวข้อสำหรับคำถาม
GuyPaddock

สิ่งที่ฉันพูดว่า: stackoverflow.com/questions/2990414/echo-that-outputs-to-stderr/…
return42

6

read เป็นคำสั่ง shell builtin ที่พิมพ์ไปยัง stderr และสามารถใช้เช่น echo โดยไม่ต้องทำการเปลี่ยนเส้นทางเทคนิค:

read -t 0.1 -p "This will be sent to stderr"

-t 0.1คือหมดเวลาที่ปิดใช้งานอ่านของฟังก์ชั่นหลักของการจัดเก็บหนึ่งบรรทัดของ stdin ลงในตัวแปร


5
Bash บน OS X ไม่อนุญาต "0.1"
James Roth

2

ทำสคริปต์

#!/bin/sh
echo $* 1>&2

นั่นจะเป็นเครื่องมือของคุณ

หรือสร้างฟังก์ชั่นหากคุณไม่ต้องการมีสคริปต์แยกไฟล์


6
ดีกว่าที่จะเป็นฟังก์ชั่น (เช่นคำตอบของ James Roth) และดีกว่าที่จะส่งต่อการขัดแย้งทั้งหมดไม่ใช่แค่ในตอนแรก
Cascabel

2
ทำไมฟังก์ชั่นถึงดีกว่า? (หรือมิฉะนั้น: "ดีกว่าที่จะอธิบายว่าทำไมมันจะดีกว่า ... ")
Ogre Psalm33

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

-10

Mac OS X:ฉันลองคำตอบที่ได้รับการยอมรับและคำตอบอื่น ๆ สองสามข้อและทั้งหมดนั้นส่งผลให้เขียน STDOUT ไม่ใช่ STDERR บน Mac ของฉัน

นี่เป็นวิธีแบบพกพาในการเขียนข้อผิดพลาดมาตรฐานโดยใช้ Perl:

echo WARNING! | perl -ne 'print STDERR'

lol downvote สิ่งที่คุณต้องการ แต่นี่เป็นทางออกที่ฉันใช้จริงในรหัสของฉัน!
Noah Sussman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.