เคล็ดลับการตีกอล์ฟใน Bash


55

คุณมีเคล็ดลับอะไรสำหรับการเล่นกอล์ฟใน Bash ฉันกำลังมองหาความคิดที่สามารถนำไปใช้กับปัญหารหัสกอล์ฟโดยทั่วไปซึ่งอย่างน้อยค่อนข้างเฉพาะกับ Bash (เช่น "ลบความคิดเห็น" ไม่ใช่คำตอบ) กรุณาโพสต์หนึ่งเคล็ดลับต่อคำตอบ

คำตอบ:


36

ไม่มีเอกสารแต่ทำงานได้กับทุกเวอร์ชั่นที่ฉันใช้เพื่อshความเข้ากันได้แบบเก่า:

forลูปช่วยให้คุณสามารถใช้แทน{ } do doneเช่นแทนที่:

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

ด้วย:

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

เชลล์คือshอะไรและเชลล์อนุญาตให้forไวยากรณ์นี้คืออะไร zshจะได้รับอนุญาตอย่างชัดแจ้งใน
mikeserv

@mikeserv ทุบตี ฉันจำได้ว่าอ่านบางเรื่องว่ารูปแบบนี้ได้รับอนุญาตในบางเก่าshและ Bash ก็อนุญาตเพราะเหตุนี้ถึงแม้ว่าฉันจะเศร้าที่ไม่มีการอ้างอิง
Digital Trauma

อ่า ... cshอาจเป็น - นั่นเป็นวิธีที่พวกเขาทำงานในเปลือกนั้น
mikeserv

โดยวิธีการในksh93สิ่งข้างต้นอาจเป็น: ;{1..10}และในbash:printf %s\\n {1..10}
mikeserv

1
for((;i++<10)){ echo $i;}สั้นกว่าfor i in {1..10};{ echo $i;}
Evan Krall

29

สำหรับการขยายเลขคณิตให้ใช้$[…]แทน$((…)):

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

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

ในการขยายเลขคณิตไม่ใช้$:

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

การขยายเลขคณิตจะดำเนินการกับตัวห้อยอาร์เรย์ที่จัดทำดัชนีดังนั้นอย่าใช้$ไม่ได้:

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

ในการขยายเลขคณิตไม่ใช้${…}:

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

การเปลี่ยนwhile((i--))ซึ่งใช้งานได้กับwhile[i--]หรือใช้งานwhile $[i--]ไม่ได้สำหรับฉัน GNU ทุบตีรุ่น 4.3.46 (1)
Glenn Randers-Pehrson

1
ถูกต้อง @ GlennRanders-Pehrson ที่ไม่ควรทำงาน
จัดการ

y=`bc<<<"($x*2.2)%10/1"`... ตัวอย่างการใช้bcสำหรับการคำนวณที่ไม่ใช่จำนวนเต็ม ... สังเกต/1ที่ท้ายตัดทอนทศนิยมที่เกิดขึ้นให้เป็น int
roblogic

s=$((i%2>0?s+x:s+y))... ตัวอย่างการใช้โอเปอเรเตอร์ใน bash arithmetic มันสั้นกว่าif..then..elseหรือ[ ] && ||
roblogic

1
@ การจัดการขอบคุณ พวกเขาจะต้องลบมันออก ฉันอยู่GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)และมันไม่ได้อยู่ในเหมือง
โยนาห์

19

วิธีปกติในการกำหนดความยาวและน่าเบื่อคือ

f(){ CODE;}

เมื่อชายคนนี้ค้นพบว่าคุณต้องการพื้นที่อย่างแน่นอนก่อนCODEและอัฒภาคหลังจากนั้น

นี่เป็นเคล็ดลับเล็กน้อยที่ฉันได้เรียนรู้จาก@DigitalTrauma :

f()(CODE)

นั่นคืออักขระสองตัวที่สั้นกว่าและทำงานได้เช่นกันโดยที่คุณไม่จำเป็นต้องดำเนินการเปลี่ยนแปลงค่าของตัวแปรหลังจากฟังก์ชันส่งคืน ( วงเล็บจะเรียกใช้เนื้อความในเชลล์ย่อย )

ในฐานะที่เป็น@ jimmy23013ชี้ให้เห็นในความคิดเห็นที่แม้วงเล็บอาจจะไม่จำเป็น

คู่มือทุบตีอ้างอิงแสดงให้เห็นว่าฟังก์ชั่นสามารถกำหนดดังต่อไปนี้:

name () compound-command [ redirections ]

หรือ

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

คำสั่งสารประกอบสามารถ:

  • สร้าง Looping: until, whileหรือfor
  • สร้างเงื่อนไข: if, case, ((...))หรือ[[...]]
  • คำสั่งที่จัดกลุ่ม: (...)หรือ{...}

นั่นหมายความว่าทั้งหมดต่อไปนี้ถูกต้อง:

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

และฉันใช้วงเล็บปีกกาเหมือนเครื่องดูด ...


2
โปรดทราบว่าคุณยังสามารถใช้f()while ... f()if ...และคำสั่งผสมอื่น ๆ
jimmy23013

อันนี้ทำให้ฉันประหลาดใจเพราะฉันคิดว่าf()CODEถูกกฎหมาย ปรากฎว่าf()echo hiถูกกฎหมายใน pdksh และ zsh แต่ไม่ใช่ในทุบตี
kernigh

1
มันมีประโยชน์โดยเฉพาะ w / forเพราะมันเริ่มต้นที่ positionals: f()for x do : $x; done;set -x *;PS4=$* f "$@"หรืออะไรบางอย่าง
mikeserv

16

:เป็นคำสั่งที่ไม่ทำอะไรเลยสถานะทางออกของมันจะสำเร็จเสมอดังนั้นจึงสามารถใช้แทนtrueได้


การใช้ subshell และ piping มันจะใช้จำนวนไบต์เท่ากัน แต่การ piping จะใช้งานได้จริงมากกว่า
ckjbgames

5
ยกเว้นเมื่อคุณทำ:(){:|:}
enedil

14

เคล็ดลับเพิ่มเติม

  1. ใช้โอเปอเรเตอร์ที่ไม่เหมาะสม((test)) && cmd1 || cmd2หรือ[ test ] && cmd1 || cmd2มากที่สุด

    ตัวอย่าง (การนับความยาวจะไม่รวมบรรทัดบนสุดเสมอ):

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

    ด้วยการใช้ผู้ประกอบการที่สามเท่านั้นสิ่งนี้สามารถทำให้สั้นลงได้อย่างง่ายดาย:

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

    ตามที่ nyuszika7h ชี้ให้เห็นในความคิดเห็นตัวอย่างที่เฉพาะเจาะจงนี้อาจสั้นลงได้อีกโดยใช้case:

    t="$something"
    case $t in "hi")cmd1;cmd2;;"bye")cmd3;cmd4;;*)cmd5;[ $t == "sup" ]&&cmd6;esac
    
  2. นอกจากนี้ให้เลือกวงเล็บให้มากที่สุด เนื่องจากเครื่องหมายวงเล็บเป็นตัวอักษรและไม่ใช่คำจึงไม่จำเป็นต้องเว้นวรรคในบริบทใด ๆ นอกจากนี้ยังหมายถึงเรียกใช้คำสั่งจำนวนมากใน subshell ให้มากที่สุดเนื่องจากเครื่องหมายปีกกา (เช่น{และ}) เป็นคำสงวนไม่ใช่ตัวอักษรเมตาดังนั้นจึงต้องมีช่องว่างทั้งสองด้านในการแยกวิเคราะห์อย่างถูกต้อง แต่ meta-character ไม่ ฉันคิดว่าคุณรู้แล้วว่า subshells ไม่ส่งผลกระทบต่อสภาพแวดล้อมพาเรนต์ดังนั้นสมมติว่าคำสั่งตัวอย่างทั้งหมดสามารถรันได้อย่างปลอดภัยใน subshell (ซึ่งไม่ใช่เรื่องปกติในกรณีใด ๆ ) คุณสามารถย่อโค้ดข้างต้นนี้ :

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

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

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

    t=$something
    [ $t == hi ]&&(cmd1;cmd2)||[ $t == bye ]&&(cmd3;cmd4)||(cmd5;[ $t == sup ]&&cmd6)
    
  4. ในเงื่อนไขการทดสอบให้เลือกใช้วงเล็บเดี่ยวมากกว่าวงเล็บสองเท่าที่จะทำได้โดยมีข้อยกเว้นเล็กน้อย มันลดอักขระสองตัวฟรี แต่ในบางกรณีอาจไม่ทนทาน (เป็นส่วนขยายของ Bash - ดูตัวอย่างด้านล่าง) นอกจากนี้ใช้อาร์กิวเมนต์เท่ากับเดียวแทนที่จะเป็นสองเท่า มันเป็นตัวอักษรฟรีที่จะลดลง

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

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

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

    ในด้านเทคนิคทั้งหมดตัวอย่างเฉพาะนี้จะดีที่สุดกับcase... in:

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

ดังนั้นคุณธรรมของการโพสต์นี้คือ:

  1. การละเมิดผู้ประกอบการบูลีนมากที่สุดเท่าที่เป็นไปได้และมักจะใช้พวกเขาแทนif/ if-else/ ฯลฯ โครงสร้าง
  2. ใช้วงเล็บให้มากที่สุดและเรียกใช้เซกเมนต์ให้มากที่สุดเท่าที่จะทำได้ใน subshells เนื่องจากวงเล็บเป็นเมตาอักขระและไม่ใช่คำที่สงวนไว้
  3. หลีกเลี่ยงการพูดให้มากที่สุด
  4. ลองดูcase... inเนื่องจากมันอาจบันทึกได้ไม่กี่ไบต์โดยเฉพาะในการจับคู่สตริง

PS: นี่คือรายการของเมตาอักขระที่รู้จักใน Bash โดยไม่คำนึงถึงบริบท (และสามารถแยกคำ):

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

แก้ไข: ตามที่ manatwork ชี้ให้เห็นการทดสอบวงเล็บคู่นั้นใช้ได้กับจำนวนเต็มเท่านั้น นอกจากนี้ทางอ้อมฉันพบว่าคุณต้องมีช่องว่างรอบตัว==ดำเนินการ แก้ไขโพสต์ของฉันด้านบน

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


ขออภัยที่จะพูด แต่คุณมีข้อผิดพลาดร้ายแรงที่นั่น [ $t=="hi" ]มักจะมีการประเมินถึง 0 [ -n "STRING" ]ขณะที่มันถูกแยกวิเคราะห์เป็น (($t=="hi"))จะประเมินเป็น 0 ตราบใดที่ $ t มีค่าที่ไม่ใช่ตัวเลขเนื่องจากสตริงจะถูกบังคับให้เป็นจำนวนเต็มในการประเมินทางคณิตศาสตร์ กรณีทดสอบบางอย่าง: pastebin.com/WefDzWbL
จัดการ

@ การจัดการขอบคุณสำหรับการจับ ฉันจะอัปเดตตามนั้น
Isiah Meadows

การใช้ a caseจะสั้นกว่าที่นี่ นอกจากนี้คุณไม่จำเป็นต้องมีช่องว่างมาก่อน}แต่คุณต้องทำหลังจาก{นั้น
nyuszika7h

1
ทำไมจะ=มีประสิทธิภาพน้อยกว่า==? =ได้รับคำสั่งจาก POSIX ==ไม่ใช่
เดนนิส

1
คำถามที่ไม่ถามหาหนึ่งเคล็ดลับต่อคำตอบ ...
Toby Speight

7

แทนที่จะgrep -E, grep -F, grep -rใช้egrep, fgrep, rgrepประหยัดสองตัวอักษร อันที่สั้นกว่านั้นเลิกใช้แล้ว แต่ทำงานได้ดี

(คุณขอหนึ่งเคล็ดลับต่อคำตอบ!)


1
เสียดายที่ไม่มีสำหรับPgrep grep -Pแม้ว่าฉันจะเห็นว่ามันอาจจะสับสนได้ง่ายpgrepซึ่งใช้ในการค้นหากระบวนการ
nyuszika7h

1
@ nyuszika7h ส่วนตัวฉันใช้งานgrep -oเยอะมาก

จะไม่ประหยัด 3 รวมถึงพื้นที่?
ckjbgames

7

องค์ประกอบ 0 ของอาเรย์สามารถเข้าถึงได้โดยใช้ชื่อตัวแปรเท่านั้นการบันทึกห้าไบต์ผ่านการระบุดัชนี 0 อย่างชัดเจน:

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

7

หากคุณต้องการส่งเนื้อหาของตัวแปรไปยัง STDIN ของกระบวนการถัดไปในไปป์ไลน์มันเป็นเรื่องปกติที่จะสะท้อนตัวแปรลงในไพพ์ไลน์ แต่คุณสามารถบรรลุสิ่งเดียวกันด้วยการ<<< ทุบตีที่นี่สตริง :

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

2
เนื่องจากเรากำลังเล่นกอล์ฟs=code\ golf, echo $s|และ<<<$s(ทำให้ทราบว่าทั้งสองฝ่ายทำงานเพียงเพราะไม่มีช่องว่างซ้ำ ฯลฯ )
เดนนิส

6

หลีกเลี่ยง$( ...command... )มีทางเลือกอื่นที่ช่วยประหยัดถ่านหนึ่งตัวและทำสิ่งเดียวกัน

` ...command... `

9
บางครั้ง$( )จำเป็นถ้าคุณมีการทดแทนคำสั่งซ้อนกัน; ไม่งั้นคุณจะต้องหลบหนีจากด้านใน``
Digital Trauma

1
เทคนิคเหล่านี้ทำสิ่งต่าง ๆ ฉันต้องใช้ backticks แทน$()เมื่อฉันต้องการเรียกใช้การทดแทนบนเครื่องของฉันแทนที่จะเป็นscpเครื่องเป้าหมาย ในกรณีส่วนใหญ่พวกเขาเหมือนกัน
undergroundmonorail

2
@undergroundmonorail: คุณไม่จำเป็นต้องมี backticks สิ่งที่พวกเขาสามารถทำได้$()สามารถทำได้ถ้าคุณพูดอย่างถูกต้อง (ยกเว้นว่าคุณต้องการคำสั่งของคุณเพื่อเอาตัวรอดจากบางสิ่งที่ munges $แต่ไม่ใช่ backticks) มีความแตกต่างเล็กน้อยในการอ้างอิงสิ่งที่อยู่ภายใน mywiki.wooledge.org/BashFAQ/082อธิบายถึงความแตกต่าง ถ้าคุณไม่เล่นกอล์ฟห้ามใช้ backticks
Peter Cordes

@PeterCordes ฉันแน่ใจว่ามีเป็นวิธี แต่ทุกอย่างที่ฉันพยายามในเวลาที่ไม่ได้ทำงาน แม้ว่า backticks ไม่ใช่ทางออกที่ดีที่สุดฉันก็ดีใจที่ฉันรู้เกี่ยวกับพวกเขาเพราะมันเป็นทางออกเดียวที่ฉันมี ¯ \ _ (ツ) _ / ¯
undergroundmonorail

@DigitalTrauma ตัวอย่างของการทำรัง: echo `bc <<<"\`date +%s\`-12"`... (มันยากที่จะโพสต์ตัวอย่างที่มี backtick ในความคิดเห็นมี!;)
F. Hauri

6

ใช้ifเพื่อจัดกลุ่มคำสั่ง

เมื่อเทียบกับเคล็ดลับนี้ซึ่งเอาที่ทั้งหมดนี้ควรจะทำงานได้ดีขึ้นในกรณีที่หายากมากบางอย่างเช่นเมื่อคุณต้องการค่าผลตอบแทนจากifif

หากคุณมีกลุ่มคำสั่งซึ่งลงท้ายด้วย a ifเช่นนี้:

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

คุณสามารถตัดคำสั่งก่อนหน้าifในเงื่อนไขแทน:

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

หรือถ้าฟังก์ชันของคุณลงท้ายด้วยif:

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

คุณสามารถลบเครื่องมือจัดฟัน:

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

1
คุณสามารถใช้ผู้ประกอบการที่สาม[test] && $if_true || $elseในฟังก์ชั่นเหล่านี้และบันทึกบางไบต์
ckjbgames

นอกจากนี้คุณไม่ต้องการช่องว่างรอบ ๆ&&และ||
roblogic

6

ใช้เลขคณิต(( ... ))สำหรับเงื่อนไข

คุณสามารถแทนที่:

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

โดย

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

(หมายเหตุ: ไม่มีที่ว่างหลังจากนั้นif)

หรือแม้กระทั่ง

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

... หรือถ้ามีเพียงคำสั่งเดียว

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

เพิ่มเติม: ซ่อนการตั้งค่าตัวแปรในโครงสร้างเลขคณิต:

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

หรือเหมือนกัน

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

... ที่ไหนถ้า i> 5 ดังนั้น c = 1 (ไม่ใช่ 0;)


คุณสามารถประหยัด 2 ไบต์โดยใช้แทน[ ] (())
ckjbgames

@ckjbgames คุณแน่ใจนะ! Wich ทุบตีรุ่นที่คุณใช้?
F. Hauri

@Fauri ฉันคิดว่ามันคงเป็นเรื่องเดียวกันในแง่ของไบต์
ckjbgames

@ckjbgames เมื่อ[ ]คุณต้องการเครื่องหมายดอลลาร์สำหรับตัวแปร [ ]ฉันไม่เห็นว่าคุณสามารถทำเช่นเดียวกันที่มีความยาวเดียวกันหรือขนาดเล็กโดยใช้
F. Hauri

เคล็ดลับโบนัส: หากบรรทัดแรกของลูปเริ่มต้นด้วย((...))ไม่จำเป็นต้องขึ้นบรรทัดใหม่หรือเว้นวรรค เช่นfor((;10>n++;)){((n%3))&&echo $n;} ลองออนไลน์!
โม่

6

ไวยากรณ์ที่สั้นกว่าสำหรับลูปไม่ จำกัด (ซึ่งสามารถหลบหนีด้วยbreakหรือexitคำสั่ง) คือ

for((;;)){ code;}

นี้จะสั้นกว่าและwhile true;while :;

หากคุณไม่ต้องการbreak(ด้วยexitวิธีเดียวที่จะหลบหนี) คุณสามารถใช้ฟังก์ชันเรียกซ้ำได้

f(){ code;f;};f

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

f()(code;f);f

6

forลูปหนึ่งบรรทัด

นิพจน์ทางคณิตศาสตร์ที่ต่อกับส่วนขยายช่วงจะถูกประเมินสำหรับแต่ละรายการในช่วง ตัวอย่างเช่นต่อไปนี้:

: $[expression]{0..9}

จะประเมินexpression10 ครั้ง

มักจะสั้นกว่าforลูปที่เทียบเท่า:

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

หากคุณไม่สนใจคำสั่งที่ไม่พบข้อผิดพลาดคุณสามารถลบ inital :ได้ สำหรับการวนซ้ำที่มากกว่า 10 คุณยังสามารถใช้ช่วงอักขระได้เช่น{A..z}จะวนซ้ำ 58 ครั้ง

เป็นตัวอย่างที่ใช้งานได้จริงดังต่อไปนี้สร้างหมายเลขสามเหลี่ยม 50 หมายเลขแรกซึ่งแต่ละรายการอยู่ในสายของตนเอง:

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

คุณสามารถทำซ้ำย้อนหลังได้:for((;0<i--;)){ f;}
roblogic

5

วนรอบอาร์กิวเมนต์มากกว่า

ตามที่ระบุไว้ในทุบตี“สำหรับ” วงโดยไม่ต้องมีส่วนร่วม“ในแถบ foo ...”ที่in "$@;"ในfor x in "$@;"ซ้ำซ้อน

จาก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.

ตัวอย่างเช่นถ้าเราต้องการที่จะยกกำลังสองจำนวนตัวเลขทั้งหมดที่ได้รับข้อโต้แย้งตำแหน่งเพื่อ Bash script หรือฟังก์ชั่นเราสามารถทำเช่นนี้

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

ลองออนไลน์!


5

ทางเลือกที่จะแมว

สมมติว่าคุณกำลังพยายามอ่านไฟล์และใช้งานในสิ่งอื่น สิ่งที่คุณอาจทำคือ:

echo foo `cat bar`

หากเนื้อหาของbarเป็นนี้จะพิมพ์foobarfoo foobar

อย่างไรก็ตามมีทางเลือกอื่นหากคุณใช้วิธีนี้ซึ่งจะช่วยประหยัด 3 ไบต์:

echo foo `<bar`

1
มีเหตุผลทำไม<barด้วยตัวเองไม่ทำงาน แต่วางไว้ใน backticks ไม่?
Kritixi Lithos

@Cowsquack ใช่ <ทำให้ไฟล์ที่จะสั่ง แต่ในกรณีนี้มันทำให้มันเป็นมาตรฐานการส่งออกเนื่องจากมุมแหลม Backticks ประเมินสิ่งนี้ด้วยกัน
Okx

มีวิธีที่สั้นกว่าในการอ่านจากอินพุตมาตรฐานอื่นที่ไม่ใช่`cat`?
Joel

4

ใช้[แทน[[และtestเมื่อเป็นไปได้

ตัวอย่าง:

[ -n $x ]


ใช้=แทน==การเปรียบเทียบ

ตัวอย่าง:

[ $x = y ]

โปรดทราบว่าคุณต้องมีช่องว่างรอบเครื่องหมายเท่ากับมิฉะนั้นจะใช้งานไม่ได้ เช่นเดียวกับ==การทดสอบของฉัน


3
The [vs. [[อาจขึ้นอยู่กับจำนวนราคาที่ต้องการ: pastebin.com/UPAGWbDQ
จัดการ

@ การจัดการนั่นเป็นจุดที่ดี
nyuszika7h

1
กฎทั่วไป: [... ]== /bin/testแต่[[... ]]! = /bin/testและหนึ่งไม่ควรชอบ[... ]มากกว่า[[... ]]นอก codegolf
cat

4

ทางเลือกในการ head

lineสามไบต์สั้นกว่าhead -1แต่จะถูกเลิกใช้

sed qhead -1เป็นสองไบต์สั้นกว่า

sed 9qhead -9เป็นหนึ่งไบต์สั้นกว่า


1
แม้ว่าจะถึงวาระแล้วก็ตามเรายังสามารถใช้งานได้สักพักlineตั้งแต่แพ็กเกจutil-linuxไปจนถึงอ่านบรรทัดเดียว
จัดการ

4

tr -cd สั้นกว่า grep -o

ตัวอย่างเช่นหากคุณต้องการนับช่องว่างgrep -o <char>(พิมพ์เฉพาะส่วนที่ตรงกัน) ให้ 10 ไบต์ในขณะที่tr -cd <char>(ลบส่วนเติมเต็ม<char>) ให้ 9

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

(ที่มา )

โปรดทราบว่าพวกเขาทั้งสองให้ผลลัพธ์ที่แตกต่างกันเล็กน้อย grep -oส่งคืนผลลัพธ์ที่คั่นด้วยบรรทัดขณะที่tr -cdให้ทั้งหมดในบรรทัดเดียวกันดังนั้นtrอาจไม่เป็นผลดีเสมอไป


4

ร่นชื่อไฟล์

ในความท้าทายเมื่อเร็ว ๆ นี้ฉันพยายามอ่านไฟล์/sys/class/power_supply/BAT1/capacityอย่างไรก็ตามสิ่งนี้สามารถย่อให้เล็กลงได้/*/*/*/*/capac*yเนื่องจากไม่มีไฟล์อื่นอยู่ในรูปแบบนั้น

ตัวอย่างเช่นถ้าคุณมีไดเรกทอรีfoo/ที่มีไฟล์foo, bar, foobar, barfooและคุณต้องการที่จะอ้างอิงไฟล์foo/barfooคุณสามารถใช้foo/barf*เพื่อบันทึกไบต์

*"หมายถึงอะไร" และเทียบเท่ากับ .*regex


3

ใช้ท่อไปยังคำสั่งแทน: ในตัวจะกินการป้อนข้อมูลทั้งหมด/dev/null:


2
ไม่มันจะขัดข้องโปรแกรมด้วย SIGPIPE ในกรณีส่วนใหญ่ echo a|tee /dev/stderr|:จะไม่พิมพ์อะไรเลย
jimmy23013

มีการแข่งขัน: echo a|tee /dev/stderr|:พิมพ์บนคอมพิวเตอร์ของฉัน แต่ที่อื่น SIGPIPE อาจฆ่าทีก่อน teeมันอาจจะขึ้นอยู่กับรุ่นของ
kernigh

ใช่มันเป็นปัญหา SIGPIPE: ใช้tee >(:) < <(seq 1 10)งานได้ แต่tee /dev/stderr | :จะไม่ใช้ แม้a() { :;};tee /dev/stderr < <(seq 1 10)| aจะไม่พิมพ์อะไรเลย
F. Hauri

@ user16402 - คุณควรมีชื่อ fccing ในขอบเขตของฉัน ... อย่างไรก็ตามสิ่งที่:แท้จริงไม่ได้กินเลย ... หากคุณใส่อินพุตไปยังลำไส้ใหญ่คุณอาจทำให้ท่อไหลผิดพลาด ... แต่คุณสามารถลอยการเปลี่ยนเส้นทาง โดยโคลอนหรือวางกระบวนการด้วย ... :| while i>&$(($??!$?:${#?})) command shit; do [ -s testitsoutput ]; doneหรืออย่างไรก็ตามข้อเสนอแนะหลอกใช้ ... เช่นกันคุณรู้หรือไม่ว่าผีฉันเกือบจะเป็นฉันเหรอ? ... หลีกเลี่ยงค่าใช้จ่ายทั้งหมด< <(psycho shit i can alias to crazy math eat your world; okay? anyway, ksh93 has a separate but equal composite char placement)
mikeserv

3

splitมีอีก (เลิกใช้ แต่ไม่มีใครใส่ใจ) ไวยากรณ์สำหรับการป้อนข้อมูลแยกเป็นส่วนของNเส้นแต่ละแทนsplit -lNคุณสามารถใช้เช่นsplit -Nsplit -9


3

ขยายการทดสอบออกไป

โดยพื้นฐานแล้วเชลล์เป็นภาษาแมโครชนิดหนึ่งหรืออย่างน้อยไฮบริดหรือบางชนิด บรรทัดคำสั่งทุกบรรทัดสามารถแบ่งออกเป็นสองส่วนโดยทั่วไป: ส่วนการแยกวิเคราะห์ / อินพุตและส่วนขยาย / เอาท์พุท

ส่วนแรกคือสิ่งที่คนส่วนใหญ่มุ่งเน้นเพราะมันง่ายที่สุด: คุณเห็นสิ่งที่คุณได้รับ ส่วนที่สองคือสิ่งที่หลายคนหลีกเลี่ยงไม่ได้แม้กระทั่งพยายามที่จะเข้าใจดีมากและเป็นเหตุผลว่าทำไมผู้คนพูดถึงสิ่งต่าง ๆ เช่นevalความชั่วร้ายและอ้างถึงการขยายของคุณ - ผู้คนต้องการผลลัพธ์ของส่วนแรกให้เท่ากันก่อน ไม่เป็นไร - แต่มันนำไปสู่การขยายสาขารหัสที่ไม่จำเป็นโดยไม่จำเป็นและการทดสอบที่ไม่เกี่ยวข้องมากมาย

ขยายมีการทดสอบตัวเอง ${param[[:]#%+-=?]word}รูปแบบที่มีมากเกินพอที่จะตรวจสอบเนื้อหาของพารามิเตอร์ที่มี Nestable และทุกคนตามรอบการประเมินสำหรับNUL - ซึ่งเป็นสิ่งที่คนส่วนใหญ่คาดหวังของการทดสอบอยู่แล้ว +มีประโยชน์เป็นพิเศษในลูป:

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

... ในขณะที่readดึงในบรรทัดว่างไม่"${r:+set}"ขยาย"set"และตำแหน่งได้รับการ$rต่อท้าย แต่เมื่อบรรทัดว่างเป็นread, $rเป็นที่ว่างเปล่าและ"${r:+set}"ขยายไป""- ซึ่งเป็นที่ไม่ถูกต้องคำสั่ง แต่เนื่องจากบรรทัดคำสั่งถูกขยายก่อนที่""คำสั่ง null จะถูกค้นหาจึง"${r:=$*}"ใช้ค่าของ positionals ทั้งหมดที่ต่อกันบนไบต์แรก$IFSด้วย r()อาจถูกเรียกอีกครั้งใน|{คำสั่งผสม;}ด้วยค่าที่แตกต่างกันสำหรับ$IFSการรับย่อหน้าอินพุตถัดไปเช่นกันเนื่องจากเป็นสิ่งผิดกฎหมายสำหรับเชลล์ที่readจะบัฟเฟอร์เกิน\newline ถัดไปในอินพุต


3

ใช้การเรียกซ้ำหางเพื่อทำให้ลูปสั้นลง:

สิ่งเหล่านี้เทียบเท่ากับพฤติกรรม (แม้ว่าอาจไม่ได้อยู่ในการใช้งานหน่วยความจำ / PID):

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

และสิ่งเหล่านี้เทียบเท่ากันโดยประมาณ:

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

(ในทางเทคนิคทั้งสามครั้งล่าสุดจะดำเนินการร่างกายอย่างน้อยหนึ่งครั้ง)

การใช้$0ต้องให้สคริปต์ของคุณอยู่ในไฟล์ไม่ใช่วางลงในพรอมต์ bash

ในที่สุดสแต็กของคุณอาจล้น แต่คุณบันทึกบางไบต์


3

บางครั้งก็จะสั้นกว่าที่จะใช้ในตัวสำหรับการแสดงผลของการแสดงออกทางคณิตศาสตร์ที่เรียบง่ายแทนปกติexpr echo $[ ]ตัวอย่างเช่น:

expr $1 % 2

หนึ่งไบต์สั้นกว่า:

echo $[$1%2]

2

ใช้pwdแทนechoการสร้างบรรทัดเอาต์พุต

ต้องการวางบรรทัดบน stdout แต่ไม่สนใจเนื้อหาและต้องการ จำกัด คำตอบของคุณสำหรับ shell builtins? เป็นไบต์สั้นกว่าpwdecho


2

สามารถละเว้นเครื่องหมายคำพูดได้เมื่อพิมพ์สตริง

echo "example"
echo example

เอาต์พุตใน SM-T335 LTE, Android 5.1.1:

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

2

เมื่อกำหนดไอเท็มอาเรย์ที่ไม่ต่อเนื่องคุณยังสามารถข้ามดัชนีต่อเนื่องของกลุ่มต่อเนื่อง:

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)

ผลลัพธ์เหมือนกัน:

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")

ตามman bash:

อาร์เรย์ได้รับมอบหมายให้ใช้การกำหนดสารประกอบของแบบฟอร์มชื่อ = (มูลค่า1 ... ค่าn ) ซึ่งแต่ละค่าจะอยู่ในรูป [ ห้อย ] = สตริง ที่ได้รับมอบหมายอาร์เรย์จัดทำดัชนีไม่จำเป็นต้องมีอะไร แต่ สตริง เมื่อกำหนดให้กับอาร์เรย์ที่มีการทำดัชนีหากมีการใส่วงเล็บและตัวห้อยที่เป็นตัวเลือกดัชนีนั้นจะถูกกำหนดให้; มิฉะนั้นดัชนีขององค์ประกอบที่กำหนดเป็นดัชนีสุดท้ายที่กำหนดโดยคำสั่งบวกหนึ่ง


สิ่งที่เป็นประโยชน์ในการเพิ่ม: องค์ประกอบที่ไม่ได้กำหนดค่าเริ่มต้นจะขยายเป็น 0 ในส่วนขยายทางคณิตศาสตร์และ "" ในส่วนขยายอื่น
บาดเจ็บทางระบบดิจิตอล

2

พิมพ์คำแรกในสตริง

หากสตริงอยู่ในตัวแปรaและไม่มีตัวอักษรยกเว้นและรูปแบบ ( \และ%) ให้ใช้สิ่งนี้:

printf $a

แต่มันจะยาวกว่ารหัสต่อไปนี้หากต้องการบันทึกผลลัพธ์ลงในตัวแปรแทนที่จะพิมพ์:

x=($a)
$x

1

ทำการวนซ้ำ 2 ฝังโดยมี 1 forคำสั่ง:

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

กำหนดและพิมพ์สตริงที่ยกมา

หากคุณต้องการกำหนดสตริงที่ยกมาให้กับตัวแปรแล้วพิมพ์ค่าของตัวแปรนั้นดังนั้นวิธีปกติในการทำเช่นนั้นคือ:

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

หากaก่อนหน้านี้ไม่มีการตั้งค่าสิ่งนี้อาจสั้นลงไปที่:

echo ${a=Programming Puzzles & Code Golf}

หากaตั้งค่าไว้ก่อนหน้านี้ควรใช้สิ่งนี้แทน:

echo ${a+Programming Puzzles & Code Golf}

หมายเหตุสิ่งนี้มีประโยชน์ก็ต่อเมื่อสตริงต้องมีเครื่องหมายอัญประกาศ ไม่มีคำพูดa=123;echo $aสั้น ๆ


${a+foo}aไม่ได้ตั้งค่า
GammaFunction
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.