จะส่งเอาต์พุตทั้งหมดไปยัง 'logger' ใน POSIX เชลล์ได้อย่างไร?


10

ฉันต้องการที่จะเข้าสู่ระบบการส่งออกมาตรฐานและข้อผิดพลาดมาตรฐานแยกต่างหากในการใช้.xprofile loggerใน Bash ฉันคิดว่ามันจะมีหน้าตาแบบนี้:

exec 1> >(logger --priority user.notice --tag $(basename $0)) \
     2> >(logger --priority user.error --tag $(basename $0))

ฉันจะทำสิ่งนั้นในลักษณะที่รองรับPOSIX /bin/shได้อย่างไร

คำตอบ:


10

ไม่มีเทียบเท่า POSIX คุณสามารถทำการเปลี่ยนเส้นทางด้วยexecไม่ใช่แยก ไปป์ต้องใช้ส้อมและเชลล์รอให้เด็กทำจนเสร็จ

ทางออกหนึ่งคือการใส่รหัสของคุณทั้งหมดในฟังก์ชั่น

all_my_code () {
  
}
{ all_my_code |
  logger --priority user.notice --tag "$(basename "$0")"; } 2>&1 | 
  logger --priority user.error --tag "$(basename "$0")"

(สิ่งนี้ยังบันทึกข้อผิดพลาดจากอินสแตนซ์ stdout ของตัวบันทึกไปยังอินสแตนซ์ stderr ด้วยคุณสามารถหลีกเลี่ยงสิ่งนี้ได้ด้วยการสลับไฟล์อธิบายเพิ่มเติม)

หากคุณต้องการให้พาเรนต์เชลล์ออกแม้ว่าloggerกระบวนการจะยังทำงานอยู่ให้วาง&ที่ส่วนท้ายของการloggerเรียกใช้

{ all_my_code |
  logger --priority user.notice --tag "$(basename "$0")" & } 2>&1 | 
  logger --priority user.error --tag "$(basename "$0")" &

หรือคุณสามารถใช้ไพพ์ที่มีชื่อ

pipe_dir=$(mktemp -d)
mkfifo "$pipe_dir/out" "$pipe_dir/err"
<"$pipe_dir/out" logger --priority user.notice --tag "$(basename "$0")" &
<"$pipe_dir/err" logger --priority user.error --tag "$(basename "$0")" &
exec >"$pipe_dir/out" 2>"$pipe_dir/err" 

rm -r "$pipe_dir"

ที่จะปรากฏใน syslog เป็นสองรายการ (ออก & ผิด) สำหรับสคริปต์ทั้งหมด ตัวอย่างในคำถามจะมีหนึ่งรายการ (หรือสองรายการ) ต่อคำสั่ง
DarkHeart

@DarkHeart ฉันไม่เข้าใจความคิดเห็นของคุณ รหัสในคำถามและคำตอบทั้งสองที่ฉันเสนอให้เรียกใช้อินสแตนซ์จำนวนเท่ากันloggerและส่งอินพุตเดียวกันให้กับแต่ละรายการ
Gilles 'หยุดชั่วร้าย'

ขออภัยความผิดพลาดของฉัน
DarkHeart

1
ดูเหมือนง่ายกว่าดังนั้นฉันจะไปกับโซลูชันไปป์ที่มีชื่อของคุณ
l0b0

9

การแทนคำสั่ง POSIX / กระบวนการ


_log()( x=0
    while  [ -e "${TMPDIR:=/tmp}/$$.$((x+=1))" ]
    do     continue; done        &&
    mkfifo -- "$TMPDIR/$$.$x"    &&
    printf %s\\n "$TMPDIR/$$.$x" || exit
    exec >&- >/dev/null
    {  rm -- "$TMPDIR/$$.$x"
       logger --priority user."$1" --tag "${0##*/}"
    }  <"$TMPDIR/$$.$x" &
)   <&- </dev/null

คุณควรใช้สิ่งต่อไปนี้:

exec >"$(_log notice)" 2>"$(_log error)"

นี่คือเวอร์ชันที่ใช้ประโยชน์จากmktempคำสั่ง:

_log()( p=
    mkfifo "${p:=$(mktemp -u)}"    &&
    printf %s "$p"                 &&
    exec  <&- >&- <>/dev/null >&0  &&
    {   rm "$p"
        logger --priority user."$1" --tag "${0##*/}"
    }   <"$p" &
)

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

ในขณะที่ POSIX เชลล์ไม่ได้ให้การพิสูจน์โดยตรงกับสิ่งนั้นการเลียนแบบมันทำได้ง่ายมาก สิ่งที่คุณต้องทำคือสร้างไฟล์พิมพ์ชื่อของมันออกมาจากมาตรฐานของการทดแทนคำสั่งและในพื้นหลังของการรันคำสั่งของคุณซึ่งจะส่งออกไปยังไฟล์นั้น ตอนนี้คุณสามารถเปลี่ยนเส้นทางไปเป็นค่าของการขยายตัวได้ - เหมือนกับที่คุณทำกับการทดแทนกระบวนการ ดังนั้น POSIX เชลล์จึงมีเครื่องมือทั้งหมดที่คุณต้องการแน่นอน - สิ่งที่จำเป็นต้องมีก็คือให้คุณใช้มันในแบบที่เหมาะกับคุณ

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


อีกวิธีคือการพันผ้า มันสามารถทำได้จากภายในสคริปต์

x=${x##*[!0-9]*}
_log(){ 
    logger --priority user."$1" --tag "${0##*/}"
}   2>/dev/null >&2

cd ../"$PPID.$x" 2>/dev/null &&
trap 'rm -rf -- "${TMPDIR:-/tmp}/$PPID.$x"' 0 || 
{   until cd -- "${TMPDIR:=/tmp}/$$.$x"
    do    mkdir -- "$TMPDIR/$$.$((x+=1))"
    done  && 
    x=$x "$0" "$@" | _log notice
    exit
}   2>&1 | _log error

โดยทั่วไปจะอนุญาตให้สคริปต์ของคุณเรียกตัวเองหากยังไม่ได้รับไดเรกทอรีการทำงานใน temp เพื่อบูต


1
เสร็จแล้วขอบคุณ!
l0b0

@ l0b0 - ฉันเพิ่งจะอัปเดตอีกประเภทหนึ่งเช่นนี้ ... มันมีจุดประสงค์ทั่วไปมากขึ้นและในที่สุดฉันก็มีความสามารถในการจัดการตัวเลือกบางอย่างที่เกี่ยวข้องกับคำอธิบาย / ไฟล์ของ i / o
mikeserv

@ l0b0 - ถ้าคุณต้องการที่จะใช้mktempและคำสั่งที่ไม่ได้มาตรฐานอื่น ๆ _log()( p=; mkfifo "${p:=$(mktemp -u)}"; printf %s "$p"; exec <&- >&- <>/dev/null >&0; { rm "$p"; logger --priority user."$1" --tag "${0##*/}"; } <"$p" &)ก็อาจจะ: ฉันเขียนมันโดยใช้ภาษาคำสั่งแบบพกพาอย่างเต็มที่ในทุกวิธี - ยกเว้นของคุณเอง นอกจากนี้ยังbasenameไม่ใช่อรรถประโยชน์ที่มีประโยชน์มาก "${0##*/}"มีทั้งความแข็งแกร่งและเร็วกว่า
mikeserv

ฉันเปลี่ยนเพราะฉันเพียงต้องการให้ทำงานบนแพลตฟอร์มที่ทันสมัย ปัญหาหลักคือการที่.xprofile มีshจะได้รับการดำเนินการด้วย
l0b0

1
@ Wildcard - เพียงไม่กี่นาทีและฉันจะพยายามสังคายนาส่วนแรกของคำถาม แต่คำตอบสำหรับคำถามที่สองคือใช่: กรณีขอบคือzshเปิดใช้งาน w / multios ส่วนแรก: { this; can\'t; happen; before; } < this. มันช่วยได้ไหม
mikeserv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.