มี xargs ใช้นามแฝงแทนไบนารี


14

Bash 4.2 บน CentOS 6.5:

ในตัวฉัน~/.bash_profileฉันมีนามแฝงมากมายรวมไปถึง:

alias grep='grep -n --color=always'

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

$ grep -Re 'regex_here' *.py

อย่างไรก็ตามเมื่อฉันวิ่งเร็ว ๆ นี้:

$ find . -name '*.py' | xargs grep -E 'regex_here'

ผลที่ไม่ได้เน้นและหมายเลขบรรทัดไม่ถูกพิมพ์, บังคับให้ฉันกลับไปและเพิ่มอย่างชัดเจน-n --color=alwaysกับgrepคำสั่ง

  • ไม่xargsอ่านนามแฝงในสภาพแวดล้อมหรือไม่?
  • ถ้าไม่มีมีวิธีที่จะทำเช่นนั้นหรือไม่?

คำถาม & คำตอบนี้มีสิ่งที่คุณต้องการ
psimon

@psimon ถูกต้องเป็นหลักว่าจะทำสิ่งที่ฉันได้ทำในการแก้ปัญหาของฉัน - ฉันต้องขยายนามแฝงด้วยตนเองในxargsคำสั่ง xargsสิ่งที่ฉันพยายามที่จะพบคือว่ามีวิธีที่ผมโดยตรงสามารถเรียกนามแฝงของฉันจาก
MattDMo

1
คุณเคยลองexport GREP_OPTIONS='-n --color=always'ก่อนคำสั่ง xargs ของคุณหรือไม่?
doneal24 24

@ DougO'Neal ขอบคุณที่ได้ผล! .bash_profileฉันจะเพิ่มที่ของฉัน อย่าลังเลที่จะเขียนคำตอบ ...
MattDMo

คำตอบ:


11

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

คุณสามารถทำให้ xargs เรียกใช้เชลล์แทนการเรียกใช้grepโดยตรง อย่างไรก็ตามการเรียกใช้เชลล์ไม่เพียงพอคุณต้องกำหนดนามแฝงในเชลล์นั้นด้วย หากนามแฝงถูกกำหนดในของ.bashrcคุณคุณสามารถแหล่งไฟล์นั้น อย่างไรก็ตามสิ่งนี้อาจไม่ทำงานของคุณ.bashrcทำงานอื่น ๆ ที่ไม่สมเหตุสมผลในเชลล์ที่ไม่มีการโต้ตอบ

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E regex_here "$@"' _

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

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E "$0" "$@"' regex_here

คุณสามารถทำการค้นหานามแฝงได้อย่างชัดเจน แล้วจะเห็นxargsgrep -n --color=always

find . -name '*.py' | xargs "${BASH_ALIASES[grep]}" regex_here

ใน zsh:

find . -name '*.py' | xargs $aliases[grep] regex_here

โดยวิธีการที่ทราบว่าfind … | xargs … การแบ่งในชื่อไฟล์ที่มีช่องว่าง (อื่น) คุณสามารถแก้ไขได้โดยเปลี่ยนเป็นเรคคอร์ดที่มีการคั่นด้วย null:

find . -name '*.py' -print0 | xargs -0 "${BASH_ALIASES[grep]}" regex_here

หรือโดยการใช้-exec:

find . -name '*.py' -exec "${BASH_ALIASES[grep]}" regex_here {} +

แทนที่จะโทรfindคุณสามารถทำทุกอย่างภายในเชลล์ได้ รูปแบบ glob **/สำรวจภายในไดเรกทอรีซ้ำ ๆ ใน bash คุณต้องshopt -s globstarเปิดใช้งานเพื่อเปิดใช้งานรูปแบบ glob นี้ก่อน

grep regex_here **/*.py

ข้อ จำกัด บางประการ:

  • หากไฟล์จำนวนมากตรงกัน (หรือหากไฟล์นั้นมีพา ธ ยาว) คำสั่งอาจล้มเหลวเนื่องจากเกินความยาวบรรทัดคำสั่งสูงสุด
  • ใน bash ≤4.2 (แต่ไม่ใช่ในเวอร์ชันที่ใหม่กว่าหรือใน ksh หรือ zsh) **/ให้เรียกใช้ลิงก์สัญลักษณ์ไปยังไดเรกทอรีอีกครั้ง

อีกวิธีหนึ่งคือการใช้การทดแทนกระบวนการตามที่ MariusMatutiaeแนะนำ

grep regex_here <(find . -name '*.py')

สิ่งนี้มีประโยชน์เมื่อ**/ไม่สามารถใช้ได้: สำหรับfindนิพจน์ที่ซับซ้อนหรือใน bash ≤4.2เมื่อคุณไม่ต้องการเรียกเก็บเงินภายใต้ลิงก์สัญลักษณ์ โปรดทราบว่าสิ่งนี้จะแบ่งในชื่อไฟล์ที่มีช่องว่าง วิธีแก้ปัญหาคือการตั้งค่าIFSและปิดใช้งานการวนรอบแต่มันเริ่มที่จะซับซ้อนเล็กน้อย:

(IFS=$'\n'; set -f; grep regex_here <(find . -name '*.py') )

ขอบคุณสำหรับคำอธิบายที่ชัดเจนว่าเหตุใดนามแฝงถึงไม่สามารถมองเห็นได้ในกระบวนการอื่น ๆ
MattDMo

หนึ่งอาจใช้ทดแทนกระบวนการดูคำตอบของฉัน
MariusMatutiae

11

ใช้ alias xargs='xargs '

alias: alias [-p] [name[=value] ... ]
(snip)
A trailing space in VALUE causes the next word to be checked for
alias substitution when the alias is expanded.

ขอบคุณที่ฉันไม่ทราบเกี่ยวกับเคล็ดลับพื้นที่ต่อท้าย
MattDMo

Np นอกจากนี้ยังมีประโยชน์กับsudo...
1.61803

2

โปรดใช้สิ่งนี้เป็นการสาธิตวิธีการอื่นซึ่งฉันไม่พบในคำถาม SOที่เกี่ยวข้อง :

คุณสามารถเขียนฟังก์ชั่น wrapper xargsซึ่งตรวจสอบว่าอาร์กิวเมนต์แรกเป็นนามแฝงหรือไม่ถ้าขยายให้เหมาะสม

นี่คือรหัสที่ทำตรงนั้น แต่น่าเสียดายที่มันต้องใช้เชลล์ Zและด้วยเหตุนี้จึงไม่รัน 1: 1 ด้วย bash (และตรงไปตรงมาฉันไม่คุ้นเคยกับการทุบพอที่จะพอร์ตมัน):

xargs () {
        local expandalias
        if [[ $(which $1) =~ "alias" ]]; then
                expandalias=$(builtin alias $1) 
                expandalias="${${(s.'.)expandalias}[2]}"
        else
                expandalias=$1
        fi
        command xargs ${(z)expandalias} "${(z)@[2,-1]}"
}

พิสูจน์ว่ามันใช้งานได้:

zsh% alias grep = "grep -n" ´                           # รวมถึงจำนวนบรรทัดที่ตรงกัน
zsh% ค้นหา foo -name "* .p *" | xargs การทดสอบ grep -E
foo / bar.p0: 151: # ข้อมูล = ทดสอบ
foo / bar.p1: 122: # data = test # รวมหมายเลขบรรทัด
zsh% unalias grep 
zsh% ค้นหา foo -name "* .p *" | xargs การทดสอบ grep -E
foo / bar.p0: # ข้อมูล = ทดสอบ
foo / bar.p1: # data = test # หมายเลขบรรทัดไม่รวมอยู่
% zsh 

1

วิธีที่ง่ายกว่าและสวยงามกว่าคือใช้การทดแทนกระบวนการ :

grep -E 'regex_here' <( find . -name '*.py')

มันไม่ได้สร้างเชลล์ใหม่เหมือนที่ไพพ์ทำซึ่งหมายความว่าคุณยังคงอยู่ในเชลล์เดิมที่กำหนดนามแฝงและเอาต์พุตเป็นสิ่งที่คุณต้องการ

เพียงระมัดระวังที่จะไม่เว้นช่องว่างระหว่างการเปลี่ยนเส้นทางและวงเล็บมิฉะนั้นทุบตีจะทำให้เกิดข้อผิดพลาด ที่ดีที่สุดของความรู้ของฉันเปลี่ยนตัวกระบวนการได้รับการสนับสนุนโดยการทุบตี zsh, Ksh {88,93} แต่ไม่ pdksh (ผมบอกว่าควรจะเป็นยังไม่ได้ )


ฉันคิดว่าการพัฒนา pdksh นั้นตายแล้ว Mksh จะมากหรือน้อยโครงการทายาท - “สวยยากก็จะเปิดออก (แยกแนวคิดที่จะทำใน tg @ 'หัว)”
Gilles 'หยุดความชั่วร้าย'

กระบวนการเปลี่ยนตัวเป็นวิธีที่ดีสำหรับซับซ้อนfindคำสั่ง แต่คุณต้องระวังว่ามันจะทำลายในช่องว่างและที่ไม่สามารถแก้ไขได้อย่างง่ายดายเช่นfind | xargsสามารถ (โดยการเปลี่ยนไป-print0และ-0หรือใช้-exec) เมื่อใช้งาน**/ได้ง่ายกว่าและแข็งแกร่งกว่า
Gilles 'หยุดความชั่วร้าย'

0

grep จะอ่านชุดตัวเลือกเริ่มต้นจากตัวแปรสภาพแวดล้อม GREP_OPTIONS ถ้าคุณจะใส่

 export GREP_OPTIONS='--line-number --color=always'

ใน. bashrc ของคุณจากนั้นตัวแปรจะถูกส่งต่อไปยัง subshells และคุณจะได้รับผลลัพธ์ตามที่คุณคาดหวัง


แต่อย่าใส่--line-numberหรือใส่--color=alwaysเข้าไปGREP_OPTIONSเว้นแต่ว่าจะเป็นเพียงคำสั่งเดียวสิ่งนี้จะทำให้สคริปต์เสียหายมาก --color=autoก็โอเคที่จะมีและนั่นคือทั้งหมดที่เกี่ยวกับ การวางสายนี้ไว้ในความ.bashrcตั้งใจของคุณจะทำลายของหลายอย่าง
Gilles 'หยุดความชั่วร้าย'

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

สคริปต์ใด ๆ ที่ใช้ grep ในลักษณะที่นอกเหนือไปจากการทดสอบว่ามีเหตุการณ์เกิดขึ้นจะแตกหัก ยกตัวอย่างเช่นจากในระบบของฉัน:/etc/init.d/cron value=`egrep "^${var}=" "$ENV_FILE" | tail -n1 | cut -d= -f2` หรือจาก:/usr/bin/pdfjam pdftitl=`printf "%s" "$PDFinfo" | grep -e … | sed -e …` นามแฝงไม่ใช่ปัญหาเนื่องจากไม่พบในสคริปต์
Gilles 'หยุดความชั่วร้าย'

@Gilles ฉันรู้สคริปต์มากมายเช่นนี้ โดยทั่วไปแล้วสิ่งที่ฉันไม่สามารถเข้าถึงได้จะถูกเรียกใช้โดยรูทเท่านั้น (เช่น /etc/init.d/cron) โดยส่วนตัวฉันไม่มีนามแฝงใด ๆ ที่กำหนดไว้ในบัญชีผู้ใช้ของฉันและฉันจะไม่ตั้งค่าตัวเลือกในไฟล์ rc หรือผ่านตัวแปรสภาพแวดล้อมเพื่อแทนที่พฤติกรรมเริ่มต้นของคำสั่ง ฉันชอบการคาดการณ์มากกว่าความสะดวกสบาย
doneal24

ชื่อแทนจะไม่ทำลายความสามารถในการคาดการณ์เนื่องจากสคริปต์ไม่เห็น การตั้งค่าGREP_OPTIONSแบ่งการคาดการณ์ไม่ดีมากยกเว้นตัวเลือกน้อยเช่น--color=auto(ซึ่งเป็นสิ่งที่มันถูกออกแบบมาสำหรับ)
Gilles 'หยุดความชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.