shell 语法
for循环
#!/bin/bash
for language in c c++ java python shell_script; do
echo "my ${language} skill is good." #变量左右加上{}
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}
done
字符串部分
连接
#!/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}" love`
数组
#!/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"
fi
逻辑与、或和布尔与、或在形式上的差别是逻辑的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
...
commandN
;;
esac
比如:
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 int;]
}
参数
在函数的内部,我们可以使用$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();'
#将标准输出和标准错误合并到文件read中:
./func.sh > read 2>&1
特殊的重定向:
command << delimiter
document
delimiter
将两个 delimiter 之间的内容作为输入传递给 command。
练习
二分查找
array=(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