bash: cara terpendek untuk mendapatkan kolom keluaran ke-n

165

Katakanlah bahwa selama hari kerja Anda, Anda berulang kali menemukan bentuk output yang dikolomisasi dari beberapa perintah berikut di bash (dalam kasus saya dari mengeksekusi svn stdi direktori kerja Rails saya):

?       changes.patch
M       app/models/superman.rb
A       app/models/superwoman.rb

untuk bekerja dengan output dari perintah Anda - dalam hal ini nama file - semacam penguraian diperlukan sehingga kolom kedua dapat digunakan sebagai input untuk perintah berikutnya.

Apa yang saya lakukan adalah menggunakan awkuntuk mendapatkan di kolom kedua, misalnya ketika saya ingin menghapus semua file (bukan itu usecase khas :), saya akan melakukan:

svn st | awk '{print $2}' | xargs rm

Karena saya sering mengetik ini, pertanyaan alami adalah: apakah ada cara yang lebih pendek (dengan demikian lebih dingin) untuk mencapai ini di bash?

CATATAN: Apa yang saya tanyakan pada dasarnya adalah pertanyaan perintah shell meskipun contoh konkret saya ada di alur kerja svn saya. Jika Anda merasa alur kerja itu konyol dan menyarankan pendekatan alternatif, saya mungkin tidak akan memilih Anda, tetapi yang lain mungkin, karena pertanyaan di sini adalah benar-benar bagaimana cara mendapatkan output perintah kolom ke-n di bash, dengan cara sesingkat mungkin . Terima kasih :)

Sv1
sumber
1
Ketika Anda menggunakan perintah sering kali Anda lebih baik membuat skrip dan meletakkannya di jalur Anda. Anda cukup membuat fungsi di bashrc Anda jika lebih suka. Saya tidak melihat titik mengurangi ekspresi pemilihan kolom.
Lynch
1
Anda benar, dan saya mungkin melakukannya. 'Poin' adalah pencarian cara-cara baru untuk melakukan hal-hal di bash, untuk tujuan belajar tetapi sebagian besar untuk bersenang-senang :)
Sv1
1
Anda juga tidak memiliki .bashrc saat ssh-ing di suatu tempat, jadi sangat berguna untuk mengetahui jalan keluar tanpa itu.
Kos

Jawaban:

125

Anda dapat menggunakan cutuntuk mengakses bidang kedua:

cut -f2

Sunting: Maaf, tidak menyadari bahwa SVN tidak menggunakan tab dalam outputnya, jadi itu agak tidak berguna. Anda dapat menyesuaikan cutdengan output tetapi agak rapuh - sesuatu seperti cut -c 10- akan berfungsi, tetapi nilai pastinya akan tergantung pada pengaturan Anda.

Pilihan lain adalah sesuatu seperti: sed 's/.\s\+//'

porges
sumber
1
Coba gunakan cut -f2dengan svn stoutput. Anda akan melihatnya tidak berfungsi.
Lynch
Saya berasumsi bahwa real svn stakan menggunakan tab dalam outputnya. Setelah beberapa pengujian, sepertinya ini bukan masalahnya. Sial.
porges
21
Melewati pembatas yang sesuai untuk -dmemungkinkan ini bekerja.
Yogh
6
Untuk memperluas apa yang dikatakan @Yogh, untuk spasi sebagai pembatas, akan terlihat seperti cut -d" " -f2.
adamyonk
W / o awk saat stdout gunakan xargs: svn st | xargs | cut -d "" -f2
vr286
106

Untuk mencapai hal yang sama seperti:

svn st | awk '{print $2}' | xargs rm

hanya menggunakan bash yang dapat Anda gunakan:

svn st | while read a b; do rm "$b"; done

Memang, ini tidak lebih pendek, tetapi sedikit lebih efisien dan menangani spasi putih di nama file Anda dengan benar.

Rick Sladkey
sumber
Apa itu adan b, bagaimana mendapatkannya?
Timo
1
@Timo amewakili kolom pertama dan bmewakili kolom lainnya. Jika Anda ingin mencetak kolom kedua, gunakan read a b c;lalu gunakan echosebagai ganti rm. Saya menggunakan ini untuk mendapatkan banyak proses ID yang mengikuti pola grep, jadi saya bisa menginterupsi semuanya.
Matt Kleinsmith
36

Saya menemukan diri saya dalam situasi yang sama dan akhirnya menambahkan alias ini ke .profilefile saya :

alias c1="awk '{print \$1}'"
alias c2="awk '{print \$2}'"
alias c3="awk '{print \$3}'"
alias c4="awk '{print \$4}'"
alias c5="awk '{print \$5}'"
alias c6="awk '{print \$6}'"
alias c7="awk '{print \$7}'"
alias c8="awk '{print \$8}'"
alias c9="awk '{print \$9}'"

Yang memungkinkan saya untuk menulis hal-hal seperti ini:

svn st | c2 | xargs rm
StackedCrooked
sumber
15
Fungsi Bash biasanya lebih bermanfaat. Saya melakukan ini: function c() { awk "{print \$$1}" } Maka Anda dapat melakukan: svn st | c 2 | xargs rm
Aissen
8
Tapi kemudian saya perlu mengetikkan ruang ekstra. Itu terlalu melelahkan :)
StackedCrooked
16

Coba zsh. Ini mendukung alias suffix, jadi Anda dapat mendefinisikan X di .zshrc menjadi

alias -g X="| cut -d' ' -f2"

maka kamu bisa melakukan:

cat file X

Anda bisa melangkah lebih jauh dan mendefinisikannya untuk kolom ke-n:

alias -g X2="| cut -d' ' -f2"
alias -g X1="| cut -d' ' -f1"
alias -g X3="| cut -d' ' -f3"

yang akan menampilkan kolom ke-n dari file "file". Anda dapat melakukan ini untuk keluaran grep atau keluaran lebih sedikit juga. Ini sangat berguna dan fitur pembunuh dari zsh.

Anda dapat melangkah lebih jauh dan mendefinisikan D menjadi:

alias -g D="|xargs rm"

Sekarang Anda dapat mengetik:

cat file X1 D

untuk menghapus semua file yang disebutkan di kolom pertama file "file".

Jika Anda tahu bash, zsh tidak banyak berubah kecuali untuk beberapa fitur baru.

HTH Chris

Chris
sumber
ahh, saya melihat Anda telah memperbarui jawaban Anda ketika saya mengetik komentar saya di atas :) Dapatkah Anda entah bagaimana secara dinamis menentukan kolom mana yang akan didapat, atau apakah saya perlu n-baris di .zshrc saya (bukan yang penting, hanya ingin tahu)
Sv1
Saya telah mengedit posting saya lebih lanjut dan mendefinisikan akhiran "D" untuk menghapus file. Sejauh yang saya tahu Anda harus menambahkan baris untuk setiap sufiks.
Chris
Atau menjadikannya parameter pertama dari perintah X. Tidak satu pun dari ini memerlukan zsh atau alias, kecuali mungkin untuk ide aneh untuk meletakkan pipa di alias.
tripleee
1
Saya tidak mengerti mengapa Anda semua sepertinya menyukai cutopsi ini. Itu tidak bekerja pada mesin saya menggunakan output dari svn st. Coba svn st | cut -d' ' -f2lihat apa yang terjadi.
Lynch
@ Sv1 Anda bisa menulis loop untuk mendefinisikan alias, karena alias hanyalah sebuah perintah.
driftcatcher
8

Karena Anda sepertinya tidak terbiasa dengan skrip, berikut ini contohnya.

#!/bin/sh
# usage: svn st | x 2 | xargs rm
col=$1
shift
awk -v col="$col" '{print $col}' "${@--}"

Jika Anda menyimpan ini ~/bin/xdan pastikan ~/binada di Anda PATH(sekarang adalah sesuatu yang Anda bisa dan harus dimasukkan ke dalam Anda .bashrc), Anda memiliki perintah sesingkat mungkin untuk umumnya mengekstraksi kolom n; x n.

Script harus melakukan pengecekan kesalahan dan jaminan yang tepat jika dipanggil dengan argumen non-numerik atau jumlah argumen yang salah, dll; tetapi memperluas pada versi esensial tanpa tulang ini akan ada di unit 102.

Mungkin Anda ingin memperluas skrip untuk memungkinkan pembatas kolom yang berbeda. Awk secara default mem-parsing input ke bidang di whitespace; menggunakan pembatas yang berbeda, menggunakan -F ':'mana :adalah pembatas baru. Menerapkan ini sebagai opsi untuk skrip membuatnya sedikit lebih lama, jadi saya meninggalkan itu sebagai latihan untuk pembaca.


Pemakaian

Diberikan file file:

1 2 3
4 5 6

Anda bisa meneruskannya melalui stdin (menggunakan yang tidak bergunacat hanya sebagai pengganti untuk sesuatu yang lebih berguna);

$ cat file | sh script.sh 2
2
5

Atau berikan sebagai argumen untuk skrip:

$ sh script.sh 2 file
2
5

Di sini, sh script.shdiasumsikan bahwa skrip disimpan seperti script.shdalam direktori saat ini; jika Anda menyimpannya dengan nama yang lebih berguna di suatu tempat di Anda PATHdan tandai itu dapat dieksekusi, seperti dalam instruksi di atas, jelas gunakan nama yang berguna sebagai gantinya (dan tidak ada sh).

tripleee
sumber
Ide bagus, tetapi tidak cukup bekerja untuk saya seperti pada sh atau bash. Ini berfungsi: #! / Bin / bash col = $ 1 shift awk "{print \ $$ col}"
mgk
Terima kasih atas komentarnya yang terlambat ini. Diperbarui dengan perbaikan Anda.
tripleee
1
lebih baikawk -v col=$col ...
fedorqui 'SO stop harming'
@ fedorqui Terima kasih atas tanggapan Anda, di sini dan di tempat lain! Diperbarui jawabannya. Sekarang sedikit lebih lama sehingga jika Anda ingin skrip terpendek yang dapat Anda miliki, lihat riwayat revisi untuk versi aslinya.
tripleee
1
@ fedorqui Terima kasih banyak! Menambahkan peringatan kecil pada catcontoh untuk alasan histeris (-:
tripleee
5

Sepertinya Anda sudah punya solusi. Untuk mempermudah, mengapa tidak hanya menempatkan perintah Anda dalam skrip bash (dengan nama pendek) dan jalankan saja alih-alih mengetikkan perintah 'panjang' itu setiap waktu?

Tarek Fadel
sumber
Hanya saja itu tidak sejuk menurut saya. Mengapa? Yah saya tidak mau harus membanjiri .ashrc saya dengan berbagai cara pintas yang melakukan ini dan itu, pada dasarnya menulis sebuah meta-shell. Itu cukup alias apa adanya. Karena itu, saran Anda tidak buruk.
Sv1
2
.bashrc? Mengapa Anda mengacaukannya dengan hal-hal yang tidak terkait dengan bash. Apa yang saya lakukan adalah saya menulis skrip individual untuk setiap 'set' perintah dan menempatkan semuanya dalam ~/scriptsdirektori. Kemudian tambahkan seluruh ~/scriptske saya PATHsehingga saya bisa memanggil mereka dengan nama saat saya membutuhkannya.
Tarek Fadel
4
Maka jangan taruh di .bashrc Anda. Buat skrip di ~ / bin dan pastikan skrip itu ada di PATH Anda.
Tripleee
2

Jika Anda ok dengan memilih kolom secara manual, Anda bisa sangat cepat menggunakan pick :

svn st | pick | xargs rm

Pergi saja ke sel mana saja dari kolom ke-2, tekan clalu tekanenter

Bernardo Rufino
sumber
Saya mencoba alat "pilih" yang Anda referensikan, dan saya sangat menyukainya. Ini sangat baik untuk banyak situasi di mana saya ingin mencetak kolom yang dipilih tetapi tidak ingin mengetikkan "awk '{print $ 3}'" hanya untuk mendapatkan kolom yang diinginkan. Sayangnya nama tersebut bertentangan dengan alat "pilih" yang berbeda yang tampaknya lebih populer - itu dapat diinstal dengan "apt install pick". Jadi saya mengganti nama alat Anda menjadi "pickk" pada sistem saya, dan berniat untuk terus menggunakannya. Terima kasih telah mengirim referensi.
William Dye
1

Catatan, path file itu tidak harus di kolom kedua dari output svn st. Misalnya jika Anda memodifikasi file, dan memodifikasi propertinya, itu akan menjadi kolom ke-3.

Lihat contoh output yang mungkin di:

svn help st

Contoh output:

 M     wc/bar.c
A  +   wc/qax.c

Saya sarankan untuk memotong 8 karakter pertama dengan:

svn st | cut -c8- | while read FILE; do echo whatever with "$FILE"; done

Jika Anda ingin 100% yakin, dan berurusan dengan nama file mewah dengan spasi putih di akhir misalnya, Anda perlu mengurai output xml:

svn st --xml | grep -o 'path=".*"' | sed 's/^path="//; s/"$//'

Tentu saja Anda mungkin ingin menggunakan parser XML nyata, bukan grep / sed.

Michał Šrajer
sumber