awk技巧-!a[$0]++

本文深入解析了awk命令中!a[$0]++的独特用法,通过实例讲解如何利用此特性去除重复行,并提供了几种不同场景下的实现方案。

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

转载自:http://www.51testing.com/html/87/363787-811057.html

解释下
awk '!a[$0]++' file

一看之下,首先是想到又用到awk的hash,又是缺省的pattern,一下子来了兴趣,做了以下的分析

这个要从awk的执行模式开始说,最后结合++运算符,和hash特色
有三个基本知识点是要了解的
1:a++的作用是先附值,再累加a,与++a正好相反。

2:hash的初始是undef,通过直接附值或声明进行定义,如a[1]=1,或直接声明a[1]。

3:awk的基本模式是,pattern   { action statements },action部分是可以省略的,缺省情况下是输出,即{print $0},至于pattern可以理解成是表达式,通过pattern表达式的值的真假,来确定是否要进行action。比如1,最简单的awk用来实现cat的功能就是 awk '1',这边1就是pattern,当然,1也可以是2,3,4,5等其他数字,但如果用字母的话,就不行,因为字母会解释成变量,变量初始值未定义,初始值为假,或者可以加个!反义

结合上边三点来分析awk '!a[$0]++' file
"!a[$0]++"

0:整个模式,没有用到action,所以采用的是默认的{print $0},即在patten为真条件来,输出行
patten分析:
1:使用了一个hash数组,a,数组的键值采用$0,即每行值
2:当a[$0]未声明时,a[$0]为假,在未声明的情况下,进行一次a[$0]++后,a[$0]即为真
3:!取反
结论:当相同的行第一次读入时,pattern为真,行输出,再次读入后,patten为假,行乎略

基本理论知道了,要用得出来还得多锻炼应用

上周帮别人写个awk,也是这种情况
我写的
awk '{if($2 in a);else{a[$2]=$0}}END{for(b in a)print a[b]}' urrfile

后来别人给出更简单的答案
awk ' !($2 in i){ i[$2]; print } ' urrfile

现在看来,还可以更简单些
awk '!a[$2]++' urrfile


【注意】:这是前加/后加的区别,前几天还说过。


   后加: 先使用变量的值,再自加。

   !a[$0]++ 对这个表达式的求值,它的值与 !a[$0] 相同(先使用变量a[$0]的值),但对表达式求值后 a[$0]会自加。

#!/bin/bash #SBATCH -p Cnode2 #SBATCH -o /public/home/2019187/liu_test/chr1_mummer/delta_filter_job.log #SBATCH -e /public/home/2019187/liu_test/chr1_mummer/delta_filter_job.err #SBATCH -N 1 #SBATCH -n 16 #SBATCH --mem=256G #SBATCH --time=72:00:00 # ==== 初始化设置 ==== set -euo pipefail OUT_DIR="/public/home/2019187/liu_test/chr1_mummer" DELTA_PREFIX="Mo17_vs_B73_chr1" LOG_FILE="delta_filter_monitor.log" mkdir -p "$OUT_DIR" cd "$OUT_DIR" || exit 1 # ==== 加载Conda环境 ==== source /public/home/2019187/miniconda3/etc/profile.d/conda.sh conda activate pansial # ==== 函数:监控delta-filter进度 ==== monitor_progress() { local pid=$1 echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] 开始监控进程 $pid ..." | tee -a "$LOG_FILE" while kill -0 "$pid" 2>/dev/null; do # 检查输出文件 if [ -f "${DELTA_PREFIX}.i90.l2000.m.filter.delta" ]; then delta_size=$(ls -lh "${DELTA_PREFIX}.i90.l2000.m.filter.delta" | awk '{print $5}') else delta_size="未生成" fi # 获取资源占用 cpu_mem=$(ps -p "$pid" -o %cpu,%mem --no-headers 2>/dev/null || echo "进程已结束") # 记录状态 echo "[$(date '+%H:%M:%S')] Delta状态: $delta_size | 资源占用: $cpu_mem" | tee -a "$LOG_FILE" sleep 300 done } # ==== 主流程 ==== { echo "==== 作业开始于: $(date) ====" | tee -a "$LOG_FILE" echo "当前工作目录: $(pwd)" | tee -a "$LOG_FILE" # 检查输入文件 if [ ! -f "${DELTA_PREFIX}.delta" ]; then echo "错误: 输入文件 ${DELTA_PREFIX}.delta 不存在!" | tee -a "$LOG_FILE" exit 1 fi FILE_SIZE=$(stat -c%s "${DELTA_PREFIX}.delta") echo "输入文件大小: $((FILE_SIZE / 1024 / 1024)) MB" | tee -a "$LOG_FILE" # 运行delta-filter并使用pv显示进度 echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] 启动delta-filter处理..." | tee -a "$LOG_FILE" delta-filter -i 90 -l 2000 -m "${DELTA_PREFIX}.delta" | pv -s "$FILE_SIZE" -p -t -e -r > "${DELTA_PREFIX}.i90.l2000.m.filter.delta" 2>> delta_filter_stderr.txt & FILTER_PID=$! # 启动监控 monitor_progress "$FILTER_PID" # 等待完成 wait "$FILTER_PID" echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] delta-filter进程已结束" | tee -a "$LOG_FILE" # 结果验证 if [[ -f "${DELTA_PREFIX}.i90.l2000.m.filter.delta" ]]; then echo "过滤成功! 结果文件:" | tee -a "$LOG_FILE" ls -lh "${DELTA_PREFIX}.i90.l2000.m.filter.delta" | tee -a "$LOG_FILE" # 记录结果统计信息 echo "统计结果: " | tee -a "$LOG_FILE" show-coords -r -H "${DELTA_PREFIX}.i90.l2000.m.filter.delta" | tee -a "${DELTA_PREFIX}_filtered_coords.txt" else echo "错误: 未生成过滤后的delta文件! 请检查以下日志:" | tee -a "$LOG_FILE" tail -n 20 "delta_filter_stderr.txt" | tee -a "$LOG_FILE" exit 1 fi } | tee -a "$LOG_FILE" echo "==== 作业完成于: $(date) ====" | tee -a "$LOG_FILE"这个代码一直跑空
07-05
#!/bin/bash # ==================== 环境检测函数 ==================== check_environment() { local missing=() local errors=0 echo -e "\033[34m[环境检测] 开始检查系统依赖...\033[0m" # 检测必要软件 echo -n "检查 fpocket ...... " if ! command -v fpocket &> /dev/null; then echo -e "\033[31m缺失\033[0m" missing+=("fpocket") ((errors++)) else echo -e "\033[32m已安装\033[0m" fi echo -n "检查 AutoDock Vina ...... " if ! command -v vina &> /dev/null; then echo -e "\033[31m缺失\033[0m" missing+=("AutoDock Vina") ((errors++)) else echo -e "\033[32m已安装\033[0m" fi echo -n "检查 bc(计算器) ...... " if ! command -v bc &> /dev/null; then echo -e "\033[31m缺失\033[0m" missing+=("bc (计算器工具)") ((errors++)) else echo -e "\033[32m已安装\033[0m" fi # 检测WSL挂载点 echo -n "检查D盘挂载(/mnt/d) ...... " if [[ ! -d "/mnt/d" ]]; then echo -e "\033[31m未挂载\033[0m" missing+=("Windows D盘未挂载到/mnt/d") ((errors++)) else echo -e "\033[32m已挂载\033[0m" fi # 检测输入文件 echo -n "检查配体文件 ...... " if [[ ! -f "$LIGAND_FILE" ]]; then echo -e "\033[31m不存在\033[0m" missing+=("配体文件不存在: $LIGAND_FILE") ((errors++)) else echo -e "\033[32m存在\033[0m" fi echo -n "检查蛋白质目录 ...... " if [[ ! -d "$PROTEIN_DIR" ]]; then echo -e "\033[31m不存在\033[0m" missing+=("蛋白质目录不存在: $PROTEIN_DIR") ((errors++)) elif [[ -z "$(ls -A "$PROTEIN_DIR"/*.pdbqt 2>/dev/null)" ]]; then echo -e "\033[31m无PDBQT文件\033[0m" missing+=("蛋白质目录为空或没有PDBQT文件: $PROTEIN_DIR") ((errors++)) else echo -e "\033[32m正常\033[0m" fi # 如果有缺失项则报错 if [[ $errors -gt 0 ]]; then echo -e "\n\033[41m!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\033[0m" echo -e "\033[41m【致命错误】环境检测失败!缺少以下组件或配置:\033[0m" for item in "${missing[@]}"; do echo -e " - \033[31m$item\033[0m" done echo -e "\n\033[43m【解决方案】请执行以下操作:\033[0m" if [[ " ${missing[*]} " =~ "fpocket" ]]; then echo " - 安装fpocket: sudo apt install fpocket" fi if [[ " ${missing[*]} " =~ "AutoDock Vina" ]]; then echo " - 安装AutoDock Vina: sudo apt install autodock-vina" fi if [[ " ${missing[*]} " =~ "bc" ]]; then echo " - 安装bc计算器: sudo apt install bc" fi if [[ " ${missing[*]} " =~ "挂载" ]]; then echo " - 确认Windows D盘已挂载,或修改脚本中的WSL_D变量" echo " - 尝试手动挂载: sudo mkdir -p /mnt/d && sudo mount -t drvfs D: /mnt/d" fi if [[ " ${missing[*]} " =~ "配体文件" || " ${missing[*]} " =~ "蛋白质目录" ]]; then echo " - 检查文件路径是否正确,确保文件存在于WSL环境中" echo " - 当前配置的蛋白质目录: $PROTEIN_DIR" echo " - 当前配置的配体文件: $LIGAND_FILE" fi echo -e "\033[41m!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\033[0m\n" exit 1 fi echo -e "\033[42m[环境检测] 所有依赖项检查通过!\033[0m" return 0 } # ==================== 用户配置区域 ==================== WINDOWS_D="D:" # 原始Windows路径(仅参考) WSL_D="/mnt/d" # WSL挂载路径(必须修改为实际值) BASE_DIR="$WSL_D/Autodocksuite" # 基础工作目录 # 输入输出目录 PROTEIN_DIR="$BASE_DIR/proteins" # 蛋白质目录 LIGAND_FILE="$BASE_DIR/Afatinib.pdbqt" # 配体文件 FPOCKET_DIR="$BASE_DIR/fpocket_results" # fpocket结果目录 OUTPUT_DIR="$BASE_DIR/output" # 输出目录 # ==================== 脚本主体 ==================== # 首先执行环境检测 check_environment || exit 1 # 创建目录结构 mkdir -p "$FPOCKET_DIR" "$OUTPUT_DIR" if [[ $? -ne 0 ]]; then echo -e "\033[41m【致命错误】无法创建输出目录!\033[0m" echo -e "请检查路径权限: $OUTPUT_DIR" exit 1 fi LOG_FILE="$OUTPUT_DIR/docking_log_$(date +%Y%m%d_%H%M%S).txt" # 记录开始信息 echo "=== 分子对接工作流程开始 ===" | tee -a "$LOG_FILE" echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')" | tee -a "$LOG_FILE" echo "蛋白质目录: $PROTEIN_DIR" | tee -a "$LOG_FILE" echo "蛋白质数量: $(ls "$PROTEIN_DIR"/*.pdbqt | wc -l)" | tee -a "$LOG_FILE" echo "配体文件: $LIGAND_FILE" | tee -a "$LOG_FILE" # 步骤1: fpocket口袋预测 echo -e "\n\033[44m[阶段1] 蛋白质结合口袋预测\033[0m" | tee -a "$LOG_FILE" protein_count=0 processed_files=0 for protein in "$PROTEIN_DIR"/*.pdbqt; do # 检查文件是否存在 if [[ ! -f "$protein" ]]; then continue fi base_name=$(basename "$protein" .pdbqt) echo "处理: $base_name" | tee -a "$LOG_FILE" # 临时转换格式供fpocket使用 if ! grep -E '^ATOM|^HETATM' "$protein" > "$FPOCKET_DIR/temp_$base_name.pdb"; then echo -e "\033[33m[警告] $base_name PDBQT转换PDB失败,跳过\033[0m" | tee -a "$LOG_FILE" continue fi # 运行fpocket预测口袋 echo "执行fpocket预测..." | tee -a "$LOG_FILE" if ! fpocket -f "$FPOCKET_DIR/temp_$base_name.pdb" -o "$FPOCKET_DIR/$base_name" >> "$LOG_FILE" 2>&1; then echo -e "\033[33m[警告] $base_name 口袋预测失败,跳过\033[0m" | tee -a "$LOG_FILE" rm -f "$FPOCKET_DIR/temp_$base_name.pdb" continue fi # 清理临时文件 rm -f "$FPOCKET_DIR/temp_$base_name.pdb" ((protein_count++)) ((processed_files++)) # 进度报告 if ((processed_files % 50 == 0)); then echo -e "\033[45m[进度] 已处理 $processed_files 个蛋白质\033[0m" | tee -a "$LOG_FILE" fi done if [[ $protein_count -eq 0 ]]; then echo -e "\033[41m【致命错误】没有成功处理任何蛋白质!\033[0m" | tee -a "$LOG_FILE" exit 1 fi echo "fpocket口袋预测完成! 成功处理 $protein_count 个蛋白质" | tee -a "$LOG_FILE" # 步骤2: 分子对接 echo -e "\n\033[44m[阶段2] 分子对接处理\033[0m" | tee -a "$LOG_FILE" SUMMARY_FILE="$OUTPUT_DIR/summary_results.csv" echo "蛋白质,最佳亲和力(kcal/mol),口袋中心X,口袋中心Y,口袋中心Z,盒子尺寸" > "$SUMMARY_FILE" processed_count=0 success_count=0 error_count=0 for protein in "$PROTEIN_DIR"/*.pdbqt; do if [[ ! -f "$protein" ]]; then continue fi base_name=$(basename "$protein" .pdbqt) pocket_info="$FPOCKET_DIR/$base_name/temp_${base_name}_info.txt" if [[ ! -f "$pocket_info" ]]; then echo -e "\033[33m[警告] $base_name 无口袋信息,跳过\033[0m" | tee -a "$LOG_FILE" echo "$base_name,NA,NA,NA,NA,NA" >> "$SUMMARY_FILE" ((error_count++)) continue fi # 解析口袋信息 - 更健壮的方法 best_pocket=$(grep "^Pocket " "$pocket_info" 2>/dev/null | sort -k6 -nr | head -1) pocket_num=$(grep "^Pocket " "$pocket_info" | sort -k6 -nr | head -1 | awk '{print $2}') center_line=$(grep -A1 "Pocket $pocket_num" "$pocket_info" | grep "Center :") if [[ -z "$best_pocket" ]]; then echo -e "\033[33m[警告] $base_name 无有效口袋,跳过\033[0m" | tee -a "$LOG_FILE" echo "$base_name,NA,NA,NA,NA,NA" >> "$SUMMARY_FILE" ((error_count++)) continue fi if [[ -z "$center_line" ]]; then echo -e "\033[33m[警告] $base_name 无中心坐标信息,跳过\033[0m" continue fi # 提取参数 - 使用awk确保健壮性 cx=$(echo "$center_line" | awk '{print $3}') cy=$(echo "$center_line" | awk '{print $4}') cz=$(echo "$center_line" | awk '{print $5}') size=$(echo "$best_pocket" | awk '{print $11}') volume=$(grep -A10 "Pocket $pocket_num" "$pocket_info" | grep -m1 "Volume :" | awk '{print $3}') box_size=$(echo "scale=2; e( l($volume)/3 ) + 5" | bc -l 2>/dev/null || echo 20) # 验证数值有效性 if ! [[ "$cx" =~ ^-?[0-9.]+$ ]] || ! [[ "$cy" =~ ^-?[0-9.]+$ ]] || ! [[ "$cz" =~ ^-?[0-9.]+$ ]] || ! [[ "$size" =~ ^[0-9.]+$ ]]; then echo -e "\033[33m[警告] $base_name 口袋参数无效,跳过\033[0m" | tee -a "$LOG_FILE" echo "$base_name,INVALID,NA,NA,NA,NA" >> "$SUMMARY_FILE" ((error_count++)) continue fi # 计算盒子尺寸(在口袋大小基础上增加padding) if [[ -z "$box_size" ]]; then box_size=20.0 echo -e "\033[33m[警告] $base_name 盒子尺寸计算失败,使用默认值20Å\033[0m" | tee -a "$LOG_FILE" fi # 确保盒子尺寸在合理范围内 box_size=$(echo "$box_size" | awk -v min=15 -v max=30 \ '{if ($1 < min) print min; else if ($1 > max) print max; else print $1}') # 创建输出子目录 protein_output_dir="$OUTPUT_DIR/$base_name" mkdir -p "$protein_output_dir" # 运行AutoDock Vina echo "对接: $base_name | 中心($cx, $cy, $cz) | 盒子: ${box_size}Å" | tee -a "$LOG_FILE" vina_cmd=( vina --receptor "$protein" --ligand "$LIGAND_FILE" --center_x "$cx" --center_y "$cy" --center_z "$cz" --size_x "$box_size" --size_y "$box_size" --size_z "$box_size" --exhaustiveness 8 --num_modes 5 --out "$protein_output_dir/${base_name}_out.pdbqt" --log "$protein_output_dir/${base_name}_log.txt" ) if ! "${vina_cmd[@]}" >> "$LOG_FILE" 2>&1; then echo -e "\033[31m[错误] $base_name 对接失败\033[0m" | tee -a "$LOG_FILE" echo "$base_name,FAILED,$cx,$cy,$cz,$box_size" >> "$SUMMARY_FILE" ((error_count++)) else # 提取最佳亲和力 - 更健壮的提取方法 log_file="$protein_output_dir/${base_name}_log.txt" affinity=$(awk '/^-----+/ {flag=1} flag && /^ 1 / {print $2; exit}' "$log_file") if [[ -n "$affinity" && "$affinity" =~ ^-?[0-9.]+$ ]]; then echo "$base_name,$affinity,$cx,$cy,$cz,$box_size" >> "$SUMMARY_FILE" ((success_count++)) echo -e "\033[32m[成功] $base_name 对接完成,亲和力: $affinity kcal/mol\033[0m" | tee -a "$LOG_FILE" else echo -e "\033[31m[错误] $base_name 亲和力解析失败\033[0m" | tee -a "$LOG_FILE" echo "$base_name,PARSE_ERROR,$cx,$cy,$cz,$box_size" >> "$SUMMARY_FILE" ((error_count++)) fi fi # 进度报告 ((processed_count++)) if ((processed_count % 20 == 0)); then echo -e "\033[45m[进度] 已处理 $processed_count 个 | 成功: $success_count | 失败: $error_count\033[0m" | tee -a "$LOG_FILE" fi done # 最终统计 total_proteins=$(ls "$PROTEIN_DIR"/*.pdbqt | wc -l) success_rate=$(echo "scale=2; $success_count * 100 / $total_proteins" | bc) # 结果报告 echo -e "\n\033[42m[完成] 任务执行完毕\033[0m" | tee -a "$LOG_FILE" echo "总处理: $total_proteins 个蛋白质" | tee -a "$LOG_FILE" echo "成功对接: $success_count 个 (成功率: ${success_rate}%)" | tee -a "$LOG_FILE" echo "失败数量: $error_count 个" | tee -a "$LOG_FILE" # 显示最佳结果 if [[ $success_count -gt 0 ]]; then echo -e "\n\033[44m最佳结合结果(亲和力越小越好):\033[0m" | tee -a "$LOG_FILE" grep -vE "NA|FAILED|INVALID|PARSE_ERROR" "$SUMMARY_FILE" | \ sort -t, -k2 -n | \ head -10 | \ awk -F, '{printf "%-20s %10.2f kcal/mol\n", $1, $2}' | \ tee -a "$LOG_FILE" else echo -e "\n\033[41m警告: 没有成功对接的蛋白质!\033[0m" | tee -a "$LOG_FILE" fi # 最终路径提示 echo -e "\n\033[43m输出文件位置:\033[0m" | tee -a "$LOG_FILE" echo "结果汇总: $SUMMARY_FILE" | tee -a "$LOG_FILE" echo "详细日志: $LOG_FILE" | tee -a "$LOG_FILE" echo "对接结果目录: $OUTPUT_DIR" | tee -a "$LOG_FILE" echo "fpocket结果目录: $FPOCKET_DIR" | tee -a "$LOG_FILE" # 退出状态 if [[ $success_count -eq 0 ]]; then exit 2 elif [[ $error_count -gt 0 ]]; then exit 1 else exit 0 fi这个代码运行结束后,每个蛋白都检测不到蛋白口袋,这是为什么
07-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值