Kiat umum apa yang Anda miliki untuk bermain golf di Bash? Saya mencari ide yang dapat diterapkan pada masalah kode golf secara umum yang setidaknya agak spesifik untuk Bash (mis. "Hapus komentar" bukan jawaban). Silakan kirim satu tip per jawaban.
55
sh
dan shell apa yang memungkinkanfor
sintaks ini ? secara tegas diizinkan masukzsh
.sh
dan bahwa Bash juga memperbolehkannya karena itu, walaupun sayangnya saya tidak memiliki kutipan.csh
, mungkin - itulah cara mereka bekerja di shell itu.ksh93
hal di atas bisa menjadi;{1..10}
bash
printf %s\\n {1..10}
for((;i++<10)){ echo $i;}
lebih pendek darifor i in {1..10};{ echo $i;}
Untuk penggunaan ekspansi aritmatika
$[…]
alih-alih$((…))
:Dalam ekspansi aritmatika jangan gunakan
$
:Ekspansi aritmatika dilakukan pada subskripsi array yang diindeks, jadi jangan gunakan
$
keduanya di sana:Dalam ekspansi aritmatika jangan gunakan
${…}
:sumber
while((i--))
, yang berfungsi, denganwhile[i--]
atauwhile $[i--]
tidak berfungsi untuk saya. GNU bash, versi 4.3.46 (1)y=`bc<<<"($x*2.2)%10/1"`
... contoh penggunaanbc
untuk perhitungan non-integer ... catat/1
pada bagian akhir memotong desimal yang dihasilkan ke int.s=$((i%2>0?s+x:s+y))
... contoh penggunaan operator ternary dalam aritmatika bash. Lebih pendek dariif..then..else
atau[ ] && ||
GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)
dan itu bukan milik saya.Cara normal, panjang dan membosankan untuk mendefinisikan suatu fungsi adalah
Seperti yang diketahui orang ini , Anda benar-benar membutuhkan ruang sebelum
CODE
dan titik koma setelahnya.Ini adalah sedikit trik yang saya pelajari dari @DigitalTrauma :
Itu dua karakter lebih pendek dan berfungsi dengan baik, asalkan Anda tidak perlu membawa perubahan dalam nilai variabel setelah fungsi kembali ( tanda kurung menjalankan tubuh dalam subkulit ).
Sebagaimana @ jimmy23013 tunjukkan dalam komentar, bahkan tanda kurung mungkin tidak diperlukan.
The Bash Reference Manual menunjukkan bahwa fungsi dapat didefinisikan sebagai berikut:
Sebuah perintah majemuk dapat:
until
,while
ataufor
if
,case
,((...))
atau[[...]]
(...)
atau{...}
Itu berarti semua hal berikut ini valid:
Dan saya telah menggunakan kurung keriting seperti pengisap ...
sumber
f()while ...
f()if ...
dan perintah majemuk lainnya.f()CODE
itu legal. Ternyata ituf()echo hi
legal di pdksh dan zsh, tetapi tidak di bash.for
karena defaultnya adalah posisi:f()for x do : $x; done;set -x *;PS4=$* f "$@"
atau sesuatu.:
adalah perintah yang tidak melakukan apa-apa, status keluarnya selalu berhasil, sehingga dapat digunakan sebagai gantitrue
.sumber
:(){:|:}
Lebih banyak tips
Menyalahgunakan operator ternary,
((test)) && cmd1 || cmd2
atau[ test ] && cmd1 || cmd2
, sebanyak mungkin.Contoh (jumlah panjang selalu mengecualikan baris teratas):
Dengan menggunakan operator ternary saja, ini dapat dengan mudah disingkat menjadi:
Seperti yang nyuszika7h tunjukkan dalam komentar, contoh khusus ini dapat dipersingkat lebih jauh dengan menggunakan
case
:Juga, lebih memilih kurung ke kawat gigi sebanyak mungkin. Karena tanda kurung adalah metacharacter, dan bukan kata, mereka tidak pernah membutuhkan spasi dalam konteks apa pun. Ini juga berarti menjalankan sebanyak mungkin perintah dalam subkulit mungkin, karena kurung kurawal (mis
{
dan}
) adalah kata-kata yang dicadangkan, bukan meta-karakter, dan dengan demikian harus memiliki spasi putih di kedua sisi untuk mengurai dengan benar, tetapi meta-karakter tidak. Saya berasumsi bahwa Anda tahu sekarang bahwa subkulit tidak mempengaruhi lingkungan induk, jadi dengan asumsi bahwa semua perintah contoh dapat dijalankan dengan aman dalam subkulit (yang tidak khas dalam kasus apa pun), Anda dapat mempersingkat kode di atas untuk ini :Juga, jika Anda tidak bisa, menggunakan tanda kurung masih dapat mengurangi beberapa. Satu hal yang perlu diingat adalah bahwa itu hanya berfungsi untuk bilangan bulat, yang menjadikannya tidak berguna untuk keperluan contoh ini (tetapi jauh lebih baik daripada menggunakan
-eq
untuk bilangan bulat).Satu hal lagi, hindari kutipan jika memungkinkan. Dengan menggunakan saran di atas, Anda dapat meminimalkannya lebih lanjut. Contoh:
Dalam kondisi pengujian, pilih kurung tunggal untuk kurung ganda sebanyak mungkin dengan beberapa pengecualian. Ini menjatuhkan dua karakter secara gratis, tetapi dalam beberapa kasus tidak sekuat itu (itu adalah ekstensi Bash - lihat di bawah untuk contoh). Juga, gunakan argumen sama dengan tunggal daripada ganda. Ini adalah karakter bebas untuk dijatuhkan.
Perhatikan peringatan ini, terutama dalam memeriksa output nol atau variabel yang tidak ditentukan:
Dalam semua teknis, contoh khusus ini akan lebih baik dengan
case
...in
:Jadi, moral dari postingan ini adalah ini:
if
/if-else
/ etc. membangun.case
...in
, karena dapat menyimpan beberapa byte, terutama dalam pencocokan string.PS: Berikut adalah daftar karakter meta yang dikenali di Bash terlepas dari konteksnya (dan dapat memisahkan kata-kata):
EDIT: Seperti yang ditunjukkan manatwork, tes kurung ganda hanya berfungsi untuk bilangan bulat. Juga, secara tidak langsung, saya menemukan bahwa Anda perlu memiliki spasi putih di sekitar
==
operator. Memperbaiki posting saya di atas.Saya juga terlalu malas untuk menghitung ulang panjang setiap segmen, jadi saya cukup menghapusnya. Seharusnya cukup mudah untuk menemukan kalkulator panjang string daring jika perlu.
sumber
[ $t=="hi" ]
akan selalu mengevaluasi ke 0, seperti yang diuraikan sebagai[ -n "STRING" ]
.(($t=="hi"))
akan selalu mengevaluasi ke 0 selama $ t memiliki nilai non-numerik, karena string dipaksa menjadi bilangan bulat dalam evaluasi aritmatika. Beberapa test case: pastebin.com/WefDzWbLcase
akan lebih pendek di sini. Juga, Anda tidak perlu ruang sebelumnya}
, tetapi Anda lakukan setelah{
.=
tidak sekuat itu==
?=
diamanatkan oleh POSIX,==
bukan.Alih-alih
grep -E
,grep -F
,grep -r
, penggunaanegrep
,fgrep
,rgrep
, tabungan dua karakter. Yang lebih pendek sudah usang tetapi berfungsi dengan baik.(Anda memang meminta satu tip per jawaban!)
sumber
Pgrep
untukgrep -P
. Meskipun saya melihat bagaimana hal itu dapat dengan mudah dikacaukanpgrep
, yang digunakan untuk mencari proses.grep -o
banyakElemen 0 dari suatu array dapat diakses dengan nama variabel saja, penghematan lima byte lebih dari yang secara eksplisit menentukan indeks 0:
sumber
Jika Anda perlu meneruskan konten variabel ke STDIN dari proses selanjutnya dalam sebuah pipeline, itu biasa untuk menggemakan variabel ke dalam pipeline. Tetapi Anda dapat mencapai hal yang sama dengan
<<<
string bash di sini :sumber
s=code\ golf
,echo $s|
dan<<<$s
(mengingat bahwa dua terakhir bekerja hanya karena ada ruang ada diulang, dll).Hindari
$( ...command... )
, ada alternatif yang menyimpan satu char dan melakukan hal yang sama:sumber
$( )
diperlukan jika Anda memiliki pengganti perintah bersarang; jika tidak, Anda harus melarikan diri dari dalam``
$()
ketika saya ingin menjalankan substitusi pada mesin saya, bukanscp
mesin target, misalnya. Dalam kebanyakan kasus mereka identik.$()
dapat lakukan jika Anda mengutip sesuatu dengan benar. (Kecuali jika Anda membutuhkan perintah Anda untuk bertahan hidup sesuatu yang munges$
tetapi tidak backticks). Ada beberapa perbedaan halus dalam mengutip hal-hal di dalamnya. mywiki.wooledge.org/BashFAQ/082 menjelaskan beberapa perbedaan. Kecuali jika Anda bermain golf, jangan pernah menggunakan backtick.echo `bc <<<"\`date +%s\`-12"`
... (Sulit untuk memposting sampel yang berisi backtick dalam komentar, di sana !; )Menggunakan
if
untuk mengelompokkan perintahDibandingkan dengan tip ini yang menghilangkan
if
sama sekali, ini seharusnya hanya berfungsi lebih baik dalam beberapa kasus yang sangat langka, seperti ketika Anda membutuhkan nilai kembali dariif
.Jika Anda memiliki grup perintah yang diakhiri dengan
if
, seperti ini:Anda dapat membungkus perintah sebelumnya
if
dalam kondisi sebagai gantinya:Atau jika fungsi Anda berakhir dengan
if
:Anda dapat menghapus kawat gigi:
sumber
[test] && $if_true || $else
dalam fungsi-fungsi ini dan menyimpan beberapa byte.&&
dan||
Gunakan aritmatika
(( ... ))
untuk kondisiAnda bisa mengganti:
oleh
(Catatan: Tidak ada ruang setelah
if
)atau bahkan
... atau jika hanya satu perintah
Selanjutnya: Sembunyikan pengaturan variabel dalam konstruksi aritmatika:
atau sama
... di mana jika i> 5, maka c = 1 (bukan 0;)
sumber
[ ]
alih-alih(())
.[ ]
Anda perlu tanda dolar untuk variabel. Saya tidak melihat bagaimana Anda bisa melakukan hal yang sama dengan panjang yang sama atau lebih kecil dengan menggunakan[ ]
.((...))
, baris atau ruang baru tidak diperlukan. Misalnya,for((;10>n++;)){((n%3))&&echo $n;}
coba online!Sintaks yang lebih pendek untuk loop tak terbatas (yang dapat diloloskan dengan
break
atauexit
pernyataan) adalahIni lebih pendek dari
while true;
danwhile :;
.Jika Anda tidak perlu
break
(denganexit
satu-satunya cara untuk melarikan diri), Anda dapat menggunakan fungsi rekursif sebagai gantinya.Jika Anda memang perlu istirahat, tetapi Anda tidak perlu keluar dan Anda tidak perlu melakukan modifikasi variabel apa pun di luar loop, Anda dapat menggunakan fungsi rekursif dengan tanda kurung di sekitar tubuh , yang menjalankan fungsi tubuh dalam subkulit.
sumber
for
Loop satu barisEkspresi aritmatika yang digabungkan dengan ekspansi rentang akan dievaluasi untuk setiap item dalam rentang tersebut. Misalnya yang berikut ini:
akan mengevaluasi
expression
10 kali.Ini seringkali jauh lebih singkat daripada
for
loop yang setara :Jika Anda tidak keberatan perintah tidak menemukan kesalahan, Anda dapat menghapus inital
:
. Untuk iterasi yang lebih besar dari 10, Anda juga dapat menggunakan rentang karakter, misalnya{A..z}
akan diulang 58 kali.Sebagai contoh praktis, berikut ini keduanya menghasilkan 50 angka segitiga pertama, masing-masing pada baris mereka sendiri:
sumber
for((;0<i--;)){ f;}
Lewati argumen
Seperti disebutkan dalam Bash "for" loop tanpa bagian "in foo bar ..." ,
in "$@;"
infor x in "$@;"
adalah mubazir.Dari
help for
:Sebagai contoh, jika kita ingin mengkuadratkan semua angka yang diberikan argumen posisional ke skrip Bash atau fungsi, kita bisa melakukan ini.
Cobalah online!
sumber
Alternatif untuk kucing
Katakanlah Anda mencoba membaca file dan menggunakannya dalam hal lain. Yang mungkin Anda lakukan adalah:
Jika isi
bar
itufoobar
, ini akan mencetakfoo foobar
.Namun, ada alternatif jika Anda menggunakan metode ini, yang menghemat 3 byte:
sumber
<bar
dengan sendirinya tidak berfungsi tetapi menempatkannya di backticks tidak?<
menempatkan file ke perintah, tetapi dalam hal ini menempatkannya ke dalam output standar karena kekhasan. Backticks mengevaluasi ini bersama.`cat`
?Menggunakan
[
sebagai ganti[[
dantest
bila memungkinkanContoh:
Gunakan
=
sebagai ganti==
untuk perbandinganContoh:
Perhatikan bahwa Anda harus memiliki spasi di sekitar tanda sama dengan atau tidak akan berfungsi. Hal yang sama berlaku untuk
==
berdasarkan tes saya.sumber
[
vs[[
mungkin tergantung pada jumlah kutipan diperlukan: pastebin.com/UPAGWbDQ[
...]
==/bin/test
, tapi[[
...]]
! =/bin/test
Dan orang tidak boleh lebih suka[
...]
lebih dari[[
... di]]
luar codegolfAlternatif untuk
head
line
tiga byte lebih pendek darihead -1
, tetapi sedang ditinggalkan .sed q
dua byte lebih pendek darihead -1
.sed 9q
lebih pendek satu byte darihead -9
.sumber
line
dari paket util-linux untuk membaca satu baris.tr -cd
lebih pendek darigrep -o
Misalnya, jika Anda perlu menghitung spasi,
grep -o <char>
(hanya cetak yang cocok) beri 10 byte sementaratr -cd <char>
(hapus komplemen<char>
) beri 9.( sumber )
Perhatikan bahwa keduanya memberikan output yang sedikit berbeda.
grep -o
mengembalikan hasil yang dipisahkan garis sambiltr -cd
memberikan semuanya pada baris yang sama, jaditr
mungkin tidak selalu menguntungkan.sumber
Persingkat nama file
Dalam tantangan baru-baru ini saya mencoba membaca file
/sys/class/power_supply/BAT1/capacity
, namun ini dapat disingkat/*/*/*/*/capac*y
karena tidak ada file lain dengan format itu.Misalnya, jika Anda memiliki direktori yang
foo/
berisi filefoo, bar, foobar, barfoo
dan Anda ingin referensi filefoo/barfoo
, Anda dapat menggunakanfoo/barf*
untuk menyimpan byte.Ini
*
mewakili "apa pun", dan setara dengan regex.*
.sumber
Gunakan pipa untuk
:
perintah, bukan/dev/null
. The:
built-in akan makan semua input.sumber
echo a|tee /dev/stderr|:
tidak akan mencetak apa pun.echo a|tee /dev/stderr|:
apakah mencetak di komputer saya, tetapi di tempat lain SIGPIPE mungkin membunuh tee terlebih dahulu. Ini mungkin tergantung pada versitee
.tee >(:) < <(seq 1 10)
akan berfungsi, tetapitee /dev/stderr | :
tidak akan. Bahkana() { :;};tee /dev/stderr < <(seq 1 10)| a
jangan cetak apa pun.:
intrinsik tidak makan sama sekali ... jika Anda mengandaikan input ke titik dua Anda mungkin membanjiri pipa ke dalam kesalahan ... tetapi Anda dapat mengapung redirect oleh titik dua, atau menghentikan proses dengan itu ...:| while i>&$(($??!$?:${#?})) command shit; do [ -s testitsoutput ]; done
atau bagaimana pun saran semu itu berlaku ... juga, apakah Anda sadar bahwa Anda hampir sekarat hantu seperti saya? ... hindari dengan cara apa pun< <(psycho shit i can alias to crazy math eat your world; okay? anyway, ksh93 has a separate but equal composite char placement)
split
memiliki sintaks lain (usang, tetapi tidak ada yang peduli) untuk membagi input menjadi beberapa bagian dariN
setiap baris: alih-alihsplit -lN
Anda dapat menggunakansplit -N
missplit -9
.sumber
Perluas tes
Pada dasarnya, shell adalah sejenis bahasa makro, atau setidaknya hibrida atau sejenisnya. Setiap baris perintah pada dasarnya dapat dipecah menjadi dua bagian: bagian parsing / input dan bagian ekspansi / output.
Bagian pertama adalah apa yang menjadi fokus kebanyakan orang karena ini adalah yang paling sederhana: Anda melihat apa yang Anda dapatkan. Bagian kedua adalah apa yang banyak dihindari bahkan mencoba untuk memahami dengan baik dan itulah sebabnya orang mengatakan hal-hal seperti
eval
itu jahat dan selalu mengutip ekspansi Anda - orang ingin hasil bagian pertama sama dengan yang pertama. Tidak apa-apa - tapi itu mengarah ke cabang kode panjang yang tidak perlu dan banyak pengujian yang tidak perlu.Ekspansi adalah swa-uji . The
${param[[:]#%+-=?]word}
bentuk yang lebih dari cukup untuk memvalidasi isi parameter, yang nestable, dan semua didasarkan sekitar mengevaluasi untuk NUL - yang adalah apa yang kebanyakan orang harapkan dari tes pula.+
bisa sangat berguna dalam loop:... saat
read
menarik garis tidak kosong"${r:+set}"
meluas ke"set"
dan posisi$r
ditambahkan. Tetapi ketika baris kosong adalahread
,$r
kosong dan"${r:+set}"
diperluas ke""
- yang merupakan perintah yang tidak valid. Tetapi karena perintah-line diperluas sebelum itu""
perintah nol dicari,"${r:=$*}"
mengambil nilai-nilai dari semua positionals bersambung pada byte pertama dalam$IFS
juga.r()
dapat dipanggil lagi dalam|{
perintah majemuk;}
w / nilai yang berbeda untuk$IFS
mendapatkan paragraf input berikutnya juga, karena itu ilegal untuk shellread
untuk buffer di luar\n
ewline berikutnya dalam input.sumber
Gunakan rekursi ekor untuk membuat loop lebih pendek:
Ini setara dalam perilaku (meskipun mungkin tidak dalam memori / penggunaan PID):
Dan ini kira-kira setara:
(Secara teknis tiga terakhir akan selalu mengeksekusi tubuh setidaknya sekali)
Menggunakan
$0
mengharuskan skrip Anda berada di file, tidak disisipkan ke bash prompt.Akhirnya tumpukan Anda mungkin meluap, tetapi Anda menghemat beberapa byte.
sumber
Kadang-kadang lebih pendek untuk menggunakan
expr
builtin untuk menampilkan hasil dari ekspresi aritmatika sederhana daripada yang biasaecho $[ ]
. Sebagai contoh:lebih pendek satu byte dari:
sumber
Gunakan
pwd
alih-alihecho
untuk menghasilkan garis outputPerlu meletakkan garis di stdout tetapi tidak peduli tentang isinya, dan ingin membatasi jawaban Anda ke shell builtin?
pwd
byte lebih pendek dariecho
.sumber
Kutipan dapat dihilangkan saat mencetak string.
Output dalam SM-T335 LTE, Android 5.1.1:
sumber
Saat menetapkan item array non-kontinu, Anda masih dapat melewatkan indeks berturut-turut dari potongan berkelanjutan:
Hasilnya sama:
Menurut
man bash
:sumber
Cetak kata pertama dalam sebuah string
Jika string dalam variabel
a
dan tidak mengandung karakter escape dan format (\
dan%
), gunakan ini:Tetapi akan lebih lama dari kode berikut jika diperlukan untuk menyimpan hasilnya ke dalam variabel daripada mencetak:
sumber
Melakukan 2 embed loop dengan 1
for
instruksi:sumber
Tetapkan dan Cetak string yang dikutip
Jika Anda ingin menetapkan string yang dikutip ke suatu variabel, dan kemudian mencetak nilai variabel itu, maka cara biasa untuk melakukannya adalah:
Jika
a
sebelumnya tidak disetel, ini dapat disingkat menjadi:Jika
a
sebelumnya ditetapkan, maka ini harus digunakan sebagai gantinya:Catatan ini hanya berguna jika string membutuhkan tanda kutip (mis. Berisi spasi putih). Tanpa kutipan,
a=123;echo $a
sama pendeknya.sumber
${a+foo}
tidak diatura
.