shell 语法

for循环

#!/bin/bash
for language in c c++ java python shell_script; do
echo "my ${language} #变量左右加上{}
done

变量

变量的二次赋值

#!/bin/bash
name="Elena" # = 左右不能有空格
echo "hello, my name is ${name}"
name="Demon"
echo "hello, my name is ${name}"

删除变量

str="Hello World!"
unset str #删除变量 本程序没有任何的输出
echo ${str}

只读变量 【 不能更改,不能删除】

str="Hello World!"
readonly str
str="haha" # /usercode/file.sh: line 4: str: readonly variable
unset str # /usercode/file.sh: line 4: unset: str: cannot unset: readonly variable

引号

单双引号区别

单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
单引号字串中不能出现单引号(对单引号使用转义符后也不行)。

双引号里可以有变量
双引号里可以出现转义字符

反引号的使用

反引号”`”可以将指令括起来,然后交给shell

for file in `ls `; do
file ${file}

字符串部分

连接

#!/bin/bash
name="linux"
str="I love ${name}"
echo $str
str="I love "${name}
echo $str

字符串的长度

#!/bin/bash
str='Hello World!'
echo ${#str} #12

或者使用expr工具 (evalution expression)

#!/bin/bash
str="Hello World!"
echo `expr length "${str}"`

字符串的截取

#!/bin/bash
str='Hello World!'
echo ${str:6:6} #截取第7个字符后的长度是6的字符串 World!

查找字符

expr 可以用于模式匹配。它有一种用法:index string char 用于求解char在string中的位置,记住是从1开始的,如果不存在相应的字符,那么我们返回0.

#!/bin/bash
str="I love linux"
echo `expr index "${str}"

数组

#!/bin/bash
array=("C" "C++" "java" "python" "shell script")
echo ${array[@]} #输出所有的元素
echo ${#array[@]} #输出数组的长度

注释

单行注释是”#”
shell中没有像C那样方便的多行注释,只能一行一行的添加#,不过我们也可以将需要多行注释的地方写成一个函数,想要使用的时候加上就行。

3个fd

从shell中运行一个进程,默认会有3个文件描述符存在(0、1、2), 0与进程的标准输入相关联,
1与进程的标准输出相关联,2与进程的标准错误输出相关联,一个进程当前有哪些打开
的文件描述符可以通过/proc/进程ID/fd目录查看.

shell处理参数

参数处理

说明

$#

传递到脚本的参数个数

$*

以一个单字符串显示所有向脚本传递的参数

$$

脚本运行的当前进程ID号

$!

后台运行的最后一个进程的ID号

$@

与$*相同,但是使用时加引号,并在引号中返回每个参数

$-

显示Shell使用的当前选项,与set命令功能相同

$?

显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误

数值计算

利用工具expr可以实现数值计算。比如:

val=`expr 2 \* 4`
echo "calculate 2 * 4: "${val}

关系运算符

运算符

说明

举例

-eq

检测两个数是否相等,相等返回 true。

[ $a -eq $b ] 返回 false。

-ne

检测两个数是否相等,不相等返回 true。

[ $a -ne $b ] 返回 true。

-gt

检测左边的数是否大于右边的,如果是,则返回 true。

[ $a -gt $b ] 返回 false。

-lt

检测左边的数是否小于右边的,如果是,则返回 true。

[ $a -lt $b ] 返回 true。

-ge

检测左边的数是否大于等于右边的,如果是,则返回 true。

[ $a -ge $b ] 返回 false。

-le

检测左边的数是否小于等于右边的,如果是,则返回 true。

[ $a -le $b ] 返回 true。

布尔运算符

operator

meaning

!


-o


-a


str="I love linux"
length=`expr length "${str}"`
if [ ${length} -gt 20 -o ${length} -lt 0 ]; then
echo "this is bad string"
elif [ ${length} -lt 20 -a ${length} -gt 10 ]; then
echo "the length > 10 and < 20"
fi

逻辑运算符

operator

meaning

||


&&


str="I love linux"
length=`expr length "${str}"`
if [[ ${length} -gt 20 || ${length} -lt 0 ]]; then
echo "this is bad string"
elif [[ ${length} -lt 20 && ${length} -gt 10 ]]; then
echo "the length > 10 and < 20"

逻辑与、或和布尔与、或在形式上的差别是逻辑的if多了一对[]

字符串运算

#!/bin/bash
s1="jordan"
s2="james"
if [ $s1 = $s2 ]; then #字符串相等
echo "s1 = s2"
fi

if [ $s1 != $s2 ]; then #字符串不相等
echo "s1 != s2"
fi

if [ -n $s1 ]; then #字符串非空
echo "the length of s1 is not zero"
fi

if [ -z $s1 ]; then
echo "the length of s1 is zero" #字符串为空
fi

if [ $s2 ]; then
echo "the s2 is not empty" #字符串非空
fi

# output:
# s1 != s2
# the length of s1 is not zero
# the s2 is not empty

文件测试运算符

操作符

说明

举例

-b file

检测文件是否是块设备文件,如果是,则返回 true。

[ -b $file ] 返回 false。

-c file

检测文件是否是字符设备文件,如果是,则返回 true。

[ -c $file ] 返回 false。

-d file

检测文件是否是目录,如果是,则返回 true。

[ -d $file ] 返回 false。

-f file

检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。

[ -f $file ] 返回 true。

-g file

检测文件是否设置了 SGID 位,如果是,则返回 true。

[ -g $file ] 返回 false。

-k file

检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。

[ -k $file ] 返回 false。

-p file

检测文件是否是有名管道,如果是,则返回 true。

[ -p $file ] 返回 false。

-u file

检测文件是否设置了 SUID 位,如果是,则返回 true。

[ -u $file ] 返回 false。

-r file

检测文件是否可读,如果是,则返回 true。

[ -r $file ] 返回 true。

-w file

检测文件是否可写,如果是,则返回 true。

[ -w $file ] 返回 true。

-x file

检测文件是否可执行,如果是,则返回 true。

[ -x $file ] 返回 true。

-s file

检测文件是否为空(文件大小是否大于0),不为空返回 true。

[ -s $file ] 返回 true。

-e file

检测文件(包括目录)是否存在,如果是,则返回 true。

[ -e $file ] 返回 true。

#!/bin/bash
cd /tmp
pwd
touch myfile

if [ -e $myfile ]; then #检测存在
echo "myfile exit"
if [ -b $myfile ]; then #块设备
echo "myfile is block file"
elif [ -c $myfile ]; then #字符设备
echo "myfile is character file"
elif [ -f $myfile ]; then #普通文件
echo "myfile is normal file"
elif [ -d $myfile ]; then #路径
echo "myfile is a dir"
fi

if [ -r $myfile ]; then #读,写,可执行属性
echo "myfile can be read"
fi
if [ -w $myfile ]; then
echo "myfile can be wrote"
fi
if [ -x $myfile ]; then
echo "myfile can be excuted"
fi
fi

输入和输出

read 从标准输入读取一行,赋予相应的变量
echo -e开启转义功能

echo:

echo -e "this is a story about hero.\n"  #没有-e, 就没有多一行
echo "end"

echo -e "this is a story about hero." #正常换行
echo "end"

echo -e "this is a story about hero.\c" #不换行
echo "end"

printf:
printf 和C里面的printf()相似。printf format-string [arguments…]

#!/bin/bash
printf "Hello World\n"
printf "%s %d\n" Jordan 23
name="Jordan"
age=23
printf "%s %d\n" $name $age

关于分支

在shell编程(1)的基础上增加一些补充。
bash里的分支不能是空的。
case分支:

case value in
mod1)
command1
command2
...
commandN
;;
mod2)
command1
command2
...

比如:

echo "enter a number: "
read number
case $number in
1)
echo "you enter 1."
;;
2)
echo "you enter 2."
;;
esac

函数

声明

下面[]中的function 可有可无。

[ function ] funname [()]
{
action;
[return

参数

在函数的内部,我们可以使用​​$n​​​来获取第n个参数的值。在函数外部,函数的返回值则可以通过​​$?​​来获得.

func(){
echo "first arg is "$1;
echo "second arg is "$2;
return $(($1 + $2));
}
func 3 4
echo "the sum of two args is "$?

output:

first arg is 3
second arg is 4
the sum of two args is 7

其他参数

参数

说明

$#

传递到脚本的参数个数

$*

以一个单字符串显示所有向脚本传递的参数

$$

脚本运行的当前进程ID号

$!

后台运行的最后一个进程的ID号

$@

与$*相同,但是使用时加引号,并在引号中返回每个参数。

$-

显示Shell使用的当前选项,与set命令功能相同。

$?

显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

输入输出重定向

文件描述符 0对应标准输入(STDIN),1 对应标准输出(STDOUT),2对应标准错误输出(STDERR)。

command > file #stdout 重定向到 file
command < file #stdin 重定向到 file
command > file file1>&file2 #将输出文件file2和file1合并后重定向到file

例子:

#func.sh:
func(){
echo "first arg is "$1;
echo "second arg is "$2;
return $(($1 + $2));
}
func 3 4
echo "the sum of two args is "$?
func();

#正常执行:
./func.sh
first arg is 3
second arg is 4
the sum of two args is 7
./func.sh: line 8: syntax error near unexpected token `;'
./func.sh: line 8: `func();'

特殊的重定向:

command << delimiter

将两个 delimiter 之间的内容作为输入传递给 command。

练习

二分查找

10 20 30 40 50 60 70 80 90 100)
bsearch(){
l=0
r=9
ans=-1
while (( $l <= $r )); do
mid=`expr $(( $l + $r )) / 2`
if [[ ${array[$mid]} -lt $1 ]]; then
l=`expr $mid + 1`
elif [[ ${array[$mid]} -eq $1 ]]; then
ans=$mid
break
else
r=`expr $mid - 1`
fi
done
#return $ans
}
while true; do
echo "enter a key number to find, -1 will kill program."
read key
if [ $key -eq -1 ]; then
break
fi
bsearch key
if [ $ans -eq -1 ]; then
echo "not found."
else
echo "the index of key is "$ans
fi
done
enter a key number to find, -1 will kill program.
23
not found.
enter a key number to find, -1 will kill program.
30
the index of key is 2
enter a key number to find, -1 will kill program.
10
the index of key is 0
enter a key number to find, -1 will kill program.
100
the index of key is 9
enter a key number to find, -1 will kill program.
2244
not found.
enter a key number to find, -1 will kill program.
50
the index of key is 4
enter a key number to find, -1 will kill program.
-1

快速排序

#!/bin/bash
array=(1 5 3 9 4 8 6 2 7 10)
partion(){
i=$1; j=$2
tmp=${array[$1]}
while [[ $i -lt $j ]]; do
while [[ $i -lt $j && ${array[$j]} -ge $tmp ]]; do
j=`expr $j - 1`
done
array[$i]=${array[$j]}
while [[ $i -lt $j && ${array[$i]} -le $tmp ]]; do
i=`expr $tmp + 1`
done
array[$j]=${array[$i]}
done
array[$i]=$tmp
return $i
}
quick_sort(){
if [ $1 -lt $2 ]; then
partion $1 $2
mid=$?
quick_sort $1 $mid
quick_sort `expr $mid + 1` $2
fi
}
quick_sort 0 9
echo "${array[@]}"

codingame之飞行着陆

飞行着陆
​​​https://www.codingame.com/ide/60414612a754befc323b355d422a6bda40d80b1​​​
大意:每一次削低最高的山峰,避免飞行器撞上山峰。
分析:每一次找出最高的山峰即可。

while true; do
max_h=-1
ans=0
for (( i=0; i<8; i++ )); do
# mountainH: represents the height of one mountain.
read mountainH
if [ ${max_h} -lt ${mountainH} ]; then
ans=$i;
max_h=${mountainH}
fi
done
# Write an action using echo
# To debug: echo "Debug messages..." >&2

echo ${ans} # The index of the mountain to fire on.
done

codingame之reach the light of power

reach the light of power
​​​https://www.codingame.com/ide/60438073859e8a8584667f65ae250e0beea41fb​​​
大意:游戏人物从初始位置需要走到power位置,每一次可以转身,上、下、左、右、斜一共8个方向的转向选择,在给定转身次数的情况下输出转身方向。
分析:向量判断,就近原则。

# Auto-generated code below aims at helping you parse
# the standard input according to the problem statement.
# ---
# Hint: You can use the debug stream to print initialTX and initialTY, if Thor seems not follow your orders.

# lightX: the X position of the light of power
# lightY: the Y position of the light of power
# initialTX: Thor's starting X position
# initialTY: Thor's starting Y position
read lightX lightY initialTX initialTY

# game loop
while true; do
# remainingTurns: The remaining amount of turns Thor can move. Do not remove this line.
read remainingTurns

# Write an action using echo
# To debug: echo "Debug messages..." >&2


# A single line providing the move to be made: N NE E SE S SW W or NW
dx=`expr $lightX - $initialTX`
dy=`expr $lightY - $initialTY`

if [ $dx -gt 0 -a $dy -gt 0 ]; then
string="SE"
initialTX=`expr $initialTX + 1`
initialTY=`expr $initialTY + 1`
elif [ $dx -gt 0 -a $dy -eq 0 ]; then
string="E"
initialTX=`expr $initialTX + 1`
elif [ $dx -gt 0 -a $dy -lt 0 ]; then
string="NE"
initialTX=`expr $initialTX + 1`
initialTY=`expr $initialTY - 1`
elif [ $dx -eq 0 -a $dy -lt 0 ]; then
string="N"
initialTY=`expr $initialTY - 1`
elif [ $dx -lt 0 -a $dy -lt 0 ]; then
string="NW"
initialTX=`expr $initialTX - 1`
initialTY=`expr $initialTY - 1`
elif [ $dx -lt 0 -a $dy -eq 0 ]; then
string="W"
initialTX=`expr $initialTX + 1`
elif [ $dx -lt 0 -a $dy -gt 0 ]; then
string="SW"
initialTX=`expr $initialTX - 1`
initialTY=`expr $initialTY + 1`
elif [ $dx -eq 0 -a $dy -gt 0 ]; then
string="S"
initialTY=`expr $initialTY + 1`
else break
fi

echo $string
done