Lời khuyên cho việc chơi golf ở Bash


55

Bạn có lời khuyên chung nào cho việc chơi golf ở Bash? Tôi đang tìm kiếm những ý tưởng có thể được áp dụng cho các vấn đề về golf nói chung ít nhất là cụ thể đối với Bash (ví dụ: "xóa bình luận" không phải là một câu trả lời). Xin vui lòng gửi một lời khuyên cho mỗi câu trả lời.

Câu trả lời:


36

Không có giấy tờ , nhưng hoạt động trong mọi phiên bản tôi đã chạy đểshtương thích ngược:

forvòng lặp cho phép bạn sử dụng { }thay vì do done. Ví dụ: thay thế:

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

với:

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

shell nào là shell shvà shell nào cho phép forcú pháp này ? nó được cho phép rõ ràng trong zsh.
mikeerv

@mikeerv Bash. Tôi nhớ đã đọc ở đâu đó rằng cú pháp này được cho phép ở một số cũ shvà Bash cũng cho phép nó vì điều đó, mặc dù buồn là tôi không có một trích dẫn.
Chấn thương kỹ thuật số

à ... cshcó lẽ - đó là cách họ làm việc trong cái vỏ đó.
mikeerv

Nhân tiện, trong ksh93những điều trên có thể là: ;{1..10}và trong bash:printf %s\\n {1..10}
mikeerv

1
for((;i++<10)){ echo $i;}ngắn hơnfor i in {1..10};{ echo $i;}
Evan Krall

29

Để sử dụng mở rộng số học $[…]thay vì $((…)):

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

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

Trong các mở rộng số học không sử dụng $:

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

Mở rộng số học được thực hiện trên các chỉ mục mảng được lập chỉ mục, vì vậy không sử dụng $cả ở đó:

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

Trong các mở rộng số học không sử dụng ${…}:

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

Thay thế while((i--)), hoạt động, với while[i--]hoặc while $[i--]không làm việc cho tôi. GNU bash, phiên bản 4.3.46 (1)
Glenn Randers-Pehrson

1
Đúng, @ GlennRanders-Pehrson. Đó không phải là để làm việc.
manatwork

y=`bc<<<"($x*2.2)%10/1"`... ví dụ về việc sử dụng bccho các phép tính không nguyên ... lưu ý /1ở cuối rút ngắn số thập phân kết quả thành một số nguyên.
roblogic

s=$((i%2>0?s+x:s+y))... ví dụ về việc sử dụng toán tử ternary trong số học bash. Nó ngắn hơn if..then..elsehoặc[ ] && ||
roblogic

1
@manatwork Cảm ơn. Họ phải loại bỏ nó. Tôi vào GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)và nó không phải của tôi.
Giô-na

19

Cách bình thường, dài và nhàm chán để xác định hàm là

f(){ CODE;}

Như anh chàng này phát hiện ra, bạn hoàn toàn cần không gian trước CODEvà dấu chấm phẩy sau nó.

Đây là một mẹo nhỏ mà tôi đã học được từ @DigitalTrauma :

f()(CODE)

Đó là hai ký tự ngắn hơn và nó cũng hoạt động tốt, với điều kiện là bạn không cần phải thực hiện bất kỳ thay đổi nào trong các giá trị của biến sau khi hàm trả về ( dấu ngoặc đơn chạy phần thân trong một khung con ).

Như @ jimmy23013 chỉ ra trong các bình luận, ngay cả dấu ngoặc đơn cũng có thể không cần thiết.

Các tay Bash Reference cho thấy chức năng này có thể được định nghĩa như sau:

name () compound-command [ redirections ]

hoặc là

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

Một lệnh ghép có thể là:

  • Cấu trúc vòng lặp : until, whilehoặcfor
  • một cấu trúc điều kiện: if, case, ((...))hoặc[[...]]
  • Các lệnh được nhóm: (...)hoặc{...}

Điều đó có nghĩa là tất cả những điều sau đây là hợp lệ:

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

Và tôi đã sử dụng dấu ngoặc nhọn như một cái mút ...


2
Lưu ý rằng bạn cũng có thể sử dụng f()while ... f()if ...và các lệnh ghép khác.
jimmy23013

Điều này làm tôi ngạc nhiên vì tôi nghĩ f()CODElà hợp pháp. Hóa ra đó f()echo hilà hợp pháp trong pdksh và zsh, nhưng không phải trong bash.
hạt nhân

1
nó đặc biệt hữu ích với forviệc nó mặc định theo vị trí: f()for x do : $x; done;set -x *;PS4=$* f "$@"hoặc một cái gì đó.
mikeerv

16

:là một lệnh không làm gì cả, trạng thái thoát của nó luôn thành công, vì vậy nó có thể được sử dụng thay thế true.


Sử dụng một lớp con và đường ống, nó sẽ sử dụng cùng một số byte, nhưng đường ống sẽ thực tế hơn.
ckjbgames

5
Ngoại trừ khi bạn làm:(){:|:}
enedil 17/07/17

14

Thêm lời khuyên

  1. Lạm dụng toán tử ternary, ((test)) && cmd1 || cmd2hoặc [ test ] && cmd1 || cmd2, càng nhiều càng tốt.

    Ví dụ (số lượng chiều dài luôn loại trừ dòng trên cùng):

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

    Bằng cách chỉ sử dụng các toán tử ternary, điều này có thể dễ dàng rút ngắn thành:

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

    Như nyuszika7h đã chỉ ra trong các bình luận, ví dụ cụ thể này có thể được rút ngắn hơn nữa bằng cách sử dụng case:

    t="$something"
    case $t in "hi")cmd1;cmd2;;"bye")cmd3;cmd4;;*)cmd5;[ $t == "sup" ]&&cmd6;esac
  2. Ngoài ra, thích dấu ngoặc đơn để niềng răng càng nhiều càng tốt. Vì dấu ngoặc đơn là một metacharacter, và không phải là một từ, chúng không bao giờ yêu cầu khoảng trắng trong bất kỳ bối cảnh nào. Điều này cũng có nghĩa là chạy càng nhiều lệnh trong một lớp con càng tốt, bởi vì dấu ngoặc nhọn (nghĩa là {}) là các từ dành riêng, không phải là ký tự meta và do đó phải có khoảng trắng ở cả hai bên để phân tích chính xác, nhưng các ký tự meta thì không. Tôi giả sử rằng bây giờ bạn biết rằng các lớp con không ảnh hưởng đến môi trường cha, vì vậy giả sử rằng tất cả các lệnh ví dụ có thể được chạy một cách an toàn trong một lớp con (không phải là điển hình trong mọi trường hợp), bạn có thể rút ngắn đoạn mã trên thành điều này :

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

    Ngoài ra, nếu bạn không thể, sử dụng dấu ngoặc đơn vẫn có thể giảm bớt một số. Một điều cần lưu ý là nó chỉ hoạt động cho các số nguyên, điều này làm cho nó vô dụng cho các mục đích của ví dụ này (nhưng nó tốt hơn nhiều so với sử dụng -eqcho các số nguyên).

  3. Một điều nữa, tránh trích dẫn khi có thể. Sử dụng lời khuyên trên, bạn có thể giảm thiểu nó. Thí dụ:

    t=$something
    [ $t == hi ]&&(cmd1;cmd2)||[ $t == bye ]&&(cmd3;cmd4)||(cmd5;[ $t == sup ]&&cmd6)
  4. Trong điều kiện thử nghiệm, ưu tiên dấu ngoặc đơn cho dấu ngoặc kép càng nhiều càng tốt với một vài ngoại lệ. Nó giảm hai ký tự miễn phí, nhưng trong một số trường hợp, nó không mạnh bằng (đó là phần mở rộng Bash - xem ví dụ bên dưới). Ngoài ra, sử dụng đối số bằng đơn chứ không phải gấp đôi. Đó là một nhân vật miễn phí để thả.

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

    Lưu ý cảnh báo này, đặc biệt là trong việc kiểm tra đầu ra null hoặc một biến không xác định:

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

    Trong tất cả các kỹ thuật, ví dụ cụ thể này sẽ là tốt nhất với case... in:

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

Vì vậy, đạo đức của bài này là:

  1. Lạm dụng các toán tử boolean càng nhiều càng tốt và luôn sử dụng chúng thay vì if/ if-else/ etc. công trình.
  2. Sử dụng dấu ngoặc đơn càng nhiều càng tốt và chạy càng nhiều phân đoạn càng tốt trong các ô con vì dấu ngoặc đơn là các ký tự meta và không phải là từ dành riêng.
  3. Tránh trích dẫn càng nhiều càng tốt về thể chất.
  4. Hãy xem case... in, vì nó có thể tiết kiệm khá nhiều byte, đặc biệt là khớp chuỗi.

PS: Đây là danh sách các ký tự meta được nhận dạng trong Bash bất kể ngữ cảnh (và có thể tách các từ):

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

EDIT: Như manatwork đã chỉ ra, kiểm tra dấu ngoặc kép chỉ hoạt động đối với các số nguyên. Ngoài ra, gián tiếp, tôi thấy rằng bạn cần phải có khoảng trắng xung quanh ==toán tử. Sửa bài của tôi ở trên.

Tôi cũng quá lười để tính toán lại độ dài của từng đoạn, vì vậy tôi chỉ cần loại bỏ chúng. Nó phải đủ dễ dàng để tìm một máy tính độ dài chuỗi trực tuyến nếu cần thiết.


Xin lỗi để nói, nhưng bạn có một số lỗi nghiêm trọng ở đó. [ $t=="hi" ]sẽ luôn luôn đánh giá về 0, vì nó được phân tích thành [ -n "STRING" ]. (($t=="hi"))sẽ luôn luôn ước tính về 0 miễn là $ t có giá trị không phải là số, vì các chuỗi được buộc thành số nguyên trong các đánh giá số học. Một số trường hợp thử nghiệm: pastebin.com/WefDzWbL
manatwork

@manatwork Cảm ơn đã bắt. Tôi sẽ cập nhật tương ứng.
Isiah Meadows

Sử dụng một casesẽ được ngắn hơn ở đây. Ngoài ra, bạn không cần một không gian trước }, nhưng bạn làm sau {.
nyuszika7h

1
Tại sao sẽ =kém mạnh mẽ hơn ==? =được ủy quyền bởi POSIX, ==không.
Dennis

1
Câu hỏi yêu cầu một mẹo cho mỗi câu trả lời ...
Toby Speight

7

Thay vì grep -E, grep -F, grep -r, sử dụng egrep, fgrep, rgrep, tiết kiệm hai ký tự. Những cái ngắn hơn được phản đối nhưng hoạt động tốt.

(Bạn đã yêu cầu một lời khuyên cho mỗi câu trả lời!)


1
Quá tệ không có Pgrepcho grep -P. Mặc dù tôi thấy làm thế nào nó có thể dễ bị nhầm lẫn pgrep, được sử dụng để tra cứu các quy trình.
nyuszika7h

1
@ nyuszika7h Cá nhân tôi sử dụng grep -orất nhiều

Nó sẽ không tiết kiệm 3 bao gồm cả không gian?
ckjbgames

7

Phần tử 0 của một mảng chỉ có thể được truy cập bằng tên biến, tiết kiệm năm byte so với chỉ định rõ ràng một chỉ số là 0:

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

7

Nếu bạn cần chuyển nội dung của một biến sang STDIN của quy trình tiếp theo trong một đường ống, thì thông thường sẽ lặp lại biến đó thành một đường ống. Nhưng bạn có thể đạt được điều tương tự với <<< chuỗi bash ở đây :

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

2
Kể từ khi chúng tôi đang chơi golf, s=code\ golf, echo $s|<<<$s(hãy nhớ rằng sau này hai công việc chỉ vì không có dấu cách lặp đi lặp lại, vv).
Dennis

6

Tránh $( ...command... ), có một cách khác để lưu một char và thực hiện điều tương tự:

` ...command... `

9
Đôi khi $( )là cần thiết nếu bạn đã thay thế lệnh lồng nhau; nếu không, bạn phải thoát khỏi nội tâm``
Chấn thương kỹ thuật số

1
Về mặt kỹ thuật, chúng làm những việc khác nhau, tôi đã phải sử dụng backticks thay vì $()khi tôi muốn chạy thay thế trên máy của mình thay vì scpmáy đích, chẳng hạn. Trong hầu hết các trường hợp chúng giống hệt nhau.
undergroundmonorail

2
@undergroundmonorail: bạn không bao giờ cần backticks. Bất cứ điều gì họ có thể làm, $()có thể làm nếu bạn trích dẫn mọi thứ đúng cách. (trừ khi bạn cần lệnh của bạn để tồn tại một cái gì đó munges $nhưng không backticks). Có một số khác biệt tinh tế trong việc trích dẫn những thứ bên trong chúng. mywiki.wooledge.org/BashFAQ/082 giải thích một số khác biệt. Trừ khi bạn đang chơi golf, không bao giờ sử dụng backticks.
Peter Cordes

@PeterCordes Tôi chắc rằng có một cách nhưng tất cả những gì tôi đã cố gắng vào thời điểm đó đã không làm việc. Ngay cả khi backticks không phải là giải pháp tốt nhất, tôi rất vui vì tôi biết về chúng vì đó là giải pháp duy nhất tôi có. ¯ \ _ (ツ) _ / ¯
undergroundmonorail

@DigitalTrauma Mẫu lồng nhau: echo `bc <<<"\`date +%s\`-12"`... (Thật khó để gửi mẫu có chứa backtick trong bình luận, đó!)
F. Hauri

6

Sử dụng ifđể nhóm lệnh

So với mẹo này loại bỏ iftất cả, điều này chỉ nên hoạt động tốt hơn trong một số trường hợp rất hiếm, chẳng hạn như khi bạn cần các giá trị trả về từ if.

Nếu bạn có một nhóm lệnh kết thúc bằng một if, như sau:

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

Bạn có thể bọc các lệnh trước iftrong điều kiện thay thế:

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

Hoặc nếu chức năng của bạn kết thúc bằng if:

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

Bạn có thể loại bỏ niềng răng:

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

1
Bạn có thể sử dụng toán tử ternary [test] && $if_true || $elsetrong các hàm này và lưu một số byte.
ckjbgames

Ngoài ra, bạn không cần không gian xung quanh &&||
roblogic

6

Sử dụng số học (( ... ))cho các điều kiện

Bạn có thể thay thế:

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

bởi

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

(Lưu ý: Không có không gian sau if)

hoặc thậm chí

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

... hoặc nếu chỉ có một lệnh

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

Hơn nữa: Ẩn cài đặt biến trong cấu trúc số học:

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

hoặc giống nhau

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

... trong đó nếu i> 5, thì c = 1 (không phải 0;)


Bạn có thể lưu 2 byte bằng cách sử dụng [ ]thay vì (()).
ckjbgames

@ckjbgames Bạn có chắc chắn về điều đó không!? Mà bash phiên bản bạn đang sử dụng?
F. Hauri

@FHauri Tôi đoán nó sẽ giống nhau về byte.
ckjbgames

@ckjbgames Với [ ]bạn cần ký hiệu đô la cho biến. Tôi không thấy cách bạn có thể làm tương tự với cùng chiều dài hoặc nhỏ hơn bằng cách sử dụng [ ].
F. Hauri

Tiền thưởng: nếu dòng đầu tiên của vòng lặp for bắt đầu bằng ((...)), không cần dòng mới hoặc khoảng trắng. Ví dụ: for((;10>n++;)){((n%3))&&echo $n;} Hãy thử trực tuyến!
primo

6

Một cú pháp ngắn hơn cho các vòng lặp vô hạn (có thể được thoát bằng breakhoặc các exitcâu lệnh) là

for((;;)){ code;}

Cái này ngắn hơn while true;while :;.

Nếu bạn không cần break(với exitcách duy nhất để thoát), bạn có thể sử dụng hàm đệ quy thay thế.

f(){ code;f;};f

Nếu bạn cần nghỉ, nhưng bạn không cần thoát và bạn không cần thực hiện bất kỳ sửa đổi biến nào bên ngoài vòng lặp, bạn có thể sử dụng hàm đệ quy với dấu ngoặc đơn quanh thân , chạy phần thân trong một hàm con.

f()(code;f);f

6

forVòng lặp một dòng

Một biểu thức số học được nối với mở rộng phạm vi sẽ được đánh giá cho từng mục trong phạm vi. Ví dụ như sau:

: $[expression]{0..9}

sẽ đánh giá expression10 lần.

Điều này thường ngắn hơn đáng kể so với forvòng lặp tương đương :

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

Nếu bạn không thấy lỗi lệnh không tìm thấy, bạn có thể loại bỏ inital :. Đối với các lần lặp lớn hơn 10, bạn cũng có thể sử dụng phạm vi ký tự, ví dụ như {A..z}sẽ lặp lại 58 lần.

Như một ví dụ thực tế, cả hai sau đây tạo ra 50 số tam giác đầu tiên, mỗi số trên một dòng riêng của chúng:

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

bạn cũng có thể lặp đi lặp lại:for((;0<i--;)){ f;}
roblogic

5

Lặp lại các đối số

Như đã lưu ý trong vòng lặp Bash Bash cho vòng lặp mà không có phần tử trong phần foo bar , phần in "$@;"trong đó for x in "$@;"là phần thừa.

Từ 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.

Ví dụ: nếu chúng ta muốn bình phương tất cả các số được đưa ra đối số vị trí cho tập lệnh Bash hoặc hàm, chúng ta có thể làm điều này.

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

Hãy thử trực tuyến!


5

Thay thế cho mèo

Giả sử bạn đang cố đọc một tập tin và sử dụng nó trong một cái gì đó khác. Những gì bạn có thể làm là:

echo foo `cat bar`

Nếu nội dung barfoobar, điều này sẽ in foo foobar.

Tuy nhiên, có một cách khác nếu bạn đang sử dụng phương pháp này, giúp tiết kiệm 3 byte:

echo foo `<bar`

1
Có một lý do tại sao <barbản thân nó không hoạt động nhưng đặt nó trong backticks không?
Kritixi Lithos

@Cowsquack Có. Việc <đặt một tập tin vào một lệnh, nhưng trong trường hợp này, nó đặt nó vào đầu ra tiêu chuẩn do một sự cố. Các backticks đánh giá điều này cùng nhau.
Okx

Có cách nào ngắn hơn để đọc từ đầu vào tiêu chuẩn khác `cat`không?
Joel

4

Sử dụng [thay thế [[testkhi có thể

Thí dụ:

[ -n $x ]


Sử dụng =thay vì ==để so sánh

Thí dụ:

[ $x = y ]

Lưu ý rằng bạn phải có khoảng trắng xung quanh dấu bằng nếu không nó sẽ không hoạt động. Áp dụng tương tự ==dựa trên các bài kiểm tra của tôi.


3
Giá trị [so với [[có thể phụ thuộc vào số lượng trích dẫn được yêu cầu: pastebin.com/UPAGWbDQ
manatwork

@manatwork Đó là một điểm tốt.
nyuszika7h

1
Quy tắc chung: [... ]== /bin/test, nhưng [[... ]]! = /bin/testVà người ta không bao giờ nên thích [... ]hơn [[... ]]bên ngoài codegolf
con mèo

4

Các lựa chọn thay thế cho head

linengắn hơn ba byte head -1, nhưng không được dùng nữa .

sed qlà hai byte ngắn hơn head -1.

sed 9qngắn hơn một byte so với head -9.


1
Mặc dù cam chịu , chúng ta vẫn có thể sử dụng một lúc linetừ gói linux-linux để đọc một dòng duy nhất.
thao tác

4

tr -cd ngắn hơn grep -o

Ví dụ: nếu bạn cần đếm khoảng trắng, grep -o <char>(chỉ in phần trùng khớp) sẽ cho 10 byte trong khi tr -cd <char>(xóa phần bù <char>) cho 9.

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

( nguồn )

Lưu ý rằng cả hai đều cho đầu ra hơi khác nhau. grep -otrả về kết quả phân tách dòng trong khi tr -cdcung cấp tất cả chúng trên cùng một dòng, vì vậy trcó thể không phải lúc nào cũng thuận lợi.


4

Rút ngắn tên tập tin

Trong một thử thách gần đây tôi đã cố gắng đọc tệp /sys/class/power_supply/BAT1/capacity, tuy nhiên điều này có thể rút ngắn lại /*/*/*/*/capac*yvì không có tệp nào khác tồn tại với định dạng đó.

Ví dụ: nếu bạn có một thư mục foo/chứa các tệp foo, bar, foobar, barfoovà bạn muốn tham chiếu tệp foo/barfoo, bạn có thể sử dụng foo/barf*để lưu một byte.

Đại *diện cho "bất cứ điều gì", và tương đương với regex .*.


3

Sử dụng một đường ống để :lệnh thay vì /dev/null. Việc tích :hợp sẽ ăn tất cả đầu vào của nó.


2
Không, nó sẽ làm sập chương trình với SIGPIPE trong hầu hết các trường hợp. echo a|tee /dev/stderr|:sẽ không in bất cứ điều gì.
jimmy23013

Có một cuộc đua: echo a|tee /dev/stderr|:đã in một cái trên máy tính của tôi, nhưng ở nơi khác SIGPIPE có thể giết tee trước. Nó có thể phụ thuộc vào phiên bản của tee.
hạt nhân

Vâng, đó là một vấn đề SIGPIPE: tee >(:) < <(seq 1 10)sẽ hoạt động, nhưng tee /dev/stderr | :sẽ không. Thậm chí a() { :;};tee /dev/stderr < <(seq 1 10)| akhông in bất cứ thứ gì.
F. Hauri

@ user16402 - dù sao thì bạn cũng nên đặt tên fccing cho mục đích của tôi ... dù sao đi nữa, :nội tại không ăn gì cả ... nếu bạn cho rằng đầu vào vào dấu hai chấm, bạn có thể làm hỏng một đường ống dẫn đến lỗi ... nhưng bạn có thể chuyển hướng bởi một dấu hai chấm, hoặc bỏ một quá trình với nó ... :| while i>&$(($??!$?:${#?})) command shit; do [ -s testitsoutput ]; donehoặc tuy nhiên đề xuất giả đó được áp dụng ... ngoài ra, bạn có biết bạn cũng gần như ma như tôi không? ... tránh bằng mọi giá< <(psycho shit i can alias to crazy math eat your world; okay? anyway, ksh93 has a separate but equal composite char placement)
mikeerv

3

splitcó khác (bị phản đối, nhưng không ai quan tâm) Cú pháp để tách đầu vào thành phần của Ndòng, mỗi dòng: thay vì split -lNbạn có thể sử dụng split -Nví dụ split -9.


3

Mở rộng các bài kiểm tra

Về cơ bản, shell là một loại ngôn ngữ vĩ mô, hoặc ít nhất là một loại lai hoặc một loại nào đó. Mỗi dòng lệnh về cơ bản có thể được chia thành hai phần: phần phân tích / đầu vào và phần mở rộng / đầu ra.

Phần đầu tiên là những gì hầu hết mọi người tập trung vào vì nó đơn giản nhất: bạn thấy những gì bạn nhận được. Phần thứ hai là điều mà nhiều người tránh thậm chí cố gắng hiểu rất rõ và đó là lý do tại sao mọi người nói những điều như evallà xấu xaluôn trích dẫn những mở rộng của bạn - mọi người muốn kết quả của phần đầu tiên bằng với phần đầu tiên. Điều đó ổn - nhưng nó dẫn đến các nhánh mã dài không cần thiết và hàng tấn thử nghiệm ngoại lai.

Mở rộng là tự kiểm tra . Các ${param[[:]#%+-=?]word}biểu mẫu là quá đủ để xác thực nội dung của một tham số, có thể lồng nhau và tất cả đều dựa trên việc đánh giá NUL - đó là điều mà hầu hết mọi người mong đợi về các bài kiểm tra. +có thể đặc biệt tiện dụng trong các vòng lặp:

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

... Trong khi readkéo vào các dòng không trống "${r:+set}"sẽ mở rộng ra "set"và các vị trí được $rnối thêm. Nhưng khi một dòng trống read, $rtrống và "${r:+set}"mở rộng thành ""- đó là một lệnh không hợp lệ. Nhưng vì các dòng lệnh được mở rộng trước khi các ""lệnh null được tìm kiếm, "${r:=$*}"mất các giá trị của tất cả các positionals nối trên byte đầu tiên trong $IFSlà tốt. r()cũng có thể được gọi lại trong |{lệnh ghép ;}w / một giá trị khác $IFSđể có được đoạn đầu vào tiếp theo, vì việc shell của readbộ đệm nằm ngoài \newline tiếp theo trong đầu vào là bất hợp pháp .


3

Sử dụng đệ quy đuôi để làm cho các vòng lặp ngắn hơn:

Đây là tương đương trong hành vi (mặc dù có thể không sử dụng bộ nhớ / PID):

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

Và những thứ này tương đương nhau:

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

(về mặt kỹ thuật, ba người cuối cùng sẽ luôn thực hiện cơ thể ít nhất một lần)

Việc sử dụng $0yêu cầu tập lệnh của bạn nằm trong một tệp, không được dán vào dấu nhắc bash.

Cuối cùng, ngăn xếp của bạn có thể tràn, nhưng bạn lưu một số byte.


3

Đôi khi nó ngắn hơn để sử dụng exprnội dung để hiển thị kết quả của biểu thức số học đơn giản thay vì thông thường echo $[ ]. Ví dụ:

expr $1 % 2

ngắn hơn một byte so với:

echo $[$1%2]

2

Sử dụng pwdthay vì echođể tạo ra một dòng đầu ra

Cần đặt một dòng trên thiết bị xuất chuẩn nhưng không quan tâm đến nội dung và muốn hạn chế câu trả lời của bạn đối với nội dung shell? pwdlà một byte ngắn hơn echo.


2

Báo giá có thể được bỏ qua khi in chuỗi.

echo "example"
echo example

Đầu ra trong SM-T335 LTE, Android 5.1.1:

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

2

Khi gán các mục mảng không liên tục, bạn vẫn có thể bỏ qua các chỉ số liên tiếp của các khối liên tục:

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)

Kết quả là như nhau:

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

Theo man bash:

Mảng được gán để sử dụng các phép gán ghép của tên biểu mẫu = (giá trị 1 ... giá trị n ), trong đó mỗi giá trị có dạng [ subscript ] = chuỗi . Bài tập mảng được lập chỉ mục không yêu cầu gì ngoài chuỗi . Khi gán cho các mảng được lập chỉ mục, nếu dấu ngoặc và chỉ mục tùy chọn được cung cấp, chỉ mục đó được gán cho; mặt khác, chỉ mục của phần tử được gán là chỉ mục cuối cùng được gán bởi câu lệnh cộng với một.


Hữu ích để thêm: các phần tử chưa được khởi tạo sẽ mở rộng về 0 trong các mở rộng số học và "" trong các mở rộng khác.
Chấn thương kỹ thuật số

2

In từ đầu tiên trong một chuỗi

Nếu chuỗi nằm trong biến avà không chứa ký tự thoát và định dạng ( \%), hãy sử dụng:

printf $a

Nhưng nó sẽ dài hơn đoạn mã sau nếu cần lưu kết quả vào một biến thay vì in:

x=($a)
$x

1

Thực hiện 2 vòng lặp nhúng với 1 forlệnh:

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

Gán và in chuỗi trích dẫn

Nếu bạn muốn gán một chuỗi được trích dẫn cho một biến, sau đó in giá trị của biến đó, thì cách thông thường để làm điều đó sẽ là:

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

Nếu atrước đây không được đặt, điều này có thể được rút ngắn thành:

echo ${a=Programming Puzzles & Code Golf}

Nếu ađã được đặt trước đó, thì cái này nên được sử dụng thay thế:

echo ${a+Programming Puzzles & Code Golf}

Lưu ý rằng điều này chỉ hữu ích nếu chuỗi yêu cầu dấu ngoặc kép (ví dụ chứa khoảng trắng). Không có trích dẫn, a=123;echo $achỉ là ngắn.


${a+foo}không được thiết lập a.
GammaFactor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.