Answers:
未记录,但可在我遇到的所有版本中使用,以实现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)
这要短两个字符,并且只要您不需要在函数返回后保留变量值的任何更改(括号在subshell中运行主体),它就可以正常工作。
正如@ 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
因为它默认为positionals:之类的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
另外,请尽可能使用括号将大括号括起来。由于括号是一个元字符,而不是一个单词,因此它们在任何上下文中都不需要空格。这也意味着在子shell中运行尽可能多的命令,因为花括号(即{
和}
)是保留字,而不是元字符,因此必须在两边都留有空格才能正确解析,而元字符则不需要。我假设您现在知道子shell不会影响父环境,因此,假设所有示例命令都可以安全地在子shell中运行(在任何情况下都是不常见的),则可以将上面的代码缩短为:
t=$something
[ $t == "hi" ]&&(cmd1;cmd2)||[ $t == "bye" ]&&(cmd3;cmd4)||(cmd5;[ $t == "sup" ]&&cmd6)
另外,如果不能,使用括号仍然可以缩小括号。要记住的一件事是,它仅适用于整数,因此在本示例中,它变得毫无用处(但比-eq
用于整数要好得多)。
还有一件事,请尽可能避免使用引号。使用以上建议,您可以进一步缩小规模。例:
t=$something
[ $t == hi ]&&(cmd1;cmd2)||[ $t == bye ]&&(cmd3;cmd4)||(cmd5;[ $t == sup ]&&cmd6)
在测试条件下,除少数例外,尽可能将单括号替换为双括号。它免费删除了两个字符,但是在某些情况下却不那么健壮(它是Bash扩展-参见下面的示例)。另外,请使用单个equals参数,而不要使用double。这是一个自由的角色。
[[ $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
因此,这篇文章的寓意是这样的:
if
/ if-else
/ etc。结构体。case
... in
,因为它可以节省很多字节,尤其是在字符串匹配中。PS:这是在Bash中识别的元字符列表,与上下文无关(并且可以分隔单词):
< > ( ) ; & | <space> <tab>
编辑:如manatwork指出,双括号测试仅适用于整数。另外,间接地,我发现您需要在==
运算符周围留有空格。更正了我上面的帖子。
我也懒得重新计算每个段的长度,因此我只是删除了它们。如有必要,在网上找到一个字符串长度计算器应该足够容易。
[ $t=="hi" ]
解析为时,其值始终为0 [ -n "STRING" ]
。(($t=="hi"))
只要$ t具有非数字值,它将始终计算为0,因为在算术评估中将字符串强制为整数。一些测试案例:pastebin.com/WefDzWbL
case
在这里使用a 会更短。另外,您在之前不需要空格}
,但在之后需要空格{
。
=
没有那么健壮==
?=
是POSIX强制要求的,==
不是。
相反的grep -E
,grep -F
,grep -r
,使用egrep
,fgrep
,rgrep
,省去了两个字符。较短的那些已被弃用,但可以正常工作。
(您确实要求每个答案一个提示!)
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
(记住,后两个工作只是因为没有连续的空格,等等)。
避免使用$( ...command... )
,另一种方法是保存一个字符并执行相同的操作:
` ...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
}
...或者如果只有一个命令
((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
循环将对范围内的每个项目评估与范围扩展串联的算术表达式。例如以下内容:
: $[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;}
如Bash的“ for”循环中没有“ in foo bar ...”部分所述,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脚本或函数的位置参数的所有数字求平方,则可以执行此操作。
for n;{ echo $[n*n];}
假设您正在尝试读取文件并将其用于其他用途。您可能会做的是:
echo foo `cat bar`
如果bar
was 的内容foobar
将打印出来foo foobar
。
但是,如果使用此方法,则有另一种选择,它可以节省3个字节:
echo foo `<bar`
<bar
它本身不起作用而是将其放在反引号中呢?
<
把一个文件的命令,但在这种情况下,将其放入标准输出因怪癖。反引号一起对此进行评估。
`cat`
?
[
代替[[
和test
例:
[ -n $x ]
=
代替==
比较例:
[ $x = y ]
请注意,等号周围必须有空格,否则它将不起作用。同样适用于==
根据我的测试。
[
... ]
== /bin/test
,但[[
... ]]
!= /bin/test
和一个永远不应该喜欢[
...而]
不是[[
... ]]
在代码高尔夫球之外
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
可能并不总是有利的。
使用管道:
代替命令/dev/null
。在:
内置的会吃它的所有输入。
echo a|tee /dev/stderr|:
将不会打印任何内容。
echo a|tee /dev/stderr|:
我的计算机上确实打印过一个,但是在其他地方,SIGPIPE可能会先杀死T恤。它可能取决于的版本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
。
本质上,shell是一种宏语言,或者至少是一种混合语言或某种语言。每个命令行基本上可以分为两部分:解析/输入部分和扩展/输出部分。
第一部分是大多数人关注的重点,因为这是最简单的:您会看到所得到的。第二部分是许多人甚至试图很好地理解也避免的事情,这就是为什么人们说诸如此类的事情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
...同时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
(从技术上讲,后三个总是至少执行一次身体)
使用$0
要求您的脚本位于文件中,而不是粘贴到bash提示符中。
最终,您的堆栈可能会溢出,但是您节省了一些字节。
pwd
而不是echo
生成一行输出是否需要在stdout上加一行,但不关心内容,并希望将答案限制在shell内置函数上?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
:
使用名称 =(值1 ...值n)形式的复合分配将数组分配给数组,其中每个值的形式为[ 下标 ] = string。索引数组分配只需要 string即可。分配给索引数组时,如果提供了可选的方括号和下标,则将该索引分配给;否则,分配的元素的索引是该语句分配的最后一个索引加一个。
如果要给带引号的字符串分配一个变量,然后输出该变量的值,则通常的方法是:
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
什么shell,什么shell允许这种for
语法?明确允许zsh
。