ตามที่ @geekosaur อธิบายเชลล์จะทำการเปลี่ยนเส้นทางก่อนที่จะรันคำสั่ง เมื่อคุณพิมพ์สิ่งนี้:
sudo foo >/some/file
กระบวนการเชลล์ปัจจุบันของคุณสร้างสำเนาของตัวเองที่พยายามเปิดขึ้น/some/file
เพื่อเขียนเป็นครั้งแรกจากนั้นหากประสบความสำเร็จจะทำให้ไฟล์นั้นเป็นตัวอธิบายเอาต์พุตมาตรฐานและเฉพาะในกรณีที่ประสบความสำเร็จเท่านั้นจึงจะดำเนินการsudo
ได้ นี่คือความล้มเหลวในขั้นตอนแรก
หากคุณได้รับอนุญาต (การกำหนดค่า sudoer มักจะไม่รวมเชลล์ที่กำลังรันอยู่) คุณสามารถทำสิ่งนี้ได้:
sudo bash -c 'foo >/some/file'
แต่ฉันคิดว่าเป็นทางออกที่ดีโดยทั่วไปคือการใช้| sudo tee
แทน>
และแทน| sudo tee -a
>>
นี่เป็นประโยชน์อย่างยิ่งหากการเปลี่ยนเส้นทางเป็นเหตุผลเดียวที่ฉันต้องการsudo
ตั้งแต่แรก ท้ายที่สุดกระบวนการทำงานโดยไม่จำเป็นเนื่องจากรูทเป็นสิ่งที่sudo
สร้างขึ้นเพื่อหลีกเลี่ยง และการทำงานecho
ในฐานะรูทนั้นโง่มาก
echo '[archlinuxfr]' | sudo tee -a /etc/pacman.conf >/dev/null
echo 'Server = http://repo.archlinux.fr/$arch' | sudo tee -a /etc/pacman.conf >/dev/null
echo ' ' | sudo tee -a /etc/pacman.conf >/dev/null
ฉันเพิ่ม> /dev/null
ที่สิ้นสุดเพราะtee
ส่งออกของไปทั้งไฟล์ชื่อและออกมาตรฐานของตัวเองและฉันไม่ต้องการที่จะเห็นมันใน terminal ของฉัน ( tee
คำสั่งทำหน้าที่เหมือนตัวเชื่อมต่อ "T" ในไปป์ไลน์ทางกายภาพซึ่งเป็นที่มาของชื่อ) และฉันเปลี่ยนไปใช้เครื่องหมายคำพูดเดี่ยว ( '
... '
) แทนที่จะเป็นคู่ ( "
... "
) เพื่อให้ทุกอย่างเป็นตัวอักษรและฉัน ไม่ต้องใส่เครื่องหมายในด้านหน้าของใน$
$arch
(หากไม่มีเครื่องหมายคำพูดหรือแบ็กสแลช$arch
จะถูกแทนที่ด้วยค่าของพารามิเตอร์เชลล์arch
ซึ่งอาจไม่มีอยู่จริงซึ่งในกรณีนี้$arch
จะถูกแทนที่ด้วยอะไรและหายไป)
ดังนั้นจะดูแลการเขียนไปยังไฟล์เป็นรูทโดยใช้sudo
. ตอนนี้สำหรับการพูดนอกเรื่องที่มีความยาวเกี่ยวกับวิธีการส่งออกข้อความที่มีขึ้นบรรทัดใหม่ในเชลล์สคริปต์ :)
หากต้องการ BLUF อย่างที่พวกเขากล่าวทางออกที่ฉันต้องการคือเพียงแค่ป้อนเอกสารที่นี่ลงในsudo tee
คำสั่งด้านบน จากนั้นไม่จำเป็นต้องมีcat
หรือecho
หรือprintf
หรือคำสั่งอื่นใดเลย เครื่องหมายคำพูดเดี่ยวได้ย้ายไปที่คำนำของ Sentinel <<'EOF'
แต่มีผลเหมือนกันที่นั่น: เนื้อหาจะถือว่าเป็นข้อความตามตัวอักษรดังนั้นจึง$arch
ถูกทิ้งไว้ตามลำพัง:
sudo tee -a /etc/pacman.conf >/dev/null <<'EOF'
[archlinuxfr]
Server = http://repo.archlinux.fr/$arch
EOF
แต่ในขณะที่ฉันทำมันก็มีทางเลือกอื่น นี่คือบางส่วน:
คุณสามารถใช้หนึ่งรายการecho
ต่อบรรทัด แต่จัดกลุ่มทั้งหมดเข้าด้วยกันใน subshell ดังนั้นคุณจะต้องต่อท้ายไฟล์เพียงครั้งเดียว:
(echo '[archlinuxfr]'
echo 'Server = http://repo.archlinux.fr/$arch'
echo ' ') | sudo tee -a /etc/pacman.conf >/dev/null
หากคุณเพิ่มลง-e
ในecho
(และคุณกำลังใช้เชลล์ที่รองรับส่วนขยายที่ไม่ใช่ POSIX นั้น) คุณสามารถฝังบรรทัดใหม่ลงในสตริงได้โดยตรงโดยใช้\n
:
echo -e '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' |
sudo tee -a /etc/pacman.conf >/dev/null
แต่ตามที่กล่าวไว้ข้างต้นนั่นไม่ใช่พฤติกรรมที่ระบุโดย POSIX เชลล์ของคุณอาจแค่สะท้อนลิเทอรัล-e
ตามด้วยสตริงที่มีตัวอักษรจำนวน\n
มากแทน วิธี POSIX คือการใช้printf
แทนecho
; มันจะถือว่าอาร์กิวเมนต์ของมันโดยอัตโนมัติเช่นเดียวกับecho -e
แต่จะไม่ต่อท้ายบรรทัดใหม่โดยอัตโนมัติดังนั้นคุณต้องติดเพิ่มที่\n
นั่นด้วย:
printf '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n \n' |
sudo tee -a /etc/pacman.conf >/dev/null
ด้วยวิธีแก้ปัญหาเหล่านี้สิ่งที่คำสั่งได้รับเป็นสตริงอาร์กิวเมนต์ประกอบด้วยลำดับสองอักขระ\n
และขึ้นอยู่กับโปรแกรมคำสั่งเอง (โค้ดภายในprintf
หรือecho
) เพื่อแปลสิ่งนั้นเป็นบรรทัดใหม่ ในเชลล์สมัยใหม่จำนวนมากคุณมีตัวเลือกในการใช้เครื่องหมายคำพูด ANSI $'
... '
ซึ่งจะแปลลำดับเช่น\n
เป็นบรรทัดใหม่ตามตัวอักษรก่อนที่โปรแกรมคำสั่งจะเห็นสตริง นั่นหมายความว่าสตริงดังกล่าวทำงานร่วมกับคำสั่งใดก็ได้รวมถึง old -e
-less ธรรมดาecho
:
echo $'[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' |
sudo tee -a /etc/pacman.conf >/dev/null
แต่ในขณะที่พกพาได้ง่ายกว่าecho -e
ราคา ANSI ก็ยังคงเป็นส่วนขยายที่ไม่ใช่ POSIX
และอีกครั้งในขณะที่สิ่งเหล่านี้เป็นตัวเลือกทั้งหมดฉันชอบtee <<EOF
วิธีแก้ปัญหาที่ตรงด้านบน