回答:
文書化されていませんが、レガシーsh
後方互換性のために私が遭遇したすべてのバージョンで動作します:
for
ループを使用すると、の{
}
代わりに使用できますdo
done
。例:置換:
for i in {1..10};do echo $i; done
で:
for i in {1..10};{ echo $i;}
sh
いたことと、そのためにBashでも許可されていることをどこかで読んだことを覚えていますが、残念ながら引用はありません。
csh
、おそらく-それは彼らがそのシェルで働いた方法です。
ksh93
上記のことが考えられます;{1..10}
し、中bash
:printf %s\\n {1..10}
for((;i++<10)){ echo $i;}
より短いfor i in {1..10};{ echo $i;}
算術膨張使用するための$[…]
代わりの$((…))
:
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 bash、バージョン4.3.46(1)
y=`bc<<<"($x*2.2)%10/1"`
... bc
整数以外の計算に使用する例... /1
最後に、結果の小数が整数に切り捨てられることに注意してください。
s=$((i%2>0?s+x:s+y))
... bash算術で三項演算子を使用する例。それはより短いですif..then..else
か[ ] && ||
GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)
あり、それは私のものではありません。
関数を定義する通常の、長くて退屈な方法は
f(){ CODE;}
この男が見つけた、あなたは絶対に前にスペース必要CODE
とそれの後にセミコロンを。
これは、@ DigitalTraumaから学んだ小さなトリックです。
f()(CODE)
これは2文字短く、同様に機能します。ただし、関数が戻った後、変数の値の変更を引き継ぐ必要はありません(括弧は本体をサブシェルで実行します)。
@ jimmy23013はコメントで指摘し、でも、括弧は不要です。
Bashのリファレンスマニュアルは、次のような機能を定義することができることを示しています。
name () compound-command [ redirections ]
または
function name [()] compound-command [ redirections ]
複合コマンドを使用できます
until
、while
またはfor
if
、case
、((...))
または[[...]]
(...)
または{...}
つまり、次のすべてが有効です。
$ f()if $1;then $2;fi
$ f()($1&&$2)
$ f()(($1)) # This one lets you assign integer values
そして、私は吸盤のような中括弧を使用しています...
f()while ...
f()if ...
およびその他の複合コマンドも使用できることに注意してください。
f()CODE
は合法だと思ったので驚いた。これはf()echo hi
、pdkshとzshでは有効ですが、bashでは有効ではありません。
for
デフォルトでポジショニング:f()for x do : $x; done;set -x *;PS4=$* f "$@"
または何かになっているため、特に便利です。
その他のヒント
三項演算子、((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
また、可能な限り括弧を括弧で囲みます。括弧は単語ではなくメタ文字であるため、どのようなコンテキストでもスペースを必要としません。これは、サブシェルで可能な限り多くのコマンドを実行することも意味します。中括弧({
および}
)はメタ文字ではなく予約語であり、したがって、正しく解析するには両側に空白が必要ですが、メタ文字はそうではありません。サブシェルは親環境に影響を与えないことをご存知だと思いますので、すべてのサンプルコマンドをサブシェルで安全に実行できると仮定すると(いずれの場合も一般的ではありません)、上記のコードをこれに短縮できます:
t=$something
[ $t == "hi" ]&&(cmd1;cmd2)||[ $t == "bye" ]&&(cmd3;cmd4)||(cmd5;[ $t == "sup" ]&&cmd6)
また、できない場合は、かっこを使用することで多少縮小することもできます。覚えておくべきことの1つは、整数に対してのみ機能するため、この例の目的には役に立たないことです(ただし-eq
、整数に使用するよりもはるかに優れています)。
もう1つ、可能な場合は引用符を避けます。上記のアドバイスを使用して、さらに縮小できます。例:
t=$something
[ $t == hi ]&&(cmd1;cmd2)||[ $t == bye ]&&(cmd3;cmd4)||(cmd5;[ $t == sup ]&&cmd6)
テスト条件では、いくつかの例外を除いて、できる限りシングルブラケットをダブルブラケットにすることをお勧めします。2文字を無料でドロップしますが、場合によってはそれほど堅牢ではありません(Bash拡張機能-例については以下を参照)。また、doubleではなく、単一のequals引数を使用します。ドロップする無料のキャラクターです。
[[ $f == b ]]&&: # ... <-- Bad
[ $f == b ]&&: # ... <-- Better
[ $f = b ]&&: # ... <-- Best. word splits and pathname-expands the contents of $f. Esp. bad if it starts with -
特に、null出力または未定義の変数をチェックする際には、この警告に注意してください。
[[ $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
したがって、この投稿の教訓は次のとおりです。
if
if-else
case
...をチェックしてくださいin
。PS:コンテキストに関係なく、Bashで認識されるメタ文字のリストを以下に示します(単語を分離できます)。
< > ( ) ; & | <space> <tab>
編集:manatworkが指摘したように、二重括弧テストは整数に対してのみ機能します。また、間接的に、==
演算子を囲む空白が必要であることがわかりました。上記の私の投稿を修正しました。
また、各セグメントの長さを再計算するのが面倒だったので、単純にそれらを削除しました。必要に応じて、オンラインで文字列の長さ計算機を見つけるのは簡単です。
[ $t=="hi" ]
として解析されるため、常に0に評価され[ -n "STRING" ]
ます。(($t=="hi"))
文字列は算術評価で整数に強制されるため、$ tが非数値である限り、常に0に評価されます。いくつかのテストケース:pastebin.com/WefDzWbL
case
が短くなります。また、前}
にスペースは必要ありませんが、後に必要です{
。
=
より堅牢では==
ないのでしょうか?=
POSIXによって義務付けられて==
いますが、そうではありません。
代わりにgrep -E
、grep -F
、grep -r
、使用egrep
、fgrep
、rgrep
、2つの文字を保存します。短いものは非推奨ですが、正常に機能します。
(回答ごとに1つのヒントをお願いしました!)
Pgrep
ですgrep -P
。pgrep
プロセスを検索するために使用されると簡単に混同される可能性がありますが、
変数の内容をパイプラインの次のプロセスのSTDINに渡す必要がある場合、一般的に変数をパイプラインにエコーします。しかし、<<<
bash here文字列で同じことを実現できます。
$ s="code golf"
$ echo "$s"|cut -b4-6
e g
$ cut -b4-6<<<"$s"
e g
$
s=code\ golf
、echo $s|
および<<<$s
(を念頭に置いているなど、何の繰り返しにスペースがないという理由だけで、後者の2つの作品)。
避けてください$( ...command... )
、1つの文字を保存して同じことをする代替案があります:
` ...command... `
$( )
ネストされたコマンド置換がある場合に必要になることがあります。それ以外の場合は、内側から脱出する必要があります``
$()
、scp
ターゲットマシンの代わりに自分のマシンで置換を実行したいときではなく、バックティックを使用する必要がありました。ほとんどの場合、それらは同一です。
$()
適切に引用すれば、彼らができることは何でもできます。($
バックティックスではなく、むさぼる何かを生き残るためにあなたの命令が必要でない限り)。それらの中にあるものを引用する際に、微妙な違いがいくつかあります。 mywiki.wooledge.org/BashFAQ/082にはいくつかの違いが説明されています。ゴルフをしているのでなければ、バックティックを使用しないでください。
echo `bc <<<"\`date +%s\`-12"`
...(コメントにバック
if
コマンドをグループ化するために使用を削除するこのヒントと比較すると、これは、if
からの戻り値が必要な場合など、非常にまれなケースでのみうまく機能するはずですif
。
次の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
[test] && $if_true || $else
これらの関数で三項演算子を使用して、いくつかのバイトを節約できます。
&&
と||
(( ... ))
条件に算術を使用する交換できます:
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
}
...または1つのコマンドのみの場合
((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バイト節約できます(())
。
[ ]
、変数にドル記号が必要です。を使用して同じまたはより短い長さで同じことができる方法がわかりません[ ]
。
((...))
改行やスペースは不要です。たとえばfor((;10>n++;)){((n%3))&&echo $n;}
、オンラインでお試しください!
無限ループ(break
or exit
ステートメントでエスケープ可能)の短い構文は次のとおりです。
for((;;)){ code;}
これは、より短いwhile true;
とwhile :;
。
break
(exit
エスケープする唯一の方法として)必要ない場合は、代わりに再帰関数を使用できます。
f(){ code;f;};f
ブレークする必要があるが、終了する必要はなく、変数の変更をループ外に持ち越す必要がない場合、サブシェルで関数bodyを実行するbodyの周りに括弧を付けて再帰関数を使用できます。
f()(code;f);f
for
ループ範囲拡張と連結された算術式は、範囲内の各アイテムに対して評価されます。たとえば、次のとおりです。
: $[expression]{0..9}
expression
10回評価されます。
多くの場合、これは同等の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;}
「in foo bar…」部分のないBashの「for」ループで述べたように、in "$@;"
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スクリプトまたは関数への位置引数を指定してすべての数値を2乗する場合、これを実行できます。
for n;{ echo $[n*n];}
ファイルを読み取って、他のファイルで使用しようとしているとします。あなたがするかもしれないことは:
echo foo `cat bar`
内容が場合bar
だったfoobar
、これは印刷しfoo foobar
。
ただし、3バイトを節約するこの方法を使用している場合は、別の方法があります。
echo foo `<bar`
<bar
単独では機能しませんが、バックティックに配置することで機能する理由はありますか?
<
コマンドにファイルを置きますが、この場合には、それが原因癖に標準出力にそれを置きます。バックティックはこれを一緒に評価します。
`cat`
ますか?
[
代わりに使用する[[
test
例:
[ -n $x ]
=
代わりに使用==
例:
[ $x = y ]
等号の前後にスペースが必要です。そうしないと機能しません。同じことが==
私のテストに基づいて適用されます。
[
... ]
== /bin/test
、しかし[[
... ]]
!= /bin/test
と1を好むことはありません[
... ]
上[[
... ]]
codegolfの外
:
コマンドの代わりにパイプを使用し/dev/null
ます。:
ビルトインは、そのすべての入力を食べるようになります。
echo a|tee /dev/stderr|:
何も印刷しません。
echo a|tee /dev/stderr|:
私のコンピューターでaを印刷しましたが、他の場所ではSIGPIPEが最初にティーを殺すかもしれません。のバージョンに依存する場合がありtee
ます。
tee >(:) < <(seq 1 10)
動作しますが、動作しtee /dev/stderr | :
ません。でも、a() { :;};tee /dev/stderr < <(seq 1 10)| a
何も印刷されません。
:
組み込み関数はまったく食べません...コロンへの入力を仮定すると、エラーをパイプにあふれさせる可能性があります...しかし、リダイレクトをフロートすることができますコロンを使用するか、それを使用してプロセスをドロップします... :| 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)
split
入力をN
各行のセクションに分割するための別の(非推奨ですが、誰も気にしない)構文がありsplit -lN
ます:代わりにsplit -N
eg を使用できますsplit -9
。
本質的に、シェルは一種のマクロ言語、または少なくともハイブリッドまたは何らかの種類です。すべてのコマンドラインは、基本的に、解析/入力部分と展開/出力部分の2つの部分に分けることができます。
最初の部分は、ほとんどの人が最もシンプルであるため、何に焦点を合わせているかです。あなたが得るものを見ることができます。2番目の部分は、多くの人が非常によく理解しようとしてさえ回避することであり、人々eval
が悪のようなことを言い、常に拡張を引用する理由です。それは大丈夫です-しかし、それは不必要に長いコードブランチと大量の外部テストにつながります。
拡張は自己テストです。${param[[:]#%+-=?]word}
フォームは、パラメータの内容を検証するために十分以上のものネスト可能であり、すべてのための評価に基づいてい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
... while read
空行ではなくプルインが"${r:+set}"
展開さ"set"
れ、位置が$r
追加されます。しかし、空白行がのread
場合、$r
は空で"${r:+set}"
展開されます""
-これは無効なコマンドです。ただし、nullコマンドが検索される前にコマンドラインが展開されるため、最初のバイトで連結されたすべての位置の値も取得します。複合コマンドで次の入力段落を取得するための別の値を使用して再度呼び出すことができます。これは、シェルが入力の次の改行を超えてバッファすることは違法であるためです。""
"${r:=$*}"
$IFS
r()
|{
;}
$IFS
read
\n
末尾再帰を使用して、ループを短くします。
これらは動作が同等です(ただし、メモリ/ 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
(技術的には、最後の3つは常に少なくとも1回は本体を実行します)
を使用$0
するには、bashプロンプトに貼り付けるのではなく、ファイルにスクリプトを含める必要があります。
最終的にはスタックがオーバーフローする可能性がありますが、いくつかのバイトを節約します。
pwd
代わりに使用してecho
、出力の行を生成しますstdoutに行を追加する必要がありますが、内容は気にせず、シェルの組み込みコマンドへの回答を制限しますか?pwd
は、より短いバイトですecho
。
文字列を印刷するときに引用符を省略することができます。
echo "example"
echo example
SM-T335 LTE、Android 5.1.1での出力:
u0_a177@milletlte:/ $ echo "example"
example
u0_a177@milletlte:/ $ echo example
example
非連続配列項目を割り当てる場合、連続チャンクの連続インデックスをスキップできます:
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
:
配列は、name =(value 1 ... value n)という形式の複合割り当てを使用して割り当てられます。各値は[ subscript ] = stringの形式です。インデックス付き配列の割り当てには、string以外は必要ありません 。インデックス付き配列に割り当てるときに、オプションのブラケットと添え字が指定されている場合、そのインデックスが割り当てられます。それ以外の場合、割り当てられた要素のインデックスは、ステートメントに割り当てられた最後のインデックスに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
。
sh
このfor
構文を許可するのですか?で明示的に許可されていzsh
ます。