redirect
> 写入文件时覆盖原文件
>> 从文件后面累加
2> 2>> 输出错误信息
$ find /home -name hh > a 2> b
将正确数据输出到a,错误数据输出到b
$ find /home -name hh > a 2>&1
将数据都输出到a
如果想要抛弃错误信息 $ find /home -name hh > a 2> /dev/null
/dev/null不会接收任何输入
双重redirect
$ tee filename #将数据存入file后再传给下一个命令
通配符、组合按键和特殊符号
符号 | 含义 |
---|---|
* | 匹配0或多个 |
? | 匹配一个 |
& | 将命令在后台运行 |
> | 替换 |
>> | 累加 |
[] | 中间是字符的组合 |
() | 中间是一个子shell |
`` | 中间是先执行的命令 |
组合按键 | 含义 |
---|---|
^D | EOF |
^M | Enter键 |
^S | 暂停屏幕输出 |
^Q | 恢复屏幕输出 |
^U | 在提示符在,删除整行命令 |
^Z | 暂停当前命令 |
单引号和双引号区别
单引号不会转义
$ name=33
$ echo "$name"
33
$ echo '$name'
$name
变量操作
基本操作
$ name="3 3"
$ name=${name}hh
$ echo $name
3 3hh
$ env #显示用户的环境变量
$ set #显示用户的局部变量和用户环境变量
export #显示用户由局部变量导出成环境变量 的变量
export name #将自定义变量导入环境变量
反单引号``
``之内的命令将先执行,其结果作为外部输入信息
$ ls -l `locate crontab`
#先用locate将所有文件名数据列出,再ls列出详细内容
read 读输入
$ read b
34
$ echo $b
34
$ read -p "in: " a
in: 33
$ echo $a
33
$ read -t 3 #等待用户输入三秒,没有则结束
declare 声明变量属性
$ declare -a sum=(1 3 56)
$ echo ${sum[1]} #将sum定义为叔祖
$ sum=100+123
$ echo $sum
100+123
$ declare -i sum=100+123 #将sum定义为interger
$ echo $sum
223
$ declare -x sum # 将sum变成环境变量
$ declare -r sum #将sum变成read only
$ sum=222 #error
ulimit 设置文件系统与程序的限制关系
字符标志作用#*%
# 是 去掉左边(键盘上#在 $ 的左边)
%是去掉右边(键盘上% 在$ 的右边)
单一符号% #是最小匹配;两个符号## %%是最大匹配
* 表示零个或多个任意字符
? 表示零个或一个任意字符
[…] 表示匹配中括号里面的字符
[!..] 表示不匹配中括号里面的字符
$ v="00/111/222/333/444.55"
$ echo ${v#*/} # 删掉第一个 / 及其左边的字符串
111/222/333/444.55
$ echo ${v##*/} # 删掉最后一个 / 及其左边的字符串
444.55
$ echo ${v%/*} # 删掉最后一个 / 及其右边的字符串
00/111/222/333
$ echo ${v%%/*} # 删掉第一个 / 及其右边的字符串
00
$ echo ${v:5:5}
1/222
$ echo ${v::3}
00/
$ echo ${v:4:-2}
11/222/333/444.
$ v=22.3322.44.5
$ echo ${v/22/_} #替换
_.3322.44.5
$ echo ${v//22/_}
_.33_.44.5
利用 ${ } 还可针对不同的变数状态赋值(沒设定、空值、非空值):
一般: “str:“代表"str"没设置或者为空, "str“代表没设置
$ file=/dir1/dir2/dir3/my.file.txt
$ ${file-my.file.txt} #假如 $file 没有设定,則使用 my.file.txt 作传回值。(空值及非空值時不作处理)
$ ${file:-my.file.txt} #假如 $file 沒有設定或為空值,則使用 my.file.txt 作傳回值。 (非空值時不作处理)
$ ${file+my.file.txt} #假如 $file 設為空值或非空值,均使用 my.file.txt 作傳回值。(沒設定時不作处理)
$ ${file:+my.file.txt} #若 $file 為非空值,則使用 my.file.txt 作傳回值。 (沒設定及空值時不作处理)
$ ${file=my.file.txt} #若 $file 沒設定,則使用 my.file.txt 作傳回值,同時將 $file 賦值為 my.file.txt 。 (空值及非空值時不作处理)
$ ${file:=my.file.txt} #若 $file 沒設定或為空值,則使用 my.file.txt 作傳回值,同時將 $file 賦值為 my.file.txt 。 (非空值時不作处理)
$ ${file?my.file.txt} #若 $file 沒設定,則將 my.file.txt 輸出至 STDERR。 (空值及非空值時不作处理)
$ ${file:?my.file.txt} #若 $file 没设定或为空值,则将 my.file.txt 输出至 STDERR。 (非空值時不作处理)
$ ${#var} #返回变量值的长度
函数
[ function ] funname [()]
{
action;
[return int;]
}
funWithParam(){
echo $1
}
funWithParam 33
结果输出:33
常用的位置参数如下:
$0 相当于 C 语言的 main 函数的 argv[0]
$1、$2、$3.... 这些称为位置参数,相当于 C 语言的 main 函数里面的 argv[1]、argv[2].....
$# 相当于 C 语言里面的 main 函数的 argc -1 ,注意这里的 # 后面不表示任何注释
$@ 表示参数列表 "$1","$2",.......... ,例如可以在 for 循环 的 in 后面
$* 表示参数列表 “$1”,"$2",........... 同上。不过这两个表示法有点区别:$@ 和 $*的存储方式不同,$@ 是按每个参数分开(分块)存储的;而, $* 是连续存储在一个块里面
$? 上一条命令的 Exit Status
$$ 当前进程号
shift 左移命令(比如 shift 3 ,表示原来的 $4 变成 $1,原来的 $5 变成 $2 ,原来的 $1,$2,$3 丢弃,$0不移动。不带参的 shift 命令相当于 shift 1)。
杂
history
$ history #历史执行过的所有命令
$ history 3 #最近3条
$ history -w #将当前shell的历史记录写入~/.bash_history
$ history -c #清除当前shell的history
$ !3 #执行~/.bash_history中第3条命令
$ !! #执行上一条
$ !his #执行从后往前找到第一条以his为前缀的命令,找不到则not found
命令执行判断 && ||
$ ls /a && touch /a/b #当前面命令执行正确的时候才会执行后面的
$ ls /a || mkdir /a #当前面命令执行错误的时候才会执行后面的
#常用操作
$ ls /a && touch /a/b || touch /a/c
$ test -e ./t.sh && echo "y" || echo "n" #查看文件是否存在
cut、grep
cut缺点:难以处理多空格相连的数据
echo $PATH | cut -d ':' -f 3
#-d后面接分分隔符 -f后面写获取第几段
$ a=123456
$ echo $a |cut -c 3- #-c cut范围
3456
$ echo $a |cut -c 3-4
34
$ ll |grep -v '33' #将不包含33字符串的行取出
grep -i 忽略大小写
grep -c 输出匹配到的次数
grep [a-zA-Z] 输出非空行
sort、uniq、wc
sort
-f 忽略大小写差异
-b 忽略每行前面空格
-M 已月份名字排序
-n 使用纯数字排序(默认按照文字类型排序)
-r 反向排序
-t 分隔符号(默认为tab)
-k n 按照第n个字段排序
#-n
$ sort
1
11
2
$ sort -n
1
2
11
#-t -k
$ cat /etc/passwd | sort -t ':' -k 3 #按照以:分隔的第三栏 排序
root:x:0:0:root:/root:/bin/bash
tony:x:1000:1000:tony,,,:/home/tony:/bin/csh
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
uniq #重复的数据只输出一次
uniq -i #忽略大小写
uniq -c #count,还输出出现的次数
wc #会输出 行数、字数、字符数
wc -l #只输出行数
wc -w #只输出字数
wc -m/-c #只输出字符
字符转换 文件处理 tr、col、join、paste、expand
tr -d 'xx' #删除其中所有xx字符串
tr -s 'xx' 'aa' #将所有xx替换成aa
$ cat /etc/passwd | tr -d ':'
$ last |tr =s '[a-z]' '[A-Z]'
$ cat filename | tr -d '\r' > newfile #可以将DOS文件尾部的^M(\r)去掉
col -x #将tab换成空格
expand -t 2 #将tab用两个空格替换(默认为8)
join -t ':' #join默认以空格为分隔符,此处以:为分隔符,并且默认比较第一个字段的字符,如果相同则连接成一行。
join -i #忽略大小写差异
join -1 3 #第一个文件用第三个字段来比较
join -2 2 #第二个文件用第二个字段来比较
join -t ':' -1 4 /etc/passwd -2 3 /etc/group
paste #连接两个文件的行,默认tab分隔
paste -d '?' #以?分隔
paste file1 file2 #如果file1或file2是'-',则表示来自stdin的数据
paste -d '!' - hh > hh1
文件拆分split
$ split -l 10 #根据每10行拆分
$ split -b 300k #划分成每300k一个文件
$ split -b 300k /etc/termcap termcap
$ ls
termcapaa
termcapab
termcapac
...
$ cat termcap* >> termcap
$ ll | split -l 10 - hh #将stdin拆分成每10行存入hhaa,hhab .....
参数替换xargs
xargs命令通俗来讲就是将标准输入转成各种格式化的参数,所以命令[command 1] | xargs [command 2]就是将command 1的标准输出结果,通过管道|变成xargs的标准输入,然后xargs再将此标准输入变成参数,传给[command 2]。这样一来,通过xargs命令,我们便可以在管道后面使用那些不接收标准输入的命令了
$ ls | xargs ls -al #输出各目录的子目录的详细信息。ls后将输出传到xargs,处理后成为一个个参数,传给后面的ls
-e eof-str
设置“文件结束符”,xargs 在处理标准输入时,如果遇到“文件结束符”,则其后的所有输入都将被忽略。如果 -E 和 -e 选项都没有被使用,则表示在标准输入中没有字符会被当成“文件结束符”处理。
$ echo "\% 33 \%"|xargs -E'33' echo #注意-E后面没有空格
%
$ echo "\% 33 \%"|xargs echo
% 33 %
-0 :当 xargs 使用此选项时,null字符会被作为“数据块”的分隔符,而引号(单引用和双引号)都不在具有特殊含义(所有的字符都代表他们自己);文件结束符(EOF)也将失效。通常情况下,当“数据块”中包含空格、引号、反斜杠时适合使用此选项
$ echo "\' \%"|xargs echo
' %
$ echo "\' \%"|xargs -0 echo
\' \%
-p:执行每个命令的参数都会i询问y/n
-n 3:将输入分成每3个一份作为参数
Shell脚本练习
循环日期创建文件
#!/bin/bash
read a
filename=${a:-filename} #避免不输入直接回车
for ((i = 1; i <= 10; i++))
do
date=`date --date="$i days ago" +%Y%m%d`
file="$filename""_""$date"
touch $file
done
exit 0
判断符[]
注意[ ]必须和内容隔开,等号两边也有空格
[ str1 == str2 ] 当两个串有相同内容、长度时为真
[ str1 != str2 ] 当串str1和str2不等时为真
[ -n str1 ] 当串的长度大于0时为真(串非空)
[ -z str1 ] 当串的长度为0时为真(空串)
[ str1 ] 当串str1为非空时为真
判断
if [xxx]; then
xx
else
aa
fi
if [ "$1" == "22" ]; then
echo "66"
elif [ "$1" == "33" ]; then
echo "77"
else
echo "hh"
fi
case $1 in
"hello")
echo "hh"
;;
"")
echo "null"
;;
*)
echo "..."
;;
esac
函数
function gg(){
echo "hhh $1 $2 $3";
return $4;
}
gg 3 4 5 11
echo $?
#输出:
hhh 3 4 5
11
循环
while [ xx ]
do
echo "!"
done
#和while相反,直到xx为真,退出
until [ xx ]
do
echo "!"
done
for (( i=0; i<100; i++))
do
echo $i
done
for i in 0 1 3
do
echo $i
done
#0 1 3
调试 sh
sh -n t.sh #不执行,检查语法。正确则无输出
sh -v t.sh #打印脚本内容,再执行
sh -x t.sh #一行一行交替打印脚本执行的代码和输出(如果是执行的代码会在输出前面有个+)
#t.sh
ech1=echi1
for i in 0 1 2
do
echo $i
done
$ sh -x t.sh
#输出
+ ech1=echi1
+ echo 0
0
+ echo 1
1
+ echo 2
2
+ exit 0
以空格为分隔符分割文件内容并输出
awk -F: #以:分割
$ awk '{for(i=1;i<=NF;i++){print $i}}' filename
LeetCode刷题
第十行 https://leetcode-cn.com/problems/tenth-line/
tail -n +10 file.txt|head -n 1
有效电话号码 https://leetcode-cn.com/problems/valid-phone-numbers/
egrep "(^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$)|(^[0-9]{3}-[0-9]{3}-[0-9]{4}$)" file.txt
转置文件 https://leetcode-cn.com/problems/transpose-file/
awk '
{
for (i=1; i<=NF; i++) {
if (NR==1) {
res[i]=$i
} else { #NR>=2
res[i]=res[i] " " $i
}
}
}
END {
for (i=1; i<=NF; i++) {
print res[i]
}
}
' file.txt
统计词频 https://leetcode-cn.com/problems/word-frequency/
awk '
{
for(i=1; i<=NF; i++){
res[$i]+=1
}
}
END{
for(k in res){
print k " " res[k]
}
}
' words.txt | sort -rn -k 2