Minta xargs menggunakan alias alih-alih biner

13

Bash 4.2 pada CentOS 6.5:

Di saya, ~/.bash_profilesaya punya banyak alias, termasuk:

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

sehingga saya bisa mendapatkan penyorotan warna dan mencetak nomor garis secara otomatis saat berjalan grep. Jika saya menjalankan yang berikut ini, menyoroti berfungsi seperti yang diharapkan:

$ grep -Re 'regex_here' *.py

Namun, ketika saya menjalankan ini baru-baru ini:

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

hasilnya tidak disorot dan nomor baris tidak dicetak, memaksa saya untuk kembali dan secara eksplisit menambahkan -n --color=alwayske grepperintah.

  • Tidak xargsmembaca alias di lingkungan?
  • Jika tidak, adakah cara untuk membuatnya melakukannya?
MattDMo
sumber
T&J ini memiliki apa yang Anda inginkan.
psimon
@psimon benar, itu pada dasarnya mengatakan untuk melakukan apa yang sudah saya lakukan dalam solusi - saya harus secara manual memperluas alias dalam xargsperintah. Yang saya coba cari tahu adalah apakah ada cara agar saya dapat langsung memanggil alias saya xargs.
MattDMo
1
Sudahkah Anda mencoba export GREP_OPTIONS='-n --color=always'sebelum perintah xargs Anda?
selesai24
@ DougO'Neal terima kasih, itu berhasil! Saya akan menambahkan itu ke .bash_profile. Jangan ragu untuk menulis jawaban ...
MattDMo

Jawaban:

10

Alias ​​adalah internal ke shell di mana ia didefinisikan. Itu tidak terlihat oleh proses lain. Hal yang sama berlaku untuk fungsi shell. xargsadalah aplikasi terpisah, yang bukan shell, jadi tidak memiliki konsep alias atau fungsi.

Anda dapat membuat xargs meminta shell alih-alih memohon grepsecara langsung. Namun, menjalankan shell saja tidak cukup, Anda harus mendefinisikan alias di shell itu juga. Jika alias didefinisikan di Anda .bashrc, Anda dapat sumber file itu; namun ini mungkin tidak berfungsi saat Anda .bashrcmelakukan tugas lain yang tidak masuk akal di shell non-interaktif.

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

Waspadalah terhadap seluk-beluk kutipan bersarang saat mengetik regexp. Anda dapat menyederhanakan hidup Anda dengan melewatkan regexp sebagai parameter ke shell.

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

Anda dapat melakukan pencarian alias secara eksplisit. Maka xargsakan melihat grep -n --color=always.

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

Dalam zsh:

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

By the way, perhatikan bahwa find … | xargs … pecah pada nama file yang mengandung spasi (antara lain) . Anda dapat memperbaikinya dengan mengubah catatan yang tidak dibatasi:

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

atau dengan menggunakan -exec:

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

Alih-alih menelepon find, Anda dapat melakukan semuanya di dalam shell. Pola gumpal **/melintasi direktori secara rekursif. Di bash, Anda harus menjalankan shopt -s globstaruntuk mengaktifkan pola gumpalan ini terlebih dahulu.

grep regex_here **/*.py

Ini memiliki beberapa keterbatasan:

  • Jika banyak file cocok (atau jika memiliki jalur panjang), perintah tersebut mungkin gagal karena melebihi panjang baris perintah maksimum.
  • Dalam bash ≤4.2 (tetapi tidak dalam versi yang lebih baru, atau di ksh atau zsh), **/berulang menjadi tautan simbolis ke direktori.

Pendekatan lain adalah dengan menggunakan substitusi proses, seperti yang disarankan oleh MariusMatutiae .

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

Ini berguna ketika **/tidak berlaku: untuk findekspresi kompleks , atau dalam bash ≤4.2 ketika Anda tidak ingin berulang di bawah tautan simbolis. Perhatikan bahwa ini memecah nama file yang berisi spasi; solusinya adalah mengatur IFSdan menonaktifkan globbing , tapi itu mulai menjadi sedikit rumit:

(IFS=$'\n'; set -f; grep regex_here <(find . -name '*.py') )
Gilles 'SANGAT berhenti menjadi jahat'
sumber
terima kasih atas penjelasan yang jelas tentang mengapa alias tidak terlihat oleh proses lain
MattDMo
Seseorang juga dapat menggunakan proses substitusi, lihat jawaban saya.
MariusMatutiae
11

Menggunakan 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.
1.61803
sumber
Terima kasih untuk itu, saya tidak tahu tentang trik luar angkasa.
MattDMo
Np. Ini juga berguna dengan sudo...
1.61803
2

Silakan anggap ini sebagai demonstrasi dari pendekatan lain, yang tidak dapat saya temukan dalam pertanyaan SO terkait :

Anda dapat menulis fungsi pembungkus xargsyang memeriksa apakah argumen pertama adalah alias dan jika demikian, perluaslah.

Berikut ini adalah kode yang melakukan hal itu, tetapi sayangnya ia membutuhkan shell Z dan karenanya tidak menjalankan 1: 1 dengan bash (dan terus terang, saya tidak terbiasa dengan bash cukup untuk port itu):

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]}"
}

Bukti, bahwa itu bekerja:

zsh% alias grep = "grep -n" ´                           # termasuk nomor baris yang cocok
zsh% find foo -name "* .p *" | xargs grep -E test
foo / bar.p0: 151: # data = test
foo / bar.p1: 122: # data = test # nomor baris yang disertakan
zsh% unalias grep 
zsh% find foo -name "* .p *" | xargs grep -E test
foo / bar.p0: # data = test
foo / bar.p1: # data = test # nomor baris tidak termasuk
zsh% 
mpy
sumber
1

Solusi yang lebih sederhana, dan lebih elegan, adalah dengan menggunakan substitusi proses :

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

Itu tidak membuat shell baru seperti pipa, yang berarti Anda masih di shell asli Anda di mana alias didefinisikan, dan output persis seperti yang Anda inginkan.

Berhati-hatilah untuk tidak meninggalkan ruang antara pengalihan dan tanda kurung, jika tidak bash akan membuat kesalahan. Sejauh yang saya ketahui, proses substitusi didukung oleh Bash, Zsh, Ksh {88,93}, tetapi tidak oleh pdksh (saya diberitahu bahwa seharusnya belum ).

MariusMatutiae
sumber
Saya pikir pengembangan pdksh sudah mati. Mksh kurang lebih adalah proyek penerus - “ternyata cukup sulit (konsep parsing dilakukan di kepala tg @)” .
Gilles 'SANGAT berhenti menjadi jahat'
Substitusi proses adalah metode yang baik untuk findperintah yang kompleks , meskipun Anda harus berhati-hati bahwa itu akan merusak ruang, dan itu tidak dapat diperbaiki semudah yang find | xargsbisa dilakukan (dengan beralih ke -print0dan -0, atau menggunakan -exec). Ketika berlaku, **/lebih sederhana dan lebih kuat.
Gilles 'SANGAT berhenti menjadi jahat'
0

grep akan membaca serangkaian opsi default dari variabel lingkungan GREP_OPTIONS. Jika Anda mau

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

di .bashrc Anda maka variabel akan diteruskan ke subshell dan Anda akan mendapatkan hasil yang Anda harapkan.

selesai24
sumber
Tapi jangan dimasukkan --line-numberatau --color=alwaysdi GREP_OPTIONSkecuali itu hanya untuk satu perintah, ini akan mematahkan banyak script. --color=autoboleh saja ada di sana, dan itu saja. Menempatkan baris ini di Anda .bashrcakan merusak banyak hal.
Gilles 'SANGAT berhenti menjadi jahat'
@Gilles Mengatur alias atau mengabaikan opsi default untuk perintah apa pun untuk akun root adalah hal yang buruk. Menetapkan opsi ini untuk akun pengguna tidak mungkin menyebabkan banyak masalah. Saya tidak dapat membuat skrip pengguna yang bermasalah.
selesai24
Hampir semua skrip yang menggunakan grep dengan cara yang melampaui pengujian keberadaan suatu kejadian akan rusak. Sebagai contoh, dari /etc/init.d/cronpada sistem saya: value=`egrep "^${var}=" "$ENV_FILE" | tail -n1 | cut -d= -f2` . Atau dari /usr/bin/pdfjam: pdftitl=`printf "%s" "$PDFinfo" | grep -e … | sed -e …` . Alias ​​bukan masalah karena tidak terlihat di skrip.
Gilles 'SANGAT berhenti menjadi jahat'
@Gilles Saya tahu banyak skrip seperti ini. Yang saya tidak bisa berkeliling umumnya dijalankan hanya oleh root (seperti /etc/init.d/cron). Secara pribadi, saya tidak memiliki alias apa pun yang didefinisikan pada akun pengguna saya atau saya tidak menetapkan opsi dalam file rc atau melalui variabel lingkungan untuk menimpa perilaku perintah standar. Saya lebih suka prediktabilitas daripada kenyamanan.
selesai24
Alias ​​tidak mematahkan prediktabilitas karena tidak terlihat oleh skrip. Pengaturan GREP_OPTIONSjeda diprediksi sangat buruk, kecuali untuk beberapa opsi seperti --color=auto(yang dirancang untuk apa).
Gilles 'SO- stop being evil'