จับเอาท์พุทของคำสั่งสุดท้ายโดยอัตโนมัติลงในตัวแปรโดยใช้ Bash?


139

ฉันต้องการที่จะสามารถใช้ผลลัพธ์ของคำสั่งที่ดำเนินการครั้งสุดท้ายในคำสั่งที่ตามมา ตัวอย่างเช่น,

$ find . -name foo.txt
./home/user/some/directory/foo.txt

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

mv <some-variable-that-contains-the-result> /some/new/location

ฉันจะทำมันได้อย่างไร อาจจะใช้ตัวแปรทุบตีบางอย่าง?

ปรับปรุง:

เพื่อชี้แจงฉันไม่ต้องการกำหนดสิ่งต่าง ๆ ด้วยตนเอง สิ่งที่ฉันตามมาคืออะไรบางอย่างเช่นตัวแปร bash ในตัวเช่น

ls /tmp
cd $_

$_ถืออาร์กิวเมนต์สุดท้ายของคำสั่งก่อนหน้า ฉันต้องการสิ่งที่คล้ายกัน แต่ด้วยผลลัพธ์ของคำสั่งสุดท้าย

อัปเดตครั้งสุดท้าย:

คำตอบของเซททำงานได้ค่อนข้างดี สิ่งที่ควรคำนึงถึง:

  • อย่าลืมtouch /tmp/xเมื่อพยายามแก้ปัญหาเป็นครั้งแรก
  • ผลลัพธ์จะถูกเก็บไว้ถ้ารหัสทางออกของคำสั่งสุดท้ายสำเร็จเท่านั้น

หลังจากเห็นการแก้ไขของคุณฉันคิดว่าจะลบคำตอบของฉัน ฉันสงสัยว่ามีอะไรในตัวที่คุณกำลังมองหา
taskinoor

ฉันไม่พบสิ่งใด ๆ ในตัว ฉันสงสัยว่ามันจะเป็นไปได้ที่จะใช้มัน .. อาจจะผ่าน. ฉันคิดว่ามันเป็นคุณสมบัติที่มีประโยชน์มาก
armandino

ฉันกลัวว่าคุณจะทำได้แค่เปลี่ยนเส้นทางไปที่ไฟล์หรือไปป์หรือจับมันมิฉะนั้นจะไม่ถูกบันทึก
bandi

3
คุณไม่สามารถทำได้หากปราศจากความร่วมมือของเชลล์และเทอร์มินัลและโดยทั่วไปพวกเขาจะไม่ให้ความร่วมมือ ดูสิ่งนี้ด้วยฉันจะใช้เอาต์พุตล่าสุดจากบรรทัดรับคำสั่งได้อย่างไร และการใช้ข้อความจากการส่งออกคำสั่งก่อนหน้านี้บนUnix Stack แลกเปลี่ยน
Gilles 'ดังนั้น - หยุดความชั่วร้าย'

6
หนึ่งในเหตุผลหลักที่ว่าทำไมคำสั่งเอาต์พุตไม่ถูกดักจับเนื่องจากเอาต์พุตสามารถมีขนาดใหญ่ได้ตามต้องการ - หลายเมกะไบต์ต่อครั้ง จริงอยู่ที่ได้รับไม่มากขนาดนั้น แต่ผลที่ออกมามากทำให้เกิดปัญหา
Jonathan Leffler

คำตอบ:


71

นี่เป็นวิธีแฮ็กจริง ๆ แต่ดูเหมือนว่าจะทำงานบางครั้ง ในระหว่างการทดสอบฉันสังเกตว่าบางครั้งมันก็ทำงานได้ไม่ดีนักเมื่อใช้^Cบรรทัดคำสั่งถึงแม้ว่าฉันจะปรับแต่งเล็กน้อยเพื่อให้ทำงานได้ดีขึ้น

แฮ็คนี้เป็นแฮ็คโหมดโต้ตอบเท่านั้นและฉันค่อนข้างมั่นใจว่าฉันจะไม่แนะนำให้ใครรู้จัก คำสั่งพื้นหลังมีแนวโน้มที่จะทำให้เกิดพฤติกรรมที่กำหนดไว้น้อยกว่าปกติ คำตอบอื่น ๆ เป็นวิธีที่ดีกว่าในการรับผลลัพธ์ทางโปรแกรม


ที่ถูกกล่าวว่านี่คือ "ทางออก":

PROMPT_COMMAND='LAST="`cat /tmp/x`"; exec >/dev/tty; exec > >(tee /tmp/x)'

ตั้งค่าตัวแปรสภาวะแวดล้อม bash นี้และใช้คำสั่งตามที่ต้องการ $LASTมักจะมีผลลัพธ์ที่คุณกำลังมองหา:

startide seth> fortune
Courtship to marriage, as a very witty prologue to a very dull play.
                -- William Congreve
startide seth> echo "$LAST"
Courtship to marriage, as a very witty prologue to a very dull play.
                -- William Congreve

1
@ armandino: หลักสูตรนี้ทำให้โปรแกรมที่คาดว่าจะโต้ตอบกับเทอร์มินัลที่ได้มาตรฐานไม่ทำงานตามที่คาดไว้ (มาก / น้อย) หรือเก็บสิ่งแปลก ๆ ใน $ LAST (emacs) แต่ฉันคิดว่ามันเกี่ยวกับดีเท่าที่คุณจะได้รับ อีกตัวเลือกเดียวคือการใช้สคริปต์ (ประเภท) เพื่อบันทึกสำเนาของทุกสิ่งไปยังไฟล์จากนั้นใช้ PROMPT_COMMAND เพื่อตรวจสอบการเปลี่ยนแปลงตั้งแต่ PROMPT_COMMAND ครั้งล่าสุด ซึ่งจะรวมถึงสิ่งที่คุณไม่ต้องการ ฉันค่อนข้างมั่นใจว่าคุณจะไม่พบอะไรที่ใกล้เคียงกับสิ่งที่คุณต้องการในเรื่องนี้
เซทโรเบิร์ตสันส์

2
ที่จะไปนิด ๆ หน่อย ๆ เพิ่มเติม: PROMPT_COMMAND='last="$(cat /tmp/last)";lasterr="$(cat /tmp/lasterr)"; exec >/dev/tty; exec > >(tee /tmp/last); exec 2>/dev/tty; exec 2> >(tee /tmp/lasterr)'ซึ่งให้ทั้งสองและ$last $lasterr
ℝaphink

@cdosborn: คนโดยค่าเริ่มต้นส่งออกผ่านเพจเจอร์ ในขณะที่ความคิดเห็นก่อนหน้าของฉันกล่าวว่า: "โปรแกรมที่คาดว่าจะโต้ตอบกับเทอร์มินัลในการออกมาตรฐานไม่ทำงานตามที่คาดไว้ (มาก / น้อย)" มากขึ้นและน้อยเป็นวิทยุติดตามตัว
Seth Robertson

ฉันได้รับ:No such file or directory
Francisco Corrales Morales

@Raphink ฉันแค่อยากจะชี้ให้เห็นการแก้ไข: ข้อเสนอแนะของคุณควรจะจบใน2> >(tee /tmp/lasterr 1>&2)เพราะเอาท์พุทมาตรฐานของteeจะต้องถูกเปลี่ยนเส้นทางกลับไปที่ข้อผิดพลาดมาตรฐาน
Hugues

90

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

vim $(!!)

ที่ไหน !!คือการขยายตัวประวัติความหมายของคำสั่งก่อนหน้านี้ '

หากคุณคาดว่าจะมีชื่อไฟล์เดียวที่มีช่องว่างหรืออักขระอื่น ๆ ในนั้นที่อาจป้องกันการแยกอาร์กิวเมนต์ที่เหมาะสมให้อ้างผล ( vim "$(!!)") การปล่อยทิ้งไว้โดยไม่เปิดเผยจะอนุญาตให้เปิดไฟล์หลายไฟล์พร้อมกันตราบใดที่ไม่มีการเว้นวรรคหรือโทเค็นการแยกเชลล์อื่น ๆ


1
การคัดลอกวางคือสิ่งที่ฉันมักจะทำในกรณีเช่นนี้เพราะคุณมักต้องการเพียงส่วนหนึ่งของผลลัพธ์ของคำสั่ง ฉันประหลาดใจที่คุณเป็นคนแรกที่พูดถึงมัน
Bruno De Fraine

4
คุณสามารถทำสิ่งเดียวกันได้มากโดยการกดแป้นน้อยลงด้วย:vim `!!`
psmears

หากต้องการดูความแตกต่างจากคำตอบที่ยอมรับให้ดำเนินการdate "+%N"แล้วecho $(!!)
Marinos An

20

Bash เป็นภาษาที่น่าเกลียด ใช่คุณสามารถกำหนดผลลัพธ์ให้กับตัวแปร

MY_VAR="$(find -name foo.txt)"
echo "$MY_VAR"

แต่ดีกว่าหวังว่าคุณจะยากที่สุด findส่งกลับผลลัพธ์เดียวเท่านั้นและผลลัพธ์นั้นไม่มีอักขระ "คี่" ในนั้นเช่นการขึ้นบรรทัดใหม่หรือการป้อนบรรทัดเนื่องจากจะถูกแก้ไขอย่างเงียบ ๆ เมื่อกำหนดให้กับตัวแปร Bash

แต่ควรระวังที่จะพูดตัวแปรของคุณให้ถูกต้องเมื่อใช้มัน!

มันเป็นการดีกว่าที่จะดำเนินการกับไฟล์โดยตรงเช่นกับfind's -execdir(ดูคู่มือ)

find -name foo.txt -execdir vim '{}' ';'

หรือ

find -name foo.txt -execdir rename 's/\.txt$/.xml/' '{}' ';'

5
พวกเขาจะไม่ถูกแก้ไขอย่างเงียบ ๆ เมื่อคุณกำหนดพวกเขาจะถูกปรับเปลี่ยนเมื่อคุณสะท้อน! คุณต้องทำecho "${MY_VAR}"เพื่อดูกรณีนี้เท่านั้น
paxdiablo

13

มีมากกว่าหนึ่งวิธีในการทำเช่นนี้ วิธีหนึ่งคือการใช้งานซึ่งจะกำหนดผลลัพธ์ของคำสั่งที่จะv=$(command) vตัวอย่างเช่น:

v=$(date)
echo $v

และคุณสามารถใช้ backquotes ได้เช่นกัน

v=`date`
echo $v

จากทุบตีคู่มือเริ่มต้น ,

เมื่อใช้รูปแบบการทดแทน backquote แบบเก่า backslash จะคงความหมายตามตัวอักษรไว้ยกเว้นเมื่อตามด้วย "$", "` "หรือ" \ " backticks แรกที่ไม่ได้นำหน้าด้วย backslash จะยุติการทดแทนคำสั่ง เมื่อใช้รูปแบบ "$ (COMMAND)" อักขระทั้งหมดระหว่างวงเล็บจะประกอบขึ้นเป็นคำสั่ง ไม่มีใครได้รับการดูแลเป็นพิเศษ

แก้ไข: หลังจากแก้ไขในคำถามดูเหมือนว่านี่ไม่ใช่สิ่งที่ OP กำลังมองหา เท่าที่ฉันรู้ไม่มีตัวแปรพิเศษที่เหมือนกับ$_ผลลัพธ์ของคำสั่งสุดท้าย


11

มันค่อนข้างง่าย ใช้คำพูดย้อนกลับ:

var=`find . -name foo.txt`

และจากนั้นคุณสามารถใช้งานได้ตลอดเวลาในอนาคต

echo $var
mv $var /somewhere

11

Disclamers:

  • คำตอบนี้ปลายครึ่งปี: D
  • ฉันเป็นผู้ใช้ tmux ที่หนักหน่วง
  • คุณต้องรันเชลล์ใน tmux เพื่อให้มันใช้งานได้

เมื่อรันเชลล์เชิงโต้ตอบใน tmux คุณสามารถเข้าถึงข้อมูลที่แสดงอยู่บนเทอร์มินัลได้อย่างง่ายดาย ลองมาดูคำสั่งที่น่าสนใจ:

  • tmux capture-pane : อันนี้คัดลอกข้อมูลที่แสดงไปยังหนึ่งในบัฟเฟอร์ภายในของ tmux มันสามารถคัดลอกประวัติที่มองไม่เห็นในขณะนี้ แต่เราไม่สนใจในตอนนี้
  • tmux list-buffers : สิ่งนี้จะแสดงข้อมูลเกี่ยวกับบัฟเฟอร์ที่ถูกจับ ใหม่ล่าสุดจะมีหมายเลข 0
  • tmux show-buffer -b (buffer num) : สิ่งนี้พิมพ์เนื้อหาของบัฟเฟอร์ที่กำหนดบนเทอร์มินัล
  • tmux paste-buffer -b (buffer num) : สิ่งนี้วางเนื้อหาของบัฟเฟอร์ที่กำหนดเป็นอินพุต

ใช่สิ่งนี้ทำให้เรามีความเป็นไปได้มากมายในตอนนี้ :) สำหรับฉันฉันตั้งชื่อแทนง่ายๆ: alias L="tmux capture-pane; tmux showb -b 0 | tail -n 3 | head -n 1"และตอนนี้ทุกครั้งที่ฉันต้องการเข้าถึงบรรทัดสุดท้ายที่ฉันใช้$(L)เพื่อรับมัน

สิ่งนี้เป็นอิสระจากเอาต์พุตสตรีมที่โปรแกรมใช้ (ไม่ว่าจะเป็น stdin หรือ stderr), วิธีการพิมพ์ (ncurses, ฯลฯ ) และรหัสทางออกของโปรแกรม - ข้อมูลเพียงแค่ต้องการแสดง


สวัสดีขอบคุณสำหรับเคล็ดลับ tmux นี้ ฉันกำลังค้นหาโดยทั่วไปเหมือนกับ OP พบความคิดเห็นของคุณและเขียนเชลล์สคริปต์เพื่อให้คุณเลือกและวางส่วนหนึ่งของผลลัพธ์ของคำสั่งก่อนหน้าโดยใช้คำสั่ง tmux ที่คุณพูดถึงและการเคลื่อนไหวที่เป็นกลุ่ม: github.com/ bgribble / lw
Bill Gribble

9

ฉันคิดว่าคุณอาจแฮ็คโซลูชันที่เกี่ยวข้องกับการตั้งค่าเชลล์ของคุณเป็นสคริปต์ที่มี:

#!/bin/sh
bash | tee /var/log/bash.out.log

จากนั้นหากคุณตั้งค่า$PROMPT_COMMANDให้ส่งออกตัวคั่นคุณสามารถเขียนฟังก์ชันตัวช่วย (อาจเรียกว่า_) ที่ทำให้คุณได้รับบันทึกชิ้นสุดท้ายของบันทึกดังกล่าวเพื่อให้คุณสามารถใช้งานได้เช่น:

% find lots*of*files
...
% echo "$(_)"
... # same output, but doesn't run the command again

7

คุณสามารถตั้งค่านามแฝงต่อไปนี้ในโปรไฟล์ทุบตีของคุณ:

alias s='it=$($(history | tail -2 | head -1 | cut -d" " -f4-))'

จากนั้นโดยพิมพ์ 's' หลังจากคำสั่งโดยพลการคุณสามารถบันทึกผลลัพธ์ลงในตัวแปรเชลล์ 'it'

ตัวอย่างการใช้งานจะเป็น:

$ which python
/usr/bin/python
$ s
$ file $it
/usr/bin/python: symbolic link to `python2.6'

ขอบคุณ! นี่คือสิ่งที่ฉันต้องการในการใช้งานgrabฟังก์ชั่นของฉันที่คัดลอกบรรทัดที่ n จากคำสั่งสุดท้ายไปยังคลิปบอร์ดgist.github.com/davidhq/f37ac87bc77f27c5027e
davidhq

6

ฉันเพิ่งกลั่นbashฟังก์ชันนี้จากคำแนะนำที่นี่:

grab() {     
  grab=$("$@")
  echo $grab
}

จากนั้นคุณเพียงทำ:

> grab date
Do 16. Feb 13:05:04 CET 2012
> echo $grab
Do 16. Feb 13:05:04 CET 2012

ปรับปรุง : ผู้ใช้ที่ไม่ระบุชื่อแนะนำให้เปลี่ยนechoโดยprintf '%s\n'ที่มีความได้เปรียบที่ว่ามันไม่ได้ตัวเลือกเช่นกระบวนการ-eในข้อความคว้า ดังนั้นหากคุณคาดหวังหรือพบกับลักษณะดังกล่าวลองพิจารณาข้อเสนอแนะนี้ ตัวเลือกอื่นคือใช้cat <<<$grabแทน


1
ตกลงพูดอย่างนี้ไม่ใช่คำตอบเพราะส่วน "อัตโนมัติ" หายไปอย่างสมบูรณ์
Tilman Vogel

คุณอธิบายcat <<<$grabตัวเลือกนี้ได้หรือไม่
leoj

1
การอ้างอิงจากman bash: "Here Strings: เอกสารที่แตกต่างของที่นี่รูปแบบคือ: <<<wordคำจะถูกขยายและมอบให้กับคำสั่งในอินพุตมาตรฐาน" ดังนั้นค่าของ$grabจะถูกป้อนไปยังcatstdin และcatเพียงป้อนกลับไปที่ stdout
Tilman Vogel

5

ด้วยการพูดว่า "ฉันต้องการให้สามารถใช้ผลลัพธ์ของคำสั่งที่ดำเนินการครั้งสุดท้ายในคำสั่งต่อมา" ฉันถือว่า - คุณหมายถึงผลลัพธ์ของคำสั่งใด ๆ ไม่ใช่แค่ค้นหา

หากเป็นเช่นนั้น - xargsคือสิ่งที่คุณกำลังมองหา

find . -name foo.txt -print0 | xargs -0 -I{} mv {} /some/new/location/{}

หรือถ้าคุณสนใจที่จะเห็นผลลัพธ์ก่อน:

find . -name foo.txt -print0

!! | xargs -0 -I{} mv {} /some/new/location/{}

คำสั่งนี้เกี่ยวข้องกับหลายไฟล์และใช้งานได้อย่างมีเสน่ห์แม้ว่าพา ธ และ / หรือชื่อไฟล์จะมีช่องว่าง

สังเกตเห็นส่วนmv {} / some / new / location / {}ของคำสั่ง คำสั่งนี้เป็นการสร้างและดำเนินการสำหรับแต่ละบรรทัดที่พิมพ์โดยคำสั่งก่อนหน้า บรรทัดที่พิมพ์โดยคำสั่งก่อนหน้าจะถูกแทนที่ด้วยที่นี่{} {}

ข้อความที่ตัดตอนมาจาก man page ของ xargs:

xargs - สร้างและดำเนินการบรรทัดคำสั่งจากอินพุตมาตรฐาน

สำหรับรายละเอียดเพิ่มเติมดูหน้าคน: man xargs



4

ฉันมักจะทำสิ่งที่คนอื่น ๆ ที่นี่แนะนำ ... โดยไม่ได้รับมอบหมาย:

$find . -iname '*.cpp' -print
./foo.cpp
./bar.cpp
$vi `!!`
2 files to edit

คุณสามารถได้รับนักเล่นมากขึ้นถ้าคุณชอบ:

$grep -R "some variable" * | grep -v tags
./foo/bar/xxx
./bar/foo/yyy
$vi `!!`

2

หากสิ่งที่คุณต้องการคือการรันคำสั่งสุดท้ายของคุณอีกครั้งและรับเอาท์พุทตัวแปร bash แบบง่ายจะทำงาน:

LAST=`!!`

ดังนั้นคุณสามารถเรียกใช้คำสั่งของคุณในผลลัพธ์ด้วย:

yourCommand $LAST

สิ่งนี้จะวางไข่กระบวนการใหม่และรันคำสั่งของคุณอีกครั้งจากนั้นให้ผลลัพธ์ ดูเหมือนว่าสิ่งที่คุณต้องการจะเป็นไฟล์ประวัติ bash สำหรับเอาต์พุตคำสั่ง ซึ่งหมายความว่าคุณจะต้องจับเอาท์พุทที่ bash ส่งไปยังเทอร์มินัลของคุณ คุณสามารถเขียนบางอย่างเพื่อดู / dev หรือ / proc ที่จำเป็น แต่มันยุ่ง นอกจากนี้คุณยังสามารถสร้าง "ไพพ์พิเศษ" ระหว่างคำของคุณและทุบตีด้วยคำสั่งทีอยู่ตรงกลางซึ่งจะเปลี่ยนเส้นทางไปยังไฟล์เอาต์พุตของคุณ

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


1

ต่อไปนี้เป็นวิธีหนึ่งในการดำเนินการหลังจากที่คุณได้ดำเนินการคำสั่งและตัดสินใจว่าคุณต้องการเก็บผลลัพธ์ไว้ในตัวแปร:

$ find . -name foo.txt
./home/user/some/directory/foo.txt
$ OUTPUT=`!!`
$ echo $OUTPUT
./home/user/some/directory/foo.txt
$ mv $OUTPUT somewhere/else/

หรือถ้าคุณรู้ล่วงหน้าว่าคุณต้องการผลลัพธ์ในตัวแปรคุณสามารถใช้ backticks:

$ OUTPUT=`find . -name foo.txt`
$ echo $OUTPUT
./home/user/some/directory/foo.txt

1

เพื่อเป็นทางเลือกแทนคำตอบที่มีอยู่: ใช้whileถ้าชื่อไฟล์ของคุณสามารถมีช่องว่างเช่นนี้:

find . -name foo.txt | while IFS= read -r var; do
  echo "$var"
done

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

หมายเหตุ:สิ่งที่มีอยู่แล้วนั้นไม่ได้เกี่ยวกับผลลัพธ์ แต่เกี่ยวกับสถานะของคำสั่งสุดท้าย


1

คุณสามารถใช้ !!: 1 ตัวอย่าง:

~]$ ls *.~
class1.cpp~ class1.h~ main.cpp~ CMakeList.txt~ 

~]$ rm !!:1
rm class1.cpp~ class1.h~ main.cpp~ CMakeList.txt~ 


~]$ ls file_to_remove1 file_to_remove2
file_to_remove1 file_to_remove2

~]$ rm !!:1
rm file_to_remove1

~]$ rm !!:2
rm file_to_remove2

สัญกรณ์นี้คว้าอาร์กิวเมนต์เลขจากคำสั่ง: เอกสารดูสำหรับ "$ {พารามิเตอร์: ชดเชย}" นี่: gnu.org/software/bash/manual/html_node/... ดูตัวอย่างเพิ่มเติมได้ที่นี่: howtogeek.com/howto/44997/…
Alexander Bird

1

ฉันมีความต้องการที่คล้ายกันซึ่งฉันต้องการใช้ผลลัพธ์ของคำสั่งสุดท้ายเป็นคำสั่งถัดไป เหมือน | (ท่อ). เช่น

$ which gradle 
/usr/bin/gradle
$ ls -alrt /usr/bin/gradle

เพื่อสิ่งที่ชอบ -

$ which gradle |: ls -altr {}

การแก้ไข: สร้างไปป์ที่กำหนดเองนี้ ง่ายมากใช้ xargs -

$ alias :='xargs -I{}'

ไม่มีอะไรโดยการสร้างมือสั้น ๆ สำหรับ xargs มันใช้งานได้อย่างมีเสน่ห์และมีประโยชน์จริงๆ ฉันเพิ่งเพิ่มนามแฝงในไฟล์. bash_profile


1

มันสามารถทำได้โดยใช้ความมหัศจรรย์ของตัวอธิบายไฟล์และ lastpipeตัวเลือกเปลือก

จะต้องทำกับสคริปต์ - ตัวเลือก "lastpipe" จะไม่ทำงานในโหมดโต้ตอบ

นี่คือสคริปต์ที่ฉันทดสอบด้วย:

$ cat shorttest.sh 
#!/bin/bash
shopt -s lastpipe

exit_tests() {
    EXITMSG="$(cat /proc/self/fd/0)"
}

ls /bloop 2>&1 | exit_tests

echo "My output is \"$EXITMSG\""


$ bash shorttest.sh 
My output is "ls: cannot access '/bloop': No such file or directory"

สิ่งที่ฉันทำที่นี่คือ:

  1. shopt -s lastpipeตั้งค่าตัวเลือกเปลือก มันจะไม่ทำงานหากไม่มีสิ่งนี้เพราะคุณจะสูญเสียไฟล์ descriptor

  2. ทำให้แน่ใจว่า stderr ของฉันจะถูกจับด้วย 2>&1

  3. ไพพ์เอาต์พุตลงในฟังก์ชันเพื่อให้สามารถอ้างอิงไฟล์ stdin ได้

  4. ตั้งค่าตัวแปรโดยรับเนื้อหาของ /proc/self/fd/0file descriptor ซึ่งก็คือ stdin

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

shopt -s lastpipe

exit_tests() {
    MYSTUFF="$(cat /proc/self/fd/0)"
    BADLINE=$BASH_LINENO
}

error_msg () {
    echo -e "$0: line $BADLINE\n\t $MYSTUFF"
    exit 1
}

ls /bloop 2>&1 | exit_tests ; [[ "${PIPESTATUS[0]}" == "0" ]] || error_msg

ด้วยวิธีนี้ฉันสามารถเพิ่ม2>&1 | exit_tests ; [[ "${PIPESTATUS[0]}" == "0" ]] || error_msgเบื้องหลังทุกคำสั่งที่ฉันสนใจที่จะตรวจสอบ

ตอนนี้คุณสามารถเพลิดเพลินกับผลลัพธ์ของคุณ!


0

นี่ไม่ใช่วิธีทุบตีอย่างเคร่งครัด แต่คุณสามารถใช้ pip กับ sed เพื่อรับแถวสุดท้ายของเอาต์พุตคำสั่งก่อนหน้า

ก่อนอื่นให้ดูว่าฉันมีอะไรในโฟลเดอร์ "a"

rasjani@helruo-dhcp022206::~$ find a
a
a/foo
a/bar
a/bat
a/baz
rasjani@helruo-dhcp022206::~$ 

จากนั้นตัวอย่างของคุณที่มี ls และ cd จะเปลี่ยนไปเป็น sed & piping เป็นอย่างนี้:

rasjani@helruo-dhcp022206::~$ cd `find a |sed '$!d'`
rasjani@helruo-dhcp022206::~/a/baz$ pwd
/home/rasjani/a/baz
rasjani@helruo-dhcp022206::~/a/baz$

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


0

เชลล์ไม่มีสัญลักษณ์พิเศษเหมือน perl ที่เก็บผลลัพธ์ echo ของคำสั่งสุดท้าย

เรียนรู้การใช้สัญลักษณ์ไปป์กับ awk

find . | awk '{ print "FILE:" $0 }'

ในตัวอย่างด้านบนคุณสามารถทำได้:

find . -name "foo.txt" | awk '{ print "mv "$0" ~/bar/" | "sh" }'

0
find . -name foo.txt 1> tmpfile && mv `cat tmpfile` /path/to/some/dir/

เป็นอีกวิธีหนึ่งแม้ว่าจะสกปรก


หา - ชื่อ foo.txt 1> tmpfile && mv `cat tmpfile` path / to / some / dir && rm tmpfile
Kevin

0

ฉันพบว่าการจดจำไปป์เอาท์พุทของคำสั่งของฉันเป็นไฟล์ที่เฉพาะเจาะจงน่ารำคาญเล็กน้อยโซลูชันของฉันคือฟังก์ชั่นในของฉัน .bash_profileที่จับเอาท์พุทในไฟล์และส่งคืนผลลัพธ์เมื่อคุณต้องการ

ข้อดีของสิ่งนี้คือคุณไม่จำเป็นต้องรันคำสั่งทั้งหมดอีกครั้ง (เมื่อใช้findหรือคำสั่งที่รันเป็นเวลานานอื่น ๆ ที่อาจสำคัญ)

พอง่ายวางในของคุณ.bash_profile:

ต้นฉบับ

# catch stdin, pipe it to stdout and save to a file
catch () { cat - | tee /tmp/catch.out}
# print whatever output was saved to a file
res () { cat /tmp/catch.out }

การใช้

$ find . -name 'filename' | catch
/path/to/filename

$ res
/path/to/filename

ณ จุดนี้ฉันมักจะเพิ่ม | catchไปยังจุดสิ้นสุดของคำสั่งทั้งหมดของฉันเพราะไม่มีค่าใช้จ่ายในการทำและช่วยให้ฉันต้องรันคำสั่งอีกครั้งซึ่งใช้เวลานานกว่าจะเสร็จสิ้น

นอกจากนี้หากคุณต้องการเปิดเอาต์พุตไฟล์ในเท็กซ์เอดิเตอร์คุณสามารถทำได้:

# vim or whatever your favorite text editor is
$ vim <(res)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.