xargs และ vi -“ อินพุตไม่ได้มาจากเทอร์มินัล”


14

ฉันมีphp.iniไฟล์ประมาณ 10 ไฟล์ในระบบของฉันตั้งอยู่ทั่วทุกที่และฉันต้องการเรียกดูไฟล์เหล่านั้นอย่างรวดเร็ว ฉันลองคำสั่งนี้:

locate php.ini | xargs vi

แต่viเตือนฉันInput is not from a terminalแล้วคอนโซลก็เริ่มแปลกจริง ๆ - หลังจากนั้นฉันต้องกด:q!เพื่อออกจากviนั้นตัดการเชื่อมต่อจากเซสชัน ssh และเชื่อมต่อใหม่เพื่อให้คอนโซลทำงานได้ตามปกติอีกครั้ง

ฉันคิดว่าฉันเข้าใจว่าเกิดอะไรขึ้นที่นี่ - โดยทั่วไปคำสั่งยังไม่เสร็จเมื่อviเริ่มต้นดังนั้นคำสั่งอาจไม่เสร็จและviไม่คิดว่าเทอร์มินัลอยู่ในโหมดปกติ

ฉันไม่รู้ว่าจะแก้ไขอย่างไร ฉันค้นหา Google และยัง unix.stackexchange.com ด้วยโชคไม่ดี



ในฐานะที่เป็นบันทึกด้านข้างคุณสามารถเรียกใช้resetเพื่อรีเซ็ตเครื่องของคุณเมื่อมันได้รับการเมาขึ้น (คุณไม่ต้องตัดการเชื่อมต่อจากเซสชั่น ssh)
wisbucky

คำตอบ:


12
vi $(locate php.ini)

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

(IFS=$'\n'; vi $(locate php.ini))


คำอธิบาย:

สิ่งที่เกิดขึ้นคือโปรแกรมต่างๆได้สืบทอดตัวอธิบายไฟล์ของพวกเขาจากกระบวนการที่เกิดขึ้น xargsมีการเชื่อมต่อ STDIN กับ STDOUT ของlocateดังนั้นจึงviไม่มีเงื่อนงำสิ่งที่ STDIN ดั้งเดิมเข้ามา


2
xargs วิเศษมากหนึ่งในเครื่องมือโปรดของฉัน - มันไม่เหมาะสำหรับใช้กับโปรแกรมที่ใช้ stdin สำหรับสิ่งอื่นนอกเหนือจาก data feed ผมชอบคำตอบของคุณและคำอธิบายของคุณกว่าที่อื่น ๆ เพื่อให้ +1 ล่ะค่ะ :)
CAS

@ CraigSanders ฉันไม่ชอบเพราะมันง่ายเกินไปที่จะละเมิด (ใช้ไม่ถูกต้อง) และจบลงด้วยการทำลาย ฉันไม่เคยพบเจออะไรที่ฉันต้องใช้xargsเพื่อสิ่งที่ไม่สามารถทำได้โดยตรงกับเชลล์ (หรือfind) อย่างไรก็ตามฉันสามารถนึกถึงกรณีที่มันจะเป็นทางออกที่ดีที่สุด ดังนั้นตราบใดที่คุณเข้าใจสิ่งที่xargsทำวิธีแยกข้อโต้แย้งวิธีการเรียกใช้โปรแกรม ฯลฯ และใช้อย่างถูกต้องฉันจะบอกว่าไป :-P
Patrick

มันไม่สามารถเอาชนะได้สำหรับสิ่งต่าง ๆ เช่น... | awk '{print $3}' | xargs | sed -e 's/ /+/g' | bc(เพื่อเพิ่มค่าทั้งหมดของฟิลด์ 3) หรือด้วยsed -e 's/ /|/g'การสร้าง regexp และใช่เช่นเดียวกับเครื่องมือใด ๆ คุณจำเป็นต้องรู้วิธีใช้และข้อ จำกัด และคำเตือนของมัน
cas

vi $(...)วิธีนอกจากนี้ยังมีปัญหาเกี่ยวกับสัญลักษณ์ในเปลือกหอยอื่น ๆ zshกว่า
Stéphane Chazelas

นอกจากนี้โปรดทราบว่าด้วยxargsวิธีการที่อยู่ข้างปัญหาช่องว่างชื่อไฟล์ที่มีเครื่องหมายคำพูดเดี่ยวเครื่องหมายคำพูดคู่และแบ็กสแลชก็เป็นปัญหาเช่นกัน
Stéphane Chazelas

10

คำถามนี้ถูกถามก่อนหน้านี้ในฟอรัมผู้ใช้ขั้นสูง

อ้างจากคำตอบของ @ grawity สำหรับคำถามนั้น:

เมื่อคุณเรียกใช้โปรแกรมผ่าน xargs stdin ของโปรแกรม (อินพุตมาตรฐาน) ชี้ไปที่ / dev / null (เนื่องจาก xargs ไม่รู้จัก stdin ดั้งเดิมจึงเป็นสิ่งที่ดีที่สุดถัดไป)

Vim คาดว่า stdin จะเหมือนกับเทอร์มินัลการควบคุมและดำเนินการกับ ioctl ที่เกี่ยวข้องกับเทอร์มินัลต่างๆบน stdin โดยตรง เมื่อเสร็จสิ้นบน / dev / null (หรือตัวอธิบายไฟล์ที่ไม่ใช่ tty) ioctls เหล่านั้นจะไม่มีความหมายและส่งคืน ENOTTY ซึ่งจะถูกละเว้นอย่างเงียบ ๆ

สิ่งนี้ถูกกล่าวถึงในหน้าคู่มือสำหรับ xarg จาก OSX / BSD:

-o เปิด stdin ใหม่เป็น / dev / tty ในกระบวนการลูกก่อนดำเนินการคำสั่ง สิ่งนี้มีประโยชน์หากคุณต้องการให้ xargs เรียกใช้แอปพลิเคชันแบบโต้ตอบ

ดังนั้นบน OSX คุณสามารถใช้คำสั่งต่อไปนี้:

find . -name "php.ini" | xargs -o vim

ในขณะที่ไม่มีการสลับโดยตรงกับรุ่น GNU คำสั่งนี้จะทำงาน (ตรวจสอบให้แน่ใจว่าได้รวมdummyสตริงไว้มิฉะนั้นจะทำให้ไฟล์แรกหล่น)

find . -name "php.ini" | xargs bash -c '</dev/tty vim "$@"' dummy

การแก้ปัญหาดังกล่าวข้างต้นเป็นมารยาทไจ McGuigan ใน SuperUser เพิ่มพวกเขาที่นี่สำหรับผู้เข้าชมในอนาคตที่ค้นหาเว็บไซต์สำหรับข้อผิดพลาดนี้


3
+1 ขอบคุณสำหรับ -o tip ฉันใช้ xargs มาหลายปีแล้วและไม่เคยสังเกตเลยว่า .... แค่ตรวจสอบ man page ในระบบของฉันนั่นเป็นเพราะมันไม่ใช่คุณสมบัติของ GNU xargs man page ให้xargs sh -c 'emacs "$@" < /dev/tty' emacsเป็นสิ่งที่พวกเขาอ้างว่าเป็นทางเลือกที่ยืดหยุ่นและพกพาได้มากกว่า (แม้ว่าจะเป็นเรื่องตลกสำหรับ GNU ที่ต้องการความสะดวกในการพกพา :)
cas

2

ด้วย GNU findutilsและเชลล์ที่สนับสนุนการทดแทนกระบวนการ (ksh, zsh, bash) คุณสามารถทำได้:

xargs -r0a <(locate -0 php.ini) vi

ความคิดที่จะส่งรายการไฟล์ผ่านทาง-a filenameมากกว่า stdin การใช้-0ทำให้แน่ใจว่ามันใช้งานได้โดยไม่คำนึงว่ามีชื่อไฟล์หรือไม่ประกอบด้วยอักขระใดบ้าง

ด้วยzshคุณสามารถทำ:

vi ${(0)"$(locate -0 php.ini)"}

(โดยที่0แฟล็กการขยายพารามิเตอร์จะแยกใน NULs)

อย่างไรก็ตามโปรดทราบว่าตรงกันข้ามกับxargs -rที่ยังคงทำงานviโดยไม่มีข้อโต้แย้งหากไม่พบไฟล์



0

ข้อผิดพลาดนี้เกิดขึ้นเมื่อเรียกใช้กลุ่มและเชื่อมต่อกับเอาต์พุตของไพพ์ไลน์ก่อนหน้าแทนที่จะเป็นเทอร์มินัลและได้รับอินพุตที่ไม่คาดคิดที่แตกต่างกัน (เช่น NUL) สิ่งเดียวกันจะเกิดขึ้นเมื่อคุณเรียกใช้: vim < /dev/nullดังนั้นresetคำสั่งในกรณีนี้จะช่วยได้ นี้จะอธิบายได้ดีโดยgrawity ที่ superuser

บน Unix / OSX คุณสามารถใช้xargsกับ-oพารามิเตอร์เช่น:

locate php.ini | xargs -o vim

-oเปิด stdin อีกครั้งเป็น / dev / tty ในกระบวนการ child ก่อนดำเนินการคำสั่ง สิ่งนี้มีประโยชน์หากคุณต้องการให้ xargs เรียกใช้แอปพลิเคชันแบบโต้ตอบ

บน Linux ให้ลองวิธีแก้ปัญหาต่อไปนี้:

locate php.ini | xargs -J% sh -c 'vim < /dev/tty $@'

หรือใช้ GNU parallelแทนxargsบังคับให้จัดสรร tty ตัวอย่างเช่น:

locate php.ini | parallel -X --tty vi

หมายเหตุ: parallelบน Unix / OSX จะไม่ทำงานเนื่องจากมีพารามิเตอร์ที่แตกต่างกันและไม่รองรับ tty

คำสั่งยอดนิยมอื่น ๆ อีกมากมายให้การจัดสรรแบบหลอกเช่นกัน (เหมือน-tในssh) ดังนั้นตรวจสอบความช่วยเหลือ

หรือใช้findเพื่อส่งชื่อไฟล์เพื่อแก้ไขดังนั้นไม่จำเป็นต้องxargsใช้-execตัวอย่างเช่น:

find /etc -name php.ini -exec vim {} +

0

@ แพททริคIFSสับเป็นเพียงสิ่งที่จำเป็นสำหรับเปลือกหอยเป็นใบ้เหมือนและbash แยกสตริงในการขึ้นบรรทัดใหม่โดยค่าเริ่มต้นzshfish

$ vim (locate php.ini)

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


zshแยก SPC, TAB, NL และ NUL เป็นค่าเริ่มต้น สิ่งที่มันไม่ได้ทำการเปรียบเทียบกับbashกำลังทำการวนรอบผลลัพธ์ดังนั้นอักขระตัวแทนในชื่อไฟล์ไม่ใช่ปัญหา ในzshคุณจะทำIFS=$'\0'; vi $(locate -0 php.ini)หรือตามที่ฉันแสดงในคำตอบvi ${(0)"$(locate -0 php.ini)"}สำหรับตัวดำเนินการแยกอย่างชัดเจน โปรดทราบ tcsh'svi "`locate php.ini`"
Stéphane Chazelas

อ๊ะอึ ตกลงนี้ทำงาน: $ f='not there'<ret>$ ls $f<ret>แต่ตอนนี้ไม่ได้: echo not thereLS ตกลงดูเหมือนว่าฉันจำเป็นต้องอัปเดตนี้เล็กน้อย
ปริศนานักฟิสิกส์

ใช่ zsh ls "$(echo test; echo other test)"ไม่ได้ทำสิ่งที่ถูกต้องเมื่อคุณทำ ปลาเท่านั้นที่ทำสิ่งที่ถูกต้อง
ปริศนานักฟิสิกส์

สมมติว่าคุณมีความหมายเหมือนกันโดยไม่ใส่เครื่องหมายอัญประกาศนั่นไม่ใช่ "สิทธิ" ที่แยกออกจากกันเป็นเพียงตัวเลือกอื่น zsh แยกคำเป็นค่าเริ่มต้น (เช่นเชลล์อื่น ๆ ทั้งหมด) และสามารถบอกให้แยกบรรทัดหรือบน NUL ไม่ว่าจะผ่าน$IFSหรือผ่านโอเปอเรเตอร์ที่ชัดเจน ( fและ0แฟล็กการขยายพารามิเตอร์) สำหรับชื่อไฟล์โดยพลการการแยกคำหรือการแยกทีละบรรทัดนั้นไม่ถูกต้องเท่ากันคุณจำเป็นต้องแยกเป็น NUL หรือแยกการเข้ารหัสบางอย่างซึ่งfishไม่สามารถทำได้ ในzshนั่นคือIFS=$'\0'; ls -ld -- $(printf '%s\0' "$file1" "$file2")หรือls -ld -- ${(0)"$(printf '%s\0' "$file1" "$file2")"}
Stéphane Chazelas

Meh การขึ้นบรรทัดใหม่นั้นดีพอ เช่นเดียวกับคำตอบที่กล่าวไว้บรรทัดใหม่ในชื่อไฟล์นั้นหายากมาก แท้จริงฉันไม่เคยเห็นมันเกิดขึ้นใน 17 ปี และการขึ้นบรรทัดใหม่เป็นตัวคั่นที่สะดวกกว่าวิธี nuls
ปริศนานักฟิสิกส์

0

วิธีที่รวดเร็วที่จะทำมันสมมติว่าคุณสามารถรับประกันไม่มีเส้นทางแฟ้มประกอบด้วย SPC, TAB, NL, *, ?, [ตัวละคร (ยัง\และ{...}ในเปลือกหอยบางคน) คือการใช้กลับเห็บ(aka สำเนียงหลุมฝังศพ)ดำเนินการคำสั่งก่อนที่จะ คำสั่งอื่นกำลังทำงาน

เช่น

vi `find / -type f -name 'php.ini'`

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

ตัวอย่างเช่นในบรรทัดด้านบนfind / -type f -name 'php.ini'คำสั่งจะดำเนินการก่อนส่งเอาต์พุตจากนั้นviจะถูกดำเนินการตามผลลัพธ์ของการแบ่ง + glob ที่ใช้กับเอาต์พุตนั้น


3
ย้อนกลับ - ติ๊กสับสนง่ายเกินไปสำหรับคำพูดเดียว ใช้$(find ...)แทน
cas

1
การเดาว่าสิ่งนี้จะทำให้ช่องว่างและ / หรือการขึ้นบรรทัดใหม่ในชื่อไฟล์หรือไม่
cwd

นี่คือวิธีที่คุณเรียกใช้คำสั่งเชลล์ในการสร้างสคริปต์ bash ฉันไม่เคยมีอะไรผิดเพี้ยนในช่องว่างหรือบรรทัดใหม่ในสคริปต์ของฉันหรือเมื่อใช้งานในสายการบินเดียว อย่างไรก็ตามฉันไม่เคยลองเปิดหลายไฟล์viโดยใช้วิธีนี้ มีความเป็นไปได้ค่อนข้างมากที่มันจะเกิดบรรทัดใหม่หรือช่องว่างขึ้นอยู่กับว่าviอ่านและดำเนินการเอาต์พุตอย่างไร
tacotuesday
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.