shell定时采集日志到hdfs中

本文介绍了一种使用Shell脚本自动将本地日志文件上传到Hadoop集群的方法,并解决了文件重复上传及重命名的问题。通过定时任务实现日志文件的自动化管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#!/bin/bash

#set java env
export JAVA_HOME=/gcx/jdk1.7.0_45
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

#set hadoop env
export HADOOP_HOME=/gcx/hadoop-2.6.4
export PATH=${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin:$PATH


#版本1的问题:
#虽然上传到Hadoop集群上了,但是原始文件还在。如何处理?
#日志文件的名称都是xxxx.log1,再次上传文件时,因为hdfs上已经存在了,会报错。如何处理?

#如何解决版本1的问题
#       1、先将需要上传的文件移动到待上传目录
#	2、在讲文件移动到待上传目录时,将文件按照一定的格式重名名
#		/export/software/hadoop.log1   /export/data/click_log/xxxxx_click_log_{date}


#日志文件存放的目录
log_src_dir=/root/hadoop/logs/log/

#待上传文件存放的目录
log_toupload_dir=/root/hadoop/logs/toupload/


#日志文件上传到hdfs的根路径
hdfs_root_dir=/root/hadoopData/logs/

#打印环境变量信息
echo "envs: hadoop_home: $HADOOP_HOME"


#读取日志文件的目录,判断是否有需要上传的文件
echo "log_src_dir:"$log_src_dir
ls $log_src_dir | while read fileName
do
	if [[ "$fileName" == access.log.* ]]; then
	# if [ "access.log" = "$fileName" ];then
		date=`date +%Y_%m_%d_%H_%M_%S`
		#将文件移动到待上传目录并重命名
		#打印信息
		echo "moving $log_src_dir$fileName to $log_toupload_dir"xxxxx_click_log_$fileName"$date"
		mv $log_src_dir$fileName $log_toupload_dir"xxxxx_click_log_$fileName"$date
		#将待上传的文件path写入一个列表文件willDoing
		echo $log_toupload_dir"xxxxx_click_log_$fileName"$date >> $log_toupload_dir"willDoing."$date
	fi
	
done
#找到列表文件willDoing
ls $log_toupload_dir | grep will |grep -v "_COPY_" | grep -v "_DONE_" | while read line
do
	#打印信息
	echo "toupload is in file:"$line
	#将待上传文件列表willDoing改名为willDoing_COPY_
	mv $log_toupload_dir$line $log_toupload_dir$line"_COPY_"
	#读列表文件willDoing_COPY_的内容(一个一个的待上传文件名)  ,此处的line 就是列表中的一个待上传文件的path
	cat $log_toupload_dir$line"_COPY_" |while read line
	do
		#打印信息
		echo "puting...$line to hdfs path.....$hdfs_root_dir"
		hadoop fs -put $line $hdfs_root_dir
	done	
	mv $log_toupload_dir$line"_COPY_"  $log_toupload_dir$line"_DONE_"
done

在配置crontab定时任务

 */1 * * * * sh /gcx/uploadFile.sh

<think>我们参考了用户提供的引用资料,特别是引用[3]中提到的脚本思路和引用[4]中的基本命令。用户需求:将本地目录中的新文件递归采集上传HDFS。思路:1.需要识别本地目录中的新文件(即未被上传过的文件)。2.递归地处理目录中的所有新文件(包括子目录)。3.上传HDFS时,可能需要保持相同的目录结构。然而,HDFSshell命令本身没有直接提供“仅上传新文件”的功能。因此,我们需要设计一个方案来追踪已经上传的文件,或者通过比较时间戳来判断哪些是新文件。一种常见做法是使用一个标记文件或数据库记录已上传的文件,但这里我们考虑另一种简单方法:利用文件的时间戳。具体步骤:a)在本地目录中,我们可以使用find命令递归查找文件,并通过比较文件的修改时间(mtime)与某个时间点(例如上次同步的时间)来判断是否为新的。b)我们可以将上次同步的时间记录在一个文件中(例如.last_sync),每次同步后更新这个文件的时间戳。c)然后,我们只上传那些修改时间晚于.last_sync文件修改时间的文件。但注意:用户要求递归处理,所以我们需要递归地查找新文件并上传,同时在HDFS上创建相应的目录结构。方案:1.初始化:首先确保有一个记录上次同步时间的文件(.last_sync)。如果不存在,则创建,并设置时间戳为过去某个时间(比如1970年),这样第一次运行时会同步所有文件。2.查找新文件:使用find命令查找本地目录中所有修改时间晚于.last_sync的文件。3.上传每个新文件:遍历找到的每个新文件,在HDFS上创建对应的目录,然后上传文件。4.更新.last_sync文件:同步完成后,更新.last_sync文件的时间戳为当前时间。但是,由于HDFS本身也有时间戳,而且上传HDFS上的文件修改时间会改变,因此我们只依赖本地的时间戳来判定新文件。注意:这种方法假设系统时钟是同步的,并且不会在同步过程中有新的文件修改。如果有文件在同步过程中被修改,则可能在下一次同步时被遗漏或重复上传。为了避免这种情况,我们可以在同步开始前获取一个时间点,然后同步所有在同步开始前修改的文件,并在同步后更新时间点。改进步骤:a)在脚本开始运行时,先记录一个开始时间(比如current_time),然后上传所有修改时间早于current_time并且晚于上次同步时间的文件。b)上传完成后,将.last_sync的时间戳设置为current_time。我们使用touch命令创建或更新.last_sync文件的时间戳。由于HDFS的put命令在上传文件时会自动创建父目录,所以我们可以直接使用put命令,而不需要先创建目录(但前提是目标路径的父目录必须存在,或者使用`-p`参数创建父目录?)。查看HDFS的put命令文档,我们发现`-p`选项可以保留访问时间、修改时间、所有权限和ACL,同时也会在HDFS上创建父目录。因此,我们可以使用`-p`选项来确保上传时创建相应的目录结构。但是,注意:用户要求递归上传新文件,我们的方案中已经通过find命令递归查找了文件,因此可以直接使用hdfsdfs-put命令,并使用`-p`选项。具体命令:假设:本地目录:/local/pathHDFS目录:/hdfs/path上次同步记录文件:/local/path/.last_sync脚本步骤:1.获取当前时间(作为同步的时间点):current_time=$(date+%s)2.如果不存在.last_sync文件,则创建并设置时间戳为0(1970-01-01)。3.使用find命令查找所有修改时间(mtime)在.last_sync文件修改时间之后且在current_time之前的文件(这样可以避免上传正在写入的文件)。注意:find命令的-newer参数可以用于比较文件修改时间。我们可以创建一个临时文件,其修改时间为上次同步时间,然后查找比这个临时文件新的文件,同时修改时间在当前时间之前(或者直接忽略这个,因为文件修改时间不可能在当前时间之后,除非时钟有问题)。4.对于每个找到的文件,使用HDFS的put命令(带`-p`选项)上传HDFS的对应路径(注意保持相对路径)。5.更新.last_sync文件的时间戳为当前时间(current_time对应的时间)。由于使用find遍历文件并上传,如果文件很多,可能会多次调用put命令,效率相对较低,但可以确保上传新文件。另一种方案:使用rsync命令,但是HDFS并不支持rsync协议。因此,我们只能使用shell脚本。我们参考引用[3]中的脚本,它没有考虑新文件,只是定时移动文件并上传。我们可以结合这个思路,但增加新文件的判断。下面是一个示例脚本的大致框架:注意:我们假设本地目录为/local/data,HDFS目标目录为/user/hadoop/data脚本内容:```#!/bin/bash#设置本地目录和HDFS目录LOCAL_DIR="/local/data"HDFS_DIR="hdfs://namenode:port/user/hadoop/data"#或者直接使用HDFS路径,如:/user/hadoop/data#上次同步记录文件LAST_SYNC_FILE="$LOCAL_DIR/.last_sync"#如果不存在上次同步文件,则创建一个时间戳为0的文件if[!-f"$LAST_SYNC_FILE"];thentouch-t197001010000"$LAST_SYNC_FILE"fi#获取当前时间戳,作为本次同步的截止时间CURRENT_TIME=$(date+%Y%m%d%H%M.%S)#格式为YYYYMMDDHHMM.SS,用于touch命令#创建一个临时文件,标记为本次同步的开始时间点touch-t"$CURRENT_TIME"/tmp/sync_start.$$#使用唯一性临时文件#递归查找比上次同步文件新但比当前时间点早的文件(避免上传正在写入的文件)find"$LOCAL_DIR"-typef-newer"$LAST_SYNC_FILE"!-newer/tmp/sync_start.$$|whilereadfiledo#计算相对路径,以便在HDFS中保持同样的目录结构relative_path=${file#$LOCAL_DIR}#如果文件位于LOCAL_DIR下的子目录,则relative_path会以/开头,我们去掉开头的/或者保留(取决于HDFS路径的拼接)#在HDFS上创建目标路径(包括目录),然后上传文件,但使用put的-p选项可以自动创建父目录hdfsdfs-put-p"$file""$HDFS_DIR$relative_path"done#删除临时文件rm/tmp/sync_start.$$#更新上次同步时间戳:将.last_sync文件的时间戳更新为本次同步的开始时间点(即/tmp/sync_start.$$的时间戳)touch"$LAST_SYNC_FILE"#更新为当前时间(但注意,这个当前时间已经过去了,因为我们是在同步之后才更新)#但是,因为我们用/tmp/sync_start.$$的时间作为同步时间点,所以我们可以直接将这个时间给.last_sync_filetouch-t"$CURRENT_TIME""$LAST_SYNC_FILE"```注意:这个脚本只是一个基础版本,可能需要处理一些边界情况,比如:-文件名包含空格或特殊字符(我们使用whileread循环可以处理空格,但不能处理换行符,不过通常文件名不会有换行符)。-如果上传失败,我们应该中断并回滚?但这里没有处理失败的情况。-另外,-put命令的-p选项在Hadoop3.x中支持,但老版本可能不支持。如果不支持,则我们需要先创建目录,然后上传文件。针对老版本的方案:如果-p选项不可用,我们可以先使用dirname获取文件的相对目录路径,然后在HDFS上创建该目录(使用mkdir-p,但HDFS的mkdir命令有-p选项吗?)。引用[4]中提到创建多层目录,使用:hdfsdfs-mkdir-p/data/dataA所以我们可以使用mkdir-p来创建父目录。修改上传部分的命令:```#在while循环内hdfs_dir_path="$HDFS_DIR$relative_path"hdfs_parent_dir=$(dirname"$hdfs_dir_path")#创建父目录(如果不存在)hdfsdfs-mkdir-p"$hdfs_parent_dir"#上传文件hdfsdfs-put"$file""$hdfs_dir_path"```考虑到时间问题,我们避免上传正在写入的文件,所以使用了两个时间点:上一次同步时间和本次同步开始时间。这样可以确保上传的文件是本次同步开始前已经修改完毕的文件。另外,如果用户需要实时的监控新增文件,可以使用inotify等工具,但这里的需求是定时递归采集,所以我们使用定时任务执行上述脚本即可。总结:我们编写一个shell脚本,使用find命令查找上次同步后到本次同步开始前修改的所有文件,然后递归上传HDFS,保持相同的目录结构。相关问题:1.如何监控本地目录变化并实时上传HDFS?2.上传HDFS时,如何保证文件的一致性(比如上传过程中文件被修改)?3.HDFS的put命令和get命令有什么其他常用选项?4.HDFS中如何查看上传后的文件?5.如果上传失败,如何重试?注意:用户要求参考站内引用,并且我们的回答也参考了这些引用。因此,我们按照上述思路提供了一个基础脚本,并解释了其原理。</think> ### 如何递归采集上传本地新文件到HDFS 根据引用资料[1][2][3][4],以下是实现本地目录新文件递归采集上传的完整方案: #### 核心方法:Shell脚本实现 ```bash #!/bin/bash # 配置参数 LOCAL_DIR="/data/logs" # 本地监控目录 HDFS_DIR="/user/hadoop/logs" # HDFS目标目录 UPLOAD_LOG="./upload.log" # 上传记录文件 # 创建HDFS目录(引用[1][4]) hdfs dfs -mkdir -p $HDFS_DIR # 递归查找新文件并上传(引用[3]) find $LOCAL_DIR -type f -newer $UPLOAD_LOG -print0 | while IFS= read -r -d $'\0' file do # 构建HDFS目标路径(保持相同目录结构) relative_path=${file#$LOCAL_DIR} hdfs_path="$HDFS_DIR$relative_path" # 创建HDFS父目录(引用[2]) hdfs dfs -mkdir -p $(dirname "$hdfs_path") # 上传文件(引用[1][3]) hdfs dfs -put "$file" "$hdfs_path" && echo "$(date) 上传成功: $file -> $hdfs_path" # 记录上传状态 echo "$file $(date +%s)" >> $UPLOAD_LOG done ``` #### 关键功能说明 1. **递归监控** - 使用`find -type f -newer`递归查找修改时间晚于上次上传记录的文件 - `-print0`配合`read -d $'\0'`处理含空格的文件路径 2. **保持目录结构** ```bash relative_path=${file#$LOCAL_DIR} # 提取相对路径 hdfs dfs -mkdir -p $(dirname "$hdfs_path") # 创建对应HDFS目录 ``` 3. **断点续传机制** - 通过`$UPLOAD_LOG`文件记录已上传文件和时间戳 - 下次执行时仅上传新修改/新增文件 #### 定时执行配置 1. 创建脚本文件:`nano upload_newfiles.sh` 2. 添加可执行权限:`chmod +x upload_newfiles.sh` 3. 配置cron定时任务(每5分钟执行): ```bash crontab -e */5 * * * * /path/to/upload_newfiles.sh ``` #### 验证操作(引用[2][4]) ```bash # 查看HDFS上传结果 hdfs dfs -ls -R $HDFS_DIR # 检查上传日志 cat upload.log ``` > **注意事项** > - 首次执行前创建空记录文件:`touch upload.log` > - Hadoop需配置正确的环境变量[^4] > - 如需处理文件名冲突,可在`-put`后添加`-f`覆盖选项[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值