Kiat untuk bermain golf di Bash


55

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.

Jawaban:


36

Tidak berdokumen , tetapi berfungsi di setiap versi yang saya temui untukshkompatibilitaslegacymundur:

forloop memungkinkan Anda untuk menggunakan { }bukan do done. Misalnya ganti:

for i in {1..10};do echo $i; done

dengan:

for i in {1..10};{ echo $i;}

apa itu shell shdan shell apa yang memungkinkan forsintaks ini ? secara tegas diizinkan masuk zsh.
mikeserv

@mikeserv Bash. Saya ingat pernah membaca di suatu tempat bahwa sintaks ini diizinkan dalam beberapa waktu yang lama shdan bahwa Bash juga memperbolehkannya karena itu, walaupun sayangnya saya tidak memiliki kutipan.
Digital Trauma

ahh ... csh, mungkin - itulah cara mereka bekerja di shell itu.
mikeserv

omong-omong, dalam ksh93hal di atas bisa menjadi ;{1..10}bashprintf %s\\n {1..10}
:,

1
for((;i++<10)){ echo $i;}lebih pendek darifor i in {1..10};{ echo $i;}
Evan Krall

29

Untuk penggunaan ekspansi aritmatika $[…]alih-alih $((…)):

bash-4.1$ echo $((1+2*3))
7

bash-4.1$ echo $[1+2*3]
7

Dalam ekspansi aritmatika jangan gunakan $:

bash-4.1$ a=1 b=2 c=3

bash-4.1$ echo $[$a+$b*$c]
7

bash-4.1$ echo $[a+b*c]
7

Ekspansi aritmatika dilakukan pada subskripsi array yang diindeks, jadi jangan gunakan $keduanya di sana:

bash-4.1$ a=(1 2 3) b=2 c=3

bash-4.1$ echo ${a[$c-$b]}
2

bash-4.1$ echo ${a[c-b]}
2

Dalam ekspansi aritmatika jangan gunakan ${…}:

bash-4.1$ a=(1 2 3)

bash-4.1$ echo $[${a[0]}+${a[1]}*${a[2]}]
7

bash-4.1$ echo $[a[0]+a[1]*a[2]]
7

Mengganti while((i--)), yang berfungsi, dengan while[i--]atau while $[i--]tidak berfungsi untuk saya. GNU bash, versi 4.3.46 (1)
Glenn Randers-Pehrson

1
Benar, @ GlennRanders-Pehrson. Itu tidak seharusnya bekerja.
manatwork

y=`bc<<<"($x*2.2)%10/1"`... contoh penggunaan bcuntuk perhitungan non-integer ... catat /1pada bagian akhir memotong desimal yang dihasilkan ke int.
roblogic

s=$((i%2>0?s+x:s+y))... contoh penggunaan operator ternary dalam aritmatika bash. Lebih pendek dari if..then..elseatau[ ] && ||
roblogic

1
@manatwork Terima kasih. Mereka pasti sudah menghapusnya. Saya aktif GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)dan itu bukan milik saya.
Jonah

19

Cara normal, panjang dan membosankan untuk mendefinisikan suatu fungsi adalah

f(){ CODE;}

Seperti yang diketahui orang ini , Anda benar-benar membutuhkan ruang sebelum CODEdan titik koma setelahnya.

Ini adalah sedikit trik yang saya pelajari dari @DigitalTrauma :

f()(CODE)

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:

name () compound-command [ redirections ]

atau

function name [()] compound-command [ redirections ]

Sebuah perintah majemuk dapat:

  • a Construct Looping: until, whileataufor
  • a Construct Bersyarat: if, case, ((...))atau[[...]]
  • Perintah yang Dikelompokkan: (...)atau{...}

Itu berarti semua hal berikut ini valid:

$ f()if $1;then $2;fi
$ f()($1&&$2)
$ f()(($1))                # This one lets you assign integer values

Dan saya telah menggunakan kurung keriting seperti pengisap ...


2
Perhatikan bahwa Anda juga dapat menggunakan f()while ... f()if ...dan perintah majemuk lainnya.
jimmy23013

Yang ini mengejutkan saya karena saya pikir f()CODEitu legal. Ternyata itu f()echo hilegal di pdksh dan zsh, tetapi tidak di bash.
kernigh

1
ini sangat berguna dg forkarena defaultnya adalah posisi: f()for x do : $x; done;set -x *;PS4=$* f "$@"atau sesuatu.
mikeserv

16

:adalah perintah yang tidak melakukan apa-apa, status keluarnya selalu berhasil, sehingga dapat digunakan sebagai ganti true.


Menggunakan subkulit dan perpipaan akan menggunakan jumlah byte yang sama, tetapi perpipaan akan lebih praktis.
ckjbgames

5
Kecuali saat Anda melakukannya:(){:|:}
enedil

14

Lebih banyak tips

  1. Menyalahgunakan operator ternary, ((test)) && cmd1 || cmd2atau [ test ] && cmd1 || cmd2, sebanyak mungkin.

    Contoh (jumlah panjang selalu mengecualikan baris teratas):

    t="$something"
    if [ $t == "hi" ];then
    cmd1
    cmd2
    elif [ $t == "bye" ];then
    cmd3
    cmd4
    else
    cmd5
    if [ $t == "sup" ];then
    cmd6
    fi
    fi

    Dengan menggunakan operator ternary saja, ini dapat dengan mudah disingkat menjadi:

    t="$something"
    [ $t == "hi" ]&&{
    cmd1;cmd2
    }||[ $t == "bye" ]&&{
    cmd3;cmd4
    }||{
    cmd5
    [ $t == "sup" ]&&cmd6
    }

    Seperti yang nyuszika7h tunjukkan dalam komentar, contoh khusus ini dapat dipersingkat lebih jauh dengan menggunakan case:

    t="$something"
    case $t in "hi")cmd1;cmd2;;"bye")cmd3;cmd4;;*)cmd5;[ $t == "sup" ]&&cmd6;esac
  2. 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 :

    t=$something
    [ $t == "hi" ]&&(cmd1;cmd2)||[ $t == "bye" ]&&(cmd3;cmd4)||(cmd5;[ $t == "sup" ]&&cmd6)

    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 -equntuk bilangan bulat).

  3. Satu hal lagi, hindari kutipan jika memungkinkan. Dengan menggunakan saran di atas, Anda dapat meminimalkannya lebih lanjut. Contoh:

    t=$something
    [ $t == hi ]&&(cmd1;cmd2)||[ $t == bye ]&&(cmd3;cmd4)||(cmd5;[ $t == sup ]&&cmd6)
  4. 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.

    [[ $f == b ]]&&: # ... <-- Bad
    [ $f == b ]&&: # ... <-- Better
    [ $f = b ]&&: # ... <-- Best.  word splits and pathname-expands the contents of $f.  Esp. bad if it starts with -

    Perhatikan peringatan ini, terutama dalam memeriksa output nol atau variabel yang tidak ditentukan:

    [[ $f ]]&&:    # double quotes aren't needed inside [[, which can save chars
    [ "$f" = '' ]&&: <-- This is significantly longer
    [ -n "$f" ]&&:

    Dalam semua teknis, contoh khusus ini akan lebih baik dengan case... in:

    t=$something
    case $t in hi)cmd1;cmd2;;bye)cmd3;cmd4;;*)cmd5;[ $t == sup ]&&cmd6;esac

Jadi, moral dari postingan ini adalah ini:

  1. Menyalahgunakan operator boolean sebanyak mungkin, dan selalu menggunakannya sebagai gantinyaif / if-else/ etc. membangun.
  2. Gunakan tanda kurung sebanyak mungkin dan jalankan segmen sebanyak mungkin dalam subkulit karena tanda kurung adalah meta-karakter dan bukan kata-kata yang dicadangkan.
  3. Hindari kutipan sebanyak mungkin secara fisik.
  4. Lihat 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):

&lt; &gt; ( ) ; & | &lt;space&gt; &lt;tab&gt;

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.


Maaf untuk mengatakan, tetapi Anda memiliki beberapa kesalahan serius di sana. [ $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/WefDzWbL
manatwork

@manatwork Terima kasih atas tangkapannya. Saya akan memperbarui sesuai.
Isiah Meadows

Menggunakan caseakan lebih pendek di sini. Juga, Anda tidak perlu ruang sebelumnya }, tetapi Anda lakukan setelah {.
nyuszika7h

1
Mengapa =tidak sekuat itu ==? =diamanatkan oleh POSIX, ==bukan.
Dennis

1
Pertanyaannya memang meminta satu tip per jawaban ...
Toby Speight

7

Alih-alih grep -E, grep -F, grep -r, penggunaan egrep, fgrep,rgrep , tabungan dua karakter. Yang lebih pendek sudah usang tetapi berfungsi dengan baik.

(Anda memang meminta satu tip per jawaban!)


1
Sayang sekali tidak ada Pgrepuntuk grep -P. Meskipun saya melihat bagaimana hal itu dapat dengan mudah dikacaukan pgrep, yang digunakan untuk mencari proses.
nyuszika7h

1
@ nyuszika7h Saya pribadi menggunakan grep -obanyak

Bukankah itu menghemat 3 termasuk ruang?
ckjbgames

7

Elemen 0 dari suatu array dapat diakses dengan nama variabel saja, penghematan lima byte lebih dari yang secara eksplisit menentukan indeks 0:

$ a=(code golf)
$ echo ${a[0]}
code
$ echo $a
code
$ 

7

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 :

$ s="code golf"
$ echo "$s"|cut -b4-6
e g
$ cut -b4-6<<<"$s"
e g
$ 

2
Karena kita golf sedang, s=code\ golf, echo $s|dan <<<$s(mengingat bahwa dua terakhir bekerja hanya karena ada ruang ada diulang, dll).
Dennis

6

Hindari $( ...command... ), ada alternatif yang menyimpan satu char dan melakukan hal yang sama:

` ...command... `

9
Terkadang $( )diperlukan jika Anda memiliki pengganti perintah bersarang; jika tidak, Anda harus melarikan diri dari dalam``
Digital Trauma

1
Ini secara teknis melakukan hal yang berbeda, saya harus menggunakan backticks daripada $()ketika saya ingin menjalankan substitusi pada mesin saya, bukan scpmesin target, misalnya. Dalam kebanyakan kasus mereka identik.
undergroundmonorail

2
@undergroundmonorail: Anda tidak perlu backticks. Apa pun yang dapat mereka lakukan, $()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.
Peter Cordes

@PeterCordes Saya yakin ada adalah jalan tapi semua yang saya mencoba pada saat itu tidak bekerja. Bahkan jika backticks bukan solusi terbaik, saya senang saya tahu tentang mereka karena itu adalah satu-satunya solusi yang saya miliki. ¯ \ _ (ツ) _ / ¯
undergroundmonorail

@DigitalTrauma Contoh nesting: echo `bc <<<"\`date +%s\`-12"`... (Sulit untuk memposting sampel yang berisi backtick dalam komentar, di sana !; )
F. Hauri

6

Menggunakan if untuk mengelompokkan perintah

Dibandingkan dengan tip ini yang menghilangkan ifsama 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:

a&&{ b;if c;then d;else e;fi;}
a&&(b;if c;then d;else e;fi)

Anda dapat membungkus perintah sebelumnya ifdalam kondisi sebagai gantinya:

a&&if b;c;then d;else e;fi

Atau jika fungsi Anda berakhir dengan if:

f(){ a;if b;then c;else d;fi;}

Anda dapat menghapus kawat gigi:

f()if a;b;then c;else d;fi

1
Anda dapat menggunakan operator ternary [test] && $if_true || $elsedalam fungsi-fungsi ini dan menyimpan beberapa byte.
ckjbgames

Juga, Anda tidak perlu spasi di sekitar &&dan||
roblogic

6

Gunakan aritmatika (( ... )) untuk kondisi

Anda bisa mengganti:

if [ $i -gt 5 ] ; then
    echo Do something with i greater than 5
fi

oleh

if((i>5));then
    echo Do something with i greater than 5
fi

(Catatan: Tidak ada ruang setelah if)

atau bahkan

((i>5))&&{
    echo Do something with i greater than 5
}

... atau jika hanya satu perintah

((i>5))&&echo Echo or do something with i greater than 5

Selanjutnya: Sembunyikan pengaturan variabel dalam konstruksi aritmatika:

((i>5?c=1:0))&&echo Nothing relevant there...
# ...
((c))&&echo Doing something else if i was greater than 5

atau sama

((c=i>5?c=0,1:0))&&echo Nothing relevant there...
# ...
((c))&&echo Doing something else if i was greater than 5

... di mana jika i> 5, maka c = 1 (bukan 0;)


Anda bisa menghemat 2 byte dengan menggunakan [ ]alih-alih (()).
ckjbgames

@ckjbgames Apakah Anda yakin akan hal itu !? Apa versi bash yang Anda gunakan?
F. Hauri

@ Fauri Saya kira itu akan sama dalam hal byte.
ckjbgames

@ckjbgames Dengan [ ]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 [ ].
F. Hauri

Tip Bonus: jika baris pertama untuk for loop dimulai dengan ((...)), baris atau ruang baru tidak diperlukan. Misalnya, for((;10>n++;)){((n%3))&&echo $n;} coba online!
primo

6

Sintaks yang lebih pendek untuk loop tak terbatas (yang dapat diloloskan dengan breakatau exitpernyataan) adalah

for((;;)){ code;}

Ini lebih pendek dari while true;danwhile :; .

Jika Anda tidak perlu break(dengan exitsatu-satunya cara untuk melarikan diri), Anda dapat menggunakan fungsi rekursif sebagai gantinya.

f(){ code;f;};f

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.

f()(code;f);f

6

forLoop satu baris

Ekspresi aritmatika yang digabungkan dengan ekspansi rentang akan dievaluasi untuk setiap item dalam rentang tersebut. Misalnya yang berikut ini:

: $[expression]{0..9}

akan mengevaluasi expression10 kali.

Ini seringkali jauh lebih singkat daripada forloop yang setara :

for((;10>n++;expression with n)){ :;}
: $[expression with ++n]{0..9}

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:

for((;50>d++;)){ echo $[n+=d];} # 31 bytes
printf %d\\n $[n+=++d]{A..r}    # 28 bytes

Anda juga dapat mengulangi mundur:for((;0<i--;)){ f;}
roblogic

5

Lewati argumen

Seperti disebutkan dalam Bash "for" loop tanpa bagian "in foo bar ..." , in "$@;"in for x in "$@;"adalah mubazir.

Dari help for:

for: for NAME [in WORDS ... ] ; do COMMANDS; done
    Execute commands for each member in a list.

    The `for' loop executes a sequence of commands for each member in a
    list of items.  If `in WORDS ...;' is not present, then `in "$@"' is
    assumed.  For each element in WORDS, NAME is set to that element, and
    the COMMANDS are executed.

    Exit Status:
    Returns the status of the last command executed.

Sebagai contoh, jika kita ingin mengkuadratkan semua angka yang diberikan argumen posisional ke skrip Bash atau fungsi, kita bisa melakukan ini.

for n;{ echo $[n*n];}

Cobalah online!


5

Alternatif untuk kucing

Katakanlah Anda mencoba membaca file dan menggunakannya dalam hal lain. Yang mungkin Anda lakukan adalah:

echo foo `cat bar`

Jika isi baritu foobar, ini akan mencetak foo foobar.

Namun, ada alternatif jika Anda menggunakan metode ini, yang menghemat 3 byte:

echo foo `<bar`

1
Apakah ada alasan mengapa <bardengan sendirinya tidak berfungsi tetapi menempatkannya di backticks tidak?
Kritixi Lithos

@Cowsquack Ya. The <menempatkan file ke perintah, tetapi dalam hal ini menempatkannya ke dalam output standar karena kekhasan. Backticks mengevaluasi ini bersama.
Okx

Apakah ada cara yang lebih pendek untuk membaca dari input standar selain `cat`?
Joel

4

Menggunakan [ sebagai ganti [[dan testbila memungkinkan

Contoh:

[ -n $x ]


Gunakan =sebagai ganti ==untuk perbandingan

Contoh:

[ $x = y ]

Perhatikan bahwa Anda harus memiliki spasi di sekitar tanda sama dengan atau tidak akan berfungsi. Hal yang sama berlaku untuk ==berdasarkan tes saya.


3
The [vs [[mungkin tergantung pada jumlah kutipan diperlukan: pastebin.com/UPAGWbDQ
manatwork

@manatwork Itu poin bagus.
nyuszika7h

1
Aturan umum: [... ]== /bin/test, tapi [[... ]]! = /bin/testDan orang tidak boleh lebih suka [... ]lebih dari [[... di ]]luar codegolf
cat

4

Alternatif untuk head

linetiga byte lebih pendek dari head -1, tetapi sedang ditinggalkan .

sed qdua byte lebih pendek dari head -1.

sed 9qlebih pendek satu byte dari head -9.


1
Meskipun hancur , kita masih dapat menggunakan untuk sementara waktu linedari paket util-linux untuk membaca satu baris.
manatwork

4

tr -cd lebih pendek dari grep -o

Misalnya, jika Anda perlu menghitung spasi, grep -o <char>(hanya cetak yang cocok) beri 10 byte sementara tr -cd <char>(hapus komplemen <char>) beri 9.

# 16 bytes
grep -o \ |wc -l
# 15 bytes
tr -cd \ |wc -c

( sumber )

Perhatikan bahwa keduanya memberikan output yang sedikit berbeda. grep -omengembalikan hasil yang dipisahkan garis sambil tr -cdmemberikan semuanya pada baris yang sama, jadi trmungkin tidak selalu menguntungkan.


4

Persingkat nama file

Dalam tantangan baru-baru ini saya mencoba membaca file /sys/class/power_supply/BAT1/capacity, namun ini dapat disingkat /*/*/*/*/capac*ykarena tidak ada file lain dengan format itu.

Misalnya, jika Anda memiliki direktori yang foo/berisi file foo, bar, foobar, barfoodan Anda ingin referensi file foo/barfoo, Anda dapat menggunakan foo/barf*untuk menyimpan byte.

Ini *mewakili "apa pun", dan setara dengan regex .*.


3

Gunakan pipa untuk :perintah, bukan /dev/null. The :built-in akan makan semua input.


2
Tidak, ini akan membuat crash program dengan SIGPIPE dalam banyak kasus. echo a|tee /dev/stderr|:tidak akan mencetak apa pun.
jimmy23013

Ada perlombaan: echo a|tee /dev/stderr|:apakah mencetak di komputer saya, tetapi di tempat lain SIGPIPE mungkin membunuh tee terlebih dahulu. Ini mungkin tergantung pada versi tee.
kernigh

Ya, ini masalah SIGPIPE: tee >(:) < <(seq 1 10)akan berfungsi, tetapi tee /dev/stderr | :tidak akan. Bahkan a() { :;};tee /dev/stderr < <(seq 1 10)| ajangan cetak apa pun.
F. Hauri

@ user16402 - Anda harus memiliki nama fccing ke ruang lingkup saya ... pokoknya, :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 ]; doneatau 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)
mikeserv

3

splitmemiliki sintaks lain (usang, tetapi tidak ada yang peduli) untuk membagi input menjadi beberapa bagian dari Nsetiap baris: alih-alih split -lNAnda dapat menggunakan split -Nmis split -9.


3

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 evalitu 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:

r()while IFS= read -r r&&"${r:+set}" -- "$@" "${r:=$*}";do :;done 2>&-

IFS=x
printf %s\\n some lines\ of input here '' some more|{ r;echo "$r"; }

somexlines ofxinputxhere

... saat readmenarik garis tidak kosong "${r:+set}"meluas ke "set"dan posisi $rditambahkan. Tetapi ketika baris kosong adalah read, $rkosong 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 $IFSjuga. r()dapat dipanggil lagi dalam |{perintah majemuk ;}w / nilai yang berbeda untuk $IFSmendapatkan paragraf input berikutnya juga, karena itu ilegal untuk shell readuntuk buffer di luar \newline berikutnya dalam input.


3

Gunakan rekursi ekor untuk membuat loop lebih pendek:

Ini setara dalam perilaku (meskipun mungkin tidak dalam memori / penggunaan PID):

while :;do body; done
f()(body;f);f
body;exec $0
body;$0

Dan ini kira-kira setara:

while condition; do body; done
f()(body;condition&&f);f
body;condition&&exec $0
body;condition&&$0

(Secara teknis tiga terakhir akan selalu mengeksekusi tubuh setidaknya sekali)

Menggunakan $0mengharuskan skrip Anda berada di file, tidak disisipkan ke bash prompt.

Akhirnya tumpukan Anda mungkin meluap, tetapi Anda menghemat beberapa byte.


3

Kadang-kadang lebih pendek untuk menggunakan exprbuiltin untuk menampilkan hasil dari ekspresi aritmatika sederhana daripada yang biasa echo $[ ]. Sebagai contoh:

expr $1 % 2

lebih pendek satu byte dari:

echo $[$1%2]

2

Gunakan pwdalih-alih echountuk menghasilkan garis output

Perlu meletakkan garis di stdout tetapi tidak peduli tentang isinya, dan ingin membatasi jawaban Anda ke shell builtin? pwdbyte lebih pendek dari echo.


2

Kutipan dapat dihilangkan saat mencetak string.

echo "example"
echo example

Output dalam SM-T335 LTE, Android 5.1.1:

u0_a177@milletlte:/ $ echo "example"
example
u0_a177@milletlte:/ $ echo example
example

2

Saat menetapkan item array non-kontinu, Anda masih dapat melewatkan indeks berturut-turut dari potongan berkelanjutan:

bash-4.4$ a=([1]=1 [2]=2 [3]=3 [21]=1 [22]=2 [23]=3 [31]=1)

bash-4.4$ b=([1]=1 2 3 [21]=1 2 3 [31]=1)

Hasilnya sama:

bash-4.4$ declare -p a b
declare -a a=([1]="1" [2]="2" [3]="3" [21]="1" [22]="2" [23]="3" [31]="1")
declare -a b=([1]="1" [2]="2" [3]="3" [21]="1" [22]="2" [23]="3" [31]="1")

Menurut man bash:

Array ditugaskan untuk menggunakan penugasan majemuk dari nama form = (nilai 1 ... nilai n ), di mana setiap nilai adalah dari bentuk [ subskrip ] = string . Penugasan array yang diindeks tidak memerlukan apa pun selain string . Saat menetapkan ke array yang diindeks, jika kurung dan subskrip opsional disediakan, indeks tersebut ditugaskan untuk; jika tidak, indeks elemen yang ditugaskan adalah indeks terakhir yang ditetapkan oleh pernyataan plus satu.


Bermanfaat untuk ditambahkan: elemen yang tidak diinisialisasi akan berkembang menjadi 0 dalam ekspansi aritmatika dan "" dalam ekspansi lainnya.
Digital Trauma

2

Cetak kata pertama dalam sebuah string

Jika string dalam variabel adan tidak mengandung karakter escape dan format ( \dan %), gunakan ini:

printf $a

Tetapi akan lebih lama dari kode berikut jika diperlukan untuk menyimpan hasilnya ke dalam variabel daripada mencetak:

x=($a)
$x

1

Melakukan 2 embed loop dengan 1 forinstruksi:

for ((l=i=0;l<=99;i=i>98?l++,0:++i)) ;do
    printf "I: %2d, L: %2d\n" $i $l
done |
    tee >(wc) | (head -n4;echo ...;tail -n 5)
I:  0, L:  0
I:  1, L:  0
I:  2, L:  0
I:  3, L:  0
...
I: 96, L: 99
I: 97, L: 99
I: 98, L: 99
I: 99, L: 99
  10000   40000  130000

1

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:

a="Programming Puzzles & Code Golf";echo $a

Jika asebelumnya tidak disetel, ini dapat disingkat menjadi:

echo ${a=Programming Puzzles & Code Golf}

Jika asebelumnya ditetapkan, maka ini harus digunakan sebagai gantinya:

echo ${a+Programming Puzzles & Code Golf}

Catatan ini hanya berguna jika string membutuhkan tanda kutip (mis. Berisi spasi putih). Tanpa kutipan, a=123;echo $asama pendeknya.


${a+foo}tidak diatur a.
GammaFunction
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.