LeetCode 195 第十行
01题目描述
给定一个文本文件 file.txt,请只打印这个文件中的第十行。
02文件内容
Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
03输出
Line 10
说明:
1. 如果文件少于十行,你应当输出什么?
2. 至少有三种不同的解法,请尝试尽可能多的方法来解题。
04解析
参考答案:
三种不同的解法
第一种:grep -n "" file.txt | grep -w '10' | cut -d: -f2
第二种:sed -n '10p' file.txt
第三种:awk '{if(NR==10){print $0}}' file.txt
说明当中提到,我们应该考虑文件少于十行的情况,那么我们就要知道文件本身包含的行数,以下给大家提供10种获取文件行数的方式:
[root@localhost ~]# awk '{print NR}' file.txt | tail -n1
10
[root@localhost ~]# awk 'END{print NR}' file.txt
10
[root@localhost ~]# grep -nc "" file.txt
10
[root@localhost ~]# grep -c "" file.txt
10
[root@localhost ~]# grep -vc "^$" file.txt
10
[root@localhost ~]# grep -n "" file.txt|awk -F: '{print '}|tail -n1 | cut -d: -f1
10
[root@localhost ~]# sed -n "$=" file.txt
10
[root@localhost ~]# wc -l file.txt
10 file.txt
[root@localhost ~]# cat file.txt | wc -l
10
[root@localhost ~]# wc -l file.txt | cut -d' ' -f1
10
知道了行号之后,我们就可以判断文件是否足够十行,足够十行则输出第十行,不足十行则打印文件不足十行,只有多少行即可。
row_num=$(cat file.txt | wc -l)
echo $row_num
if [ $row_num -lt 10 ];then
echo "The number of row is less than 10"
else
awk '{if(NR==10){print $0}}' file.txt
fi
其中打印输出第10行的代码可以替换为:
grep -n "" file.txt | grep -w '10' | cut -d: -f2
sed -n '10p' file.txt
awk '{if(NR==10){print $0}}' file.txt
05拓展
tail命令将每个文件的最后10行打印到标准输出。对于多个文件,在每个文件前面加上一个给出文件名的头。如果没有文件,或者文件为-,则读取标准输入。
如何使用tail命令
使用tail命令查看yum.log日志文件,显示最后10行内容,tail默认显示问价你的最后10行内容:
如何显示指定的行数
使用-n命令显示指定的行数,也可以省略字母n,只使用-和数字(数字和-之间没有空格)。
例如:查看自己创建的用户,查看/etc/passwd文件最后两行内容:
如何实时监控文件的更改
如果需要监视文件内容的更改,使用-f选项。这个选项对于监视日志文件非常有用。例如,要显示/var/log/messages文件的最后10行,并监视文件的更新:
想要退出,请按Ctrl+C退出。
查看多个文件
如果提供了多个文件作为tail命令的输入,它将显示每个文件的最后十行。下面例子,使用tail命令显示/etc/passwd和/etc/shadow文件的最后两行内容:
tail命令和其他命令一起使用
例如,要实时监视/var/log/messages文件并显示包含session为129的行,可以使用:
LeetCode 193 有效电话号码
01题目描述
给定一个包含电话号码列表(一行一个电话号码)的文本文件 file.txt,写一个 bash 脚本输出所有有效的电话号码。
你可以假设一个有效的电话号码必须满足以下两种格式: (xxx) xxx-xxxx 或 xxx-xxx-xxxx。(x 表示一个数字)
你也可以假设每行前后没有多余的空格字符。
02文件内容
987-123-4567
123 456 7890
(123) 456-7890
03输出
你的脚本应当输出下列有效的电话号码:
987-123-4567
(123) 456-7890
04解析
这道题目主要考察正则表达式和行匹配工具。
解决此问题,只要写出两种电话号码格式 (xxx) xxx-xxxx 或 xxx-xxx-xxxx 所对应的正则表达式即可:
(xxx) xxx-xxxx 所对应的正则表达式最笨的写法:
'\([0-9][0-9][0-9]\) [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]'
'[(][0-9][0-9][0-9][)] [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]'
也就是把每一个x换成数字[0-9],再将给小括号加上反斜杠进行转义或者放进一对中括号内即可。
xxx-xxx-xxxx 则可以表示为:
'[0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]'
对上面两种正则表达式做进一步的简化:
'\([0-9]{3}\) [0-9]{3}-[0-9]{4}'
'[(][0-9]{3}[)] [0-9]{3}-[0-9]{4}'
'[0-9]{3}-[0-9]{3}-[0-9]{4}'
此处再给出另外一种电话号码的正则表达式表示方式:
#\d 是基于 Perl 的正则表达式
#(xxx) xxx-xxxx
'[(]\d{3}[)] \d{3}-\d{4}'
'\(\d{3}\) \d{3}-\d{4}'
#xxx-xxx-xxxx
'\d{3}-\d{3}-\d{4}'
有了上面我们总结出来的正则表达式,进行行匹配就简单多了,只需要一个或|就可以将两个正则表达式连在一起:
'\(\d{3}\) \d{3}-\d{4}|\d{3}-\d{3}-\d{4}'
'[(]\d{3}[)] \d{3}-\d{4}|\d{3}-\d{3}-\d{4}'
'\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4}'
使用或运算连接在一起,你如果觉得还不够简化可以再提取出两个正则中相同的部分,则表示为:
'(\(\d{3}\) |\d{3}-)\d{3}-\d{4}'
'([(]\d{3}[)] |\d{3}-)\d{3}-\d{4}'
'(\([0-9]{3}\) |[0-9]{3}-)[0-9]{3}-[0-9]{4}'
最后匹配出电话号码格式正确的就轻而易举:6种方法
grep -P '(\(\d{3}\) |\d{3}-)\d{3}-\d{4}' file.txt
grep -P '([(]\d{3}[)] |\d{3}-)\d{3}-\d{4}' file.txt
grep -E '(\([0-9]{3}\) |[0-9]{3}-)[0-9]{3}-[0-9]{4}' file.txt
grep -P '^([0-9]{3}-|\([0-9]{3}\) )[0-9]{3}-[0-9]{4}$' file.txt
awk '/^([0-9]{3}-|\([0-9]{3}\) )[0-9]{3}-[0-9]{4}$/' file.txt
gawk '/^([0-9]{3}-|\([0-9]{3}\) )[0-9]{3}-[0-9]{4}$/' file.txt
05拓展
温馨提示:这道题主要考察正则表达式和行匹配,可以参考下面的文章学习
LeetCode 192 统计词频
01题目描述
写一个 bash 脚本以统计一个文本文件 words.txt 中每个单词出现的频率。
为了简单起见,你可以假设:
words.txt只包括小写字母和 ' ' 。
每个单词只由小写字母组成。
单词间由一个或多个空格字符分隔。
02文件内容
the day is sunny the the
the sunny is is
03输出
你的脚本应当输出(以词频降序排列):
the 4
is 3
sunny 2
day 1
说明:
不要担心词频相同的单词的排序问题,每个单词出现的频率都是唯一的。
你可以使用一行 Unix pipes 实现吗?
04解析
对于words.txt文件进行词频统计,首先要做的事情就是把words.txt文件当中的每一个单词分割出来,分割出每一个单词可以使用以下两种方式:
使用awk命令:
[root@localhost ~]# awk '{for(i=1;i<=NF;i++){print $i}}' words.txt
the
day
is
sunny
the
the
the
sunny
is
is
其中NF表示当前记录的字段数(即列数)
$i 文件中每行以间隔符号分割的不同字段
使用xargs命令:
[root@localhost ~]# cat words.txt | xargs -n1
the
day
is
sunny
the
the
the
sunny
is
is
[root@localhost ~]# cat words.txt | xargs -n2
the day
is sunny
the the
the sunny
is is
xargs命令是用于给其他命令传递参数的一个过滤器,也是组合多个命令的一个工具。
-n选项,指定输出时每行输出的列数
当我们将words.txt文件中的所有单词都分割出来之后,就可以统计这些单词当中每一个单词出现的次数了。
我们仅考虑使用awk命令来完成这个任务的话很简单,在进行分割的过程中直接用一个关联数组直接保存每一个单词出现的次数,此处我们可以暂时将关联数组理解为一个字典,关键字为单词,值为单词出现的次数(这样理解只是一种通俗的说法)
[root@localhost ~]# awk '{for(i=1;i<=NF;i++){asso_array[$i]++;}};END{for(w in asso_array){print w,asso_array[w];}}' words.txt
day 1
sunny 2
the 4
is 3
当然我们也可以在xargs的基础之上使用一些shell小工具来得到每个单词出现的次数。sort 工具及 uniq 工具
[root@localhost ~]# cat words.txt | xargs -n1 | sort
day
is
is
is
sunny
sunny
the
the
the
the
[root@localhost ~]# cat words.txt | xargs -n1 | sort | uniq -c
1 day
3 is
2 sunny
4 the
sort工具用于排序,它将文件的每一行作为一个单位,从首字母向后按照ASCII码值进行比较,默认将他们升序输出。
-r : 降序排列
-n : 以数字排序,默认是按照字符排序的。
uniq用去取出连续的重复行
-c :统计重复行的次数
最后我们仅需要对上面的结果进行排序啦,很简单的使用sort就可以啦!
[root@localhost ~]# cat words.txt | xargs -n1 | sort | uniq -c | sort -rn | awk '{print $2,$1}'
the 4
is 3
sunny 2
day 1
[root@localhost ~]# awk '{for(i=1;i<=NF;i++){asso_array[$i]++;}};END{for(w in asso_array){print w,asso_array[w];}}' words.txt | sort -rn
the 4
sunny 2
is 3
day 1
05拓展
关联数组的更多内容可以阅读:SHELL编程之变量与四则运算
LeetCode 194 转置文件
01题目描述
给定一个文件 file.txt,转置它的内容。你可以假设每行列数相同,并且每个字段由 ' ' 分隔.
02文件内容
name age
alice 21
ryan 30
03输出
name alice ryan
age 21 30
04解析
对于这道题目我们可以简单的理解为将第一列变成第一行,第二列变为第二行,...,第n列变为第n行。这样一来,问题就简单了,只需要把一列元素串起来,保存起来并输出即可,如何获取一列元素成了我们问题的关键了,awk命令就可以轻松解决这个问题了:
[root@localhost ~]# awk '{for(i=1;i<=NF;i++){print "row[",i,"]="$i}}' file.txt
row[ 1 ]=name
row[ 2 ]=age
row[ 1 ]=alice
row[ 2 ]=21
row[ 1 ]=ryan
row[ 2 ]=30
可以从awk命令对每一行处理后的结果观察到,现在要做的就是把所有row[1]的给串起来,也就是第一列的给串起来,构成第一行,所有的row[2]串起来构成第二行。这个实现起来就简单了,我们只需要加一个判断语句即可:
[root@localhost ~]# awk '{
for (i=1;i<=NF;i++){
if (NR==1){
res[i]=$i
}
else{
res[i]=res[i]" "$i
}
}
}END{
for(j=1;j<=NF;j++){
print res[j]
}
}' file.txt
name alice ryan
age 21 30
其中NR表示行号,判断行号是否等于1的目的在于,第一行的内容转置之后为每一行的行首,保证第一行内容在行首就得通过判断得到。
另外给大家提供一种投机取巧的做法,那就是使用cut命令进行列分割,代码如下:
#!/bin/env bash
column=$(awk '{print NF}' file.txt | uniq)
for((i=1;i<=column;i++))
do
cut -d' ' -f$i file.txt|xargs
done
其中变量column用来保存列数,cut -d' ' -f$i file.txt 表示取出文件当中的一列元素,配合xargs将一列内容转化为一行并输出。是不是超简单,看着比awk简单。
05拓展
关于awk命令不熟悉的读者朋友,可以参考此文学习: