split:分割文本文件

在数据处理时有时需要将一个大的数据集分为几个部分,交给不同机器或不同账号进行处理,之前我都是用sed或vi进行分割,没想到linux有一个专门这样的工具进行处理:split。

split的用法也非常简单,可以按行分割也可以按大小分割。

$ split --help
Usage: split [OPTION]... [FILE [PREFIX]]
Output pieces of FILE to PREFIXaa, PREFIXab, ...;
default size is 1000 lines, and default PREFIX is 'x'.

With no FILE, or when FILE is -, read standard input.

Mandatory arguments to long options are mandatory for short options too.
  -a, --suffix-length=N   generate suffixes of length N (default 2)
      --additional-suffix=SUFFIX  append an additional SUFFIX to file names
  -b, --bytes=SIZE        put SIZE bytes per output file
  -C, --line-bytes=SIZE   put at most SIZE bytes of records per output file
  -d                      use numeric suffixes starting at 0, not alphabetic
      --numeric-suffixes[=FROM]  same as -d, but allow setting the start value
  -e, --elide-empty-files  do not generate empty output files with '-n'
      --filter=COMMAND    write to shell COMMAND; file name is $FILE
  -l, --lines=NUMBER      put NUMBER lines/records per output file
  -n, --number=CHUNKS     generate CHUNKS output files; see explanation below
  -t, --separator=SEP     use SEP instead of newline as the record separator;
                            '\0' (zero) specifies the NUL character
  -u, --unbuffered        immediately copy input to output with '-n r/...'
      --verbose           print a diagnostic just before each
                            output file is opened
      --help     display this help and exit
      --version  output version information and exit

The SIZE argument is an integer and optional unit (example: 10K is 10*1024).
Units are K,M,G,T,P,E,Z,Y (powers of 1024) or KB,MB,... (powers of 1000).

CHUNKS may be:
  N       split into N files based on size of input
  K/N     output Kth of N to stdout
  l/N     split into N files without splitting lines/records
  l/K/N   output Kth of N to stdout without splitting lines/records
  r/N     like 'l' but use round robin distribution
  r/K/N   likewise but only output Kth of N to stdout

GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
Full documentation at: <http://www.gnu.org/software/coreutils/split>
or available locally via: info '(coreutils) split invocation'

简单而言,我将large.csv按照每个2000行进行分割,输入文件名按数字形式命名,则可以:

split -l 2000 -d large.csv

cron脚本中date命令问题

在shell中可以正常执行的命令,转移到cron中让其定时运行发现存在问题,即有错误提示,导致命令不执行。

d=`date -d “1 day ago” ‘+%Y-%m-%d’` && python3 -u regiondata.py –regionfile=region.id –date=$d

此命令问题在于d变量不能获取到,而在shell下执行则没有问题。网上搜索也有同样的问题:

https://unix.stackexchange.com/questions/29578/how-can-i-execute-date-inside-of-a-cron-tab-job

发现解决方案也比较简单,即在%符号前加上\转义,即可执行命令:

d=`date -d “1 day ago” ‘+\%Y-\%m-\%d’` && python3 -u regiondata.py –regionfile=region.id –date=$d

bash tips: kill a background bash job

执行了一个shell脚本,突然发现脚本里的代码有错误,想要终止其运行,这是要用ps & kill 来执行的话,就很困难(取决于脚本里运行的程序逻辑复杂程度)。

但有个简便的方法可以kill掉刚进入background执行的脚本:

kill %%

bash tips:巧用xargs来组合不同命令

在处理数据时,使用python编写了一个小程序,可将多个同类型但文件头次序有可能不同的csv文件进行合并,其使用方法为:

python3 csvmerge.py --output=output.csv input1.csv input2.csv ...

输入文件少的时候直接输入文件名称执行即可,但若文件很多,一个一个键入则是个苦差事,还容易出错,因此想是否可以通过管道来组合命令。

直接想到的是:

ls input-*.csv | python3 csvmerge.py --output=output.csv

但发现这样后边的程序其实是没有接受前面管道的输出结果,导致其并没有对应的输入参数。搜索后发现xargs是解决这类问题的一个最佳方案。

ls input-*.csv | xargs python3 csvmerge.py --output=output.csv

xargs 可以读入 stdin 的资料,并且以空白字元或断行字元作为分辨,将 stdin 的资料分隔成为 arguments 。 因为是以空白字元作为分隔,所以,如果有一些档名或者是其他意义的名词内含有空白字元的时候, xargs 可能就会误判了,如果需要处理特殊字符,需要使用-0参数进行处理。

用sort对csv内容进行排序

bash下可以用sort对文本进行排序。若需要对csv(有header)进行排序,则可以:

sort -t, -nk1 test.csv

-t: 指定分割符
-n: 指定数字排序
-k1: 指定第1列

shell下定时监测程序的死锁

这几天在处理微博爬虫。由于各种原因,爬虫经常为会出现网络错误断开连接,有时莫名其妙的程序锁死(程序在运行,但其实没有任何输出结果显示在运行)。因此就产生了一个定时检查所运行脚本的脚本,或者就在脚本中定时检查。但又不能将检测程序放入cron中,因为程序kill掉后还要继续向前运行。

处理方法如下:
1. 爬虫脚本将结果输出到某log文件中
2. 若log文件在一定时间内未发生变化,而且爬虫脚本还在运行,那可以认定此时程序锁死。
3. 处理锁死程序,直接kill掉,然后继续时间循环。

脚本运用了pgrep(获取某运行程序的pid)、pkill(杀死某程序)。

#!/bin/bash

#only works in Linux shell
startdate='2016-07-03'
for i in {0..7};do
echo "Running time:"`date`
tododate=`date -d "${startdate} $i days" +%Y-%m-%d`
logfile="weibo-${tododate}.log"
python -u weibo-num.py $tododate >$logfile 2>&1 &
while sleep 120;do
cmd=`pgrep -f "weibo-num.py ${tododate}"`
delta=$((`date +%s`-`date -r $logfile +%s`))
if [ ! -z $cmd ] && [ $delta -ge 900 ];then
pkill -f "weibo-num.py ${tododate}"
break
elif [ -z $cmd ]; then
break
fi
done
done

数据巧处理:重复数据的合并处理

要处理一批数据,形式为:
lat,lon,num
每个文件有一个timestamp,文件数量巨大,开始我感觉lat、lat应该唯一,但后来检查数据后发现,这批数据存在大量重复的点(可能之前高精度数据是唯一的,但精度降低后导致有重复数据出现),因此需要专门处理。
开始是想在bash里直接处理,但点位相同的数据,其后的num感觉应该sum后替换,因此还没有想到较好的方法。
后来一想数据不是要入库么,入库后先放松唯一性检查,然后通过group操作,可以简单获取到对应的结果。
SQL:
create table sum_point as
select ts,lat,lon,sum(num) from point group by ts,lat,lon

linux shell下处理web log

现在要将服务器的web log经过分析后将对应的分析结果保存到数据库里,因此需要在服务器后台自动运行,采用shell脚本进行处理,处理过程中遇到两个问题,将解决方法记录如下。

1. 合并昨日对应网站的web log

三个网站(中文版、英文版、专题版)的log记录要合并,而且现在为了保存更多的log,是将每个log按照日期的方式进行命令的,即:

website-access-20141225.log

但也有可能是.log.1的形式(之前是两个形式并存)。
因此需要在shell中获取昨日日期然后进行合并。在shell获取日期可以使用date命令,搜索后发现获取昨日日期的命令如下;

date –date=”yesterday” +%Y%m%d

或者:

date –date=”1 day ago” +%Y%m%d

然后就可以使用mergelog命令将所有的log合并。

2. 运行psql命令
需要使用管理员账号对数据库变更(用到copy命令),因此需要使用su切换到postgres账号并运行对应的sql。发现这个解决方法也比较简单:

su -c “psql db_name -f log.sql” postgres

其中-c就是运行指定的命令。

linux shell的代码执行问题

之前通过incron触发执行的一个wsn文件生成代码,据用户反馈,有很多情况下文件并未生成,因此思考是不是之前的触发脚本是不是存在不能的问题,因此就考虑重新修改代码。

之前的代码如下:

#!/bin/sh
cp /www/wsndata.sh /root/wsndata.sh
chmod +x /root/wsndata.sh
/root/wsndata.sh

怀疑就是执行/root/wsndata.sh未能并行执行多个实例,因此考虑引入临时变量。

#!/bin/bash
tmp=$RANDOM
cp /var/www/ftp2.westgis.ac.cn/wsndata.sh /root/wsndata-${tmp}.sh
chmod +x /root/wsndata-${tmp}.sh
/root/wsndata-${tmp}.sh &

后来发现第一行的sh和bash还有影响。