shell編程入門

来源:https://www.cnblogs.com/wyq1995/archive/2020/03/11/12466193.html
-Advertisement-
Play Games

最近由於工作原因,寫了幾個腳本,然後再linux中用定時任務去跑,記錄一下shell腳本的學習過程吧! 首先知道shell第一行#!/bin/bash表示這是一個shell腳本,例如下麵這個簡單的腳本: #!/bin/bash #author:java小新人 #date:20200311 #desc ...


  最近由於工作原因,寫了幾個腳本,然後再linux中用定時任務去跑,記錄一下shell腳本的學習過程吧!

  首先知道shell第一行#!/bin/bash表示這是一個shell腳本,例如下麵這個簡單的腳本:

#!/bin/bash

#author:java小新人

#date:20200311

#description:用於清理14天之前日誌文件夾

#這裡就是要進行進行清理的nas中的目錄
baseDir="/usr/local/java/shellscript/store"

#伺服器中不存在該目錄就退出
if [ ! -d ${baseDir} ];then
  echo "不存在目錄:${baseDir}"
  exit 1
fi

#這裡指定前段時間第n天日誌需要進行清理
num=14

#需要清理的日期轉換成時間戳
barrierDate=`date -d "$num days ago" +%F`
barrierDateStamp=`date -d "${barrierDate}" +%s`

#遍歷目錄下的所有日誌文件進行正則匹配
for file in `ls ${baseDir}`;do
  #獲取目錄中文件名中的日期
  dirDate=`expr "${file}" : '\([0-9]\{4\}\-[0-9]\{2\}\-[0-9]\{2\}\)'`
  #目錄中的日誌文件名沒有日期在其中,說明不需要刪除,就跳過,進行下一次迴圈
  if [ -z "${dirDate}" ];then
    continue
  fi
  #獲取該文件文件名的時間戳,當該時間符合需要清理的時間的時候,再判斷一次該目錄是否存在,保險一點!存在的話就刪除就行了
  dirDateTimeStamp=`date -d "${dirDate}" +%s`

  if [ ${dirDateTimeStamp} -lt ${barrierDateStamp} ];then
    echo "當前遍歷的目錄是:${file},目錄時間是:${dirDate}"
    #當前日誌目錄所在的絕對路徑
    fileAbsPath="${baseDir}/${file}"
      if [ -d ${basePath} ];then
        echo "對${file}文件進行刪除"
        rm -rf ${fileAbsPath}
        echo "日誌目錄刪除成功:${fileAbsPath}"
      else
        echo "刪除目錄發生錯誤:${fileAbsPath}"
      fi
  fi
done

 

  上面的目錄中存放的就是每天生成的日誌文件,粗略看一下上面這個簡單的腳本,由於本人寫腳本的水平摳腳,只能用這種很直白的寫法,沒有任何花里胡哨.....

  就是按照java代碼那樣的邏輯寫的,應該都能看得懂,基本上是一些for迴圈和if判斷;還有一個echo語句表示在命令行中輸出相關信息,如果使用echo "xxx" >> /usr/local/file_name.log表示將echo輸出的信息追加到/usr/local/file_name.log文件中;

  註意>和>>的區別,>是覆蓋的意思,比如用命令清空一個文件,最簡單的就是echo “” > a.txt;而>>表示在文件後面追加,不會覆蓋

 

1.先說說for迴圈

  下麵這個就是遍歷一個目錄下的子目錄的,不能遍歷文件的哦!那個反引號,表示讓系統去執行ls /etc命令,什麼叫做讓系統去執行呢?就跟我們手動敲這條命令去執行一樣;還有一點,就是shell中要使用一個聲明好的變數,需要用${xxx}表示,可以不加大括弧,習慣加上好看點;

  最後就是要以done表示這個for迴圈結束;

for file in `ls /etc`;do
    echo ${file}
done

 

 2.if語句

  if和for寫法差不多,下麵這個作用是:當根目錄下沒有Top這個目錄,就創建Top目錄;註意,if後面的中括弧那麼多空格,不能少!!!if語句要以fi結尾;

  那麼問題來了,那個-d是幹嘛的呀?下麵列出來了很多-xx的,寫腳本的時候拿出來看一看就行;發現判斷文件或者是字元串是不是空都可以用這種-xx的方式判斷;

if [ ! -d "/Top" ]; then
  mkdir -p /Top
fi

 

[ -a FILE ]  如果 FILE 存在則為真。  

[ -b FILE ]  如果 FILE 存在且是一個塊特殊文件則為真。  

[ -c FILE ]  如果 FILE 存在且是一個字特殊文件則為真。  

[ -d FILE ]  如果 FILE 存在且是一個目錄則為真。  

[ -e FILE ]  如果 FILE 存在則為真。  

[ -f FILE ]  如果 FILE 存在且是一個普通文件則為真。  

[ -g FILE ]  如果 FILE 存在且已經設置了SGID則為真。  

[ -h FILE ]  如果 FILE 存在且是一個符號連接則為真。  

[ -k FILE ]  如果 FILE 存在且已經設置了粘制位則為真。  

[ -p FILE ]  如果 FILE 存在且是一個名字管道(F如果O)則為真。  

[ -r FILE ]  如果 FILE 存在且是可讀的則為真。  

[ -s FILE ]  如果 FILE 存在且大小不為0則為真。  

[ -t FD ]  如果文件描述符 FD 打開且指向一個終端則為真。  

[ -u FILE ]  如果 FILE 存在且設置了SUID (set user ID)則為真。  

[ -w FILE ]  如果 FILE 如果 FILE 存在且是可寫的則為真。  

[ -x FILE ]  如果 FILE 存在且是可執行的則為真。  

[ -O FILE ]  如果 FILE 存在且屬有效用戶ID則為真。  

[ -G FILE ]  如果 FILE 存在且屬有效用戶組則為真。  

[ -L FILE ]  如果 FILE 存在且是一個符號連接則為真。  

[ -N FILE ]  如果 FILE 存在 and has been mod如果ied since it was last read則為真。  

[ -S FILE ]  如果 FILE 存在且是一個套接字則為真。  

[ FILE1 -nt FILE2 ]  如果 FILE1 has been changed more recently than FILE2, or 如果 FILE1 exists and FILE2 does not則為真。  

[ FILE1 -ot FILE2 ]  如果 FILE1 比 FILE2 要老, 或者 FILE2 存在且 FILE1 不存在則為真。  

[ FILE1 -ef FILE2 ]  如果 FILE1 和 FILE2 指向相同的設備和節點號則為真。  

[ -o OPTIONNAME ]  如果 shell選項 “OPTIONNAME” 開啟則為真。  

[ -z STRING ]  字元串“STRING” 的長度為零則為真。 或者字元串為NULL時也為真。 

[ -n STRING ]  和-z相反,預設不加-n也行,也就是說這個寫法和[STRING]是一樣的

[ STRING1 == STRING2 ]  如果2個字元串相同。 “=” may be used instead of “==” for strict POSIX compliance則為真。  

[ STRING1 != STRING2 ]  如果字元串不相等則為真。

 

 3.數組的遍歷

  可以看到數組中每個元素用空格分隔就行了,如果是字元串數組也是一樣的,例如:dirs=("/log1" "/log2");

  註意下麵的for迴圈中有兩個小括弧啊,括弧裡面的${#arr(*)}表示數組裡面元素的個數,註意和${arr[*]}的區別,這個表示數組中所有的實際元素

arr=(1 2 3 4)
for((i=0;${#arr[*])};i++));do
   echo ${arr[i]}
done

  

  上面的for迴圈也可以用for in迴圈表示,,如下所示,看到這裡應該很多人微微一笑,很多變成語言應該都知道這兩種迴圈吧!

arr=(1 2 3 4)
for a in ${arr[*]};do
   echo ${a}
done

 

 4.比較運算

  很多時候我們需要比較兩個字元串是否相同,如下所示,註意,中括弧兩邊的空格啊!==號兩邊空格,不要吝嗇空格,不然你會發現一些奇葩的錯誤;

if [ ${a} == ${b} ];then
    echo "相同"
else
    echo "不相同"
fi

   

  那麼又有人要問了,數字的比較呢?註意,此時是沒有大於號,小於號這種東西的(這裡不用轉義符號。。)

左邊等於右邊: $a -eq $b;
左邊不等於右邊: $a -ne $b;
左邊大於右邊: $a -gt $b;
左邊小於右邊: $a -lt $b;
左邊大於等於右邊: $a -ge $b;
左邊小於等於右邊: $a -le $b;

 

  這裡註意一點東西,=,==和-eq都可以用來比較是否相等,都可以用在if後面的中括弧中;在[ ]中=和==效果一樣,在(( ))中=表示賦值,而==表示比較;那麼eq和==的區別在哪呢?==可以比較字元串和數字,而eq只能比較數字(eq可以比較這樣的字元串[ "20" -eq "20" ],不能比較[ "hello" -eq "hello" ],會報錯),所以儘量用==;

#!/bin/bash

#不會報錯,列印false
 if [ "a" == "" ];then
   echo "true"
 else
   echo "false"
 fi

#會報錯
 if [ "a" -eq "" ];then
   echo "true"
 else
   echo "false"
 fi

 

   另外,註意[[  ]]和[ ]的區別,簡單說一下,只要你使用[[ ]]那麼你想表示&&和||的關係,你可使用這兩個符號,也可以使用對應的-a和-o;但是如果你使用的是[ ]那麼你只能使用-a和-o,還只能在中括弧裡面,例如[ 5 -lt 3 ] -o [ 7 -gt 6 ]就會報錯;

if [[ 5 -lt 3 || 7 -gt 6 ]];then
  echo "true"
else
  echo "false"
fi


if [[ 5 -lt 3 ]] || [[ 7 -gt 6 ]];then
  echo "true"
else
  echo "false"
fi


if [ 5 -lt 3 -o 7 -gt 6 ];then
  echo "true"
else
  echo "false"
fi


if [ 5 -lt 3 ] && [ 7 -gt 6 ];then
  echo "true"
else
  echo "false"
fi

 

 5.時間

  比較常見的就是腳本中處理時間:

獲取今天的日期:todayDate=`date -d now +%Y-%m-%d`或者`date +%F`
明天日期:`date -d next-day +%Y-%m-%d`或者`date -d tomorrow +%Y-%m-%d`
昨天日期:`date -d "1 years ago" +%Y-%m-%d`
第n天前的日期:`date -d "n days ago" +%F`
轉換成時間戳:stamp=`date -d "${todayDate}" +s`

 

 6.正則匹配

  例如一個日誌文件是這樣的sgffg.log.2020-02-05.2,那麼用下麵這個正則去匹配,其中${file}表示該日誌文件名;

fileDate=`expr "${file}" : '.*\([0-9]\{4\}\-[0-9]\{2\}\-[0-9]\{2\}\).*'`

 

 7.日誌腳本

#!/bin/bash

#author:java小新人

#date:20200218

#description:主要用於定期備份過期日誌

#這裡表示可以備份多個目錄下的日誌文件
baseDirs=(
"/home/path1" 
"/home/path2" 
"/home/path3"  
)

#想要備份的地方
storePath="/home/store/path"

#這裡指定前段時間第n天日誌需要進行備份
num=7

#備份成功文件的個數
storeLogFileNum=0

taskStartTime=`date "+%Y-%m-%d %H:%M:%S"`
#因為需要把日誌按照時間進行分類的,這裡獲取需要清理日誌的那天日期以及時間戳
barrierDate=`date -d "$num days ago" +%F`
barrierDateStamp=`date -d "${barrierDate}" +%s`

#路徑不存在就創建該目錄
if [ ! -d ${storePath}/${barrierDate} ];then
  mkdir -p ${storePath}/${barrierDate} || exit 1
fi

#將腳本文件中echo輸出的信息追加到文件中,這裡該文件不存在就會創建
storeRecord=${storePath}/${barrierDate}/storeRecord.log

echo "當前時間為${taskStartTime},對日誌文件時間是${barrierDate}的文件開始備份..." >> ${storeRecord}

#開始遍歷所有需要備份日誌的目錄
for ((i=0;i<${#baseDirs[*]};i++));do
  basePath=${baseDirs[i]}
  
  #對存日誌的路徑判斷進行處理,該目錄不存在的話就進行下一次迴圈
  if [ ! -d ${basePath} ];then
    # echo "日誌文件目錄不存在,basePath:${basePath}"
    continue
  fi
  echo "開始對目錄${basePath}下的日誌文件進行處理" >> ${storeRecord}

  #判斷日誌目錄下有沒有日誌文件
  folder=`ls ${basePath}`
  if [ -z ${folder} ];then
    echo "目錄${basePath}中沒有任何文件" >> ${storeRecord}
    continue
  fi

  #遍歷目錄下的所有日誌文件進行正則匹配
  for file in ${folder};do
    #獲取文件名中的日期
    fileDate=`expr "${file}" : '.*\([0-9]\{4\}\-[0-9]\{2\}\-[0-9]\{2\}\).*'`
    #目錄中的日誌文件不符合條件篩選條件,說明該文件不需要備份,跳過,進行下一次迴圈
    #這個很重要!!!!!!!
    if [ -z "${fileDate}" ];then
      continue
    fi

    #不為空就獲取該文件文件名的時間戳,當該時間符合需要清理的時間的時候,就移動到目標目錄中存起來
    fileDateTimeStamp=`date -d "${fileDate}" +%s`

    if [ ${fileDateTimeStamp} -eq ${barrierDateStamp} ];then
      #當前日誌文件所在的絕對路徑
      fileAbsPath="${basePath}/${file}"
      #如果在目標目錄中有重名的,這裡的-b參數會對目標目錄中的同名文件進行備份,不會覆蓋
      echo "對${file}文件進行備份" >> ${storeRecord}
      mv -b ${fileAbsPath} ${storePath}/${barrierDate}
      #日誌文件移動成功的計數器
      ((storeLogFileNum++))
      echo "該文件備份成功" >> ${storeRecord}
    fi
  done
  echo "目錄${basePath}下的日誌文件處理完畢" >> ${storeRecord}
done
taskEndTime=`date "+%Y-%m-%d %H:%M:%S"`
startSeconds=$(date --date="${taskStartTime}" +%s);
endSeconds=$(date --date="${taskEndTime}" +%s);

#執行該任務所花費時間,精確到秒
runTime=$((endSeconds-startSeconds))"s"
echo "日誌備份整理完畢,備份完成時間為${taskEndTime},花費了${runTime},共備份了${storeLogFileNum}個日誌文件" >> ${storeRecord}
echo >> ${storeRecord}
exit 0

 

  我這個腳本在linux定時任務中是每天執行一次,將前面第七天的日誌備份,例如今天是3月8號清理3月1號的,3月9號清理3月2號的...,始終保證最新的一個星期的日誌不被清理;

  備份之後生成的目錄是這樣的:

 

 8.定時任務

  我們肯定不會自己手動去執行這個shell腳本吧!這個時候就要用到linux的定時任務,最重要的時cron表達式;

  定時任務分兩種,一種是系統級別的定時任務,可以通過vim /etc/crontab打開,但是不建議使用這個,這個文件必須要有root許可權才能修改;另外一種是用戶級別的定時任務,預設就是當前用戶,使用crontab -e打開;

  那麼問題來了,cron表達式怕寫錯了怎麼辦?肯定不會自己傻乎乎的等啊,這裡有個線上的cron表達式測試工具https://tool.lu/crontab,可以直接看看你的任務啥時候執行;

  最後,註意一點,使用crontab -e打開之後,需要配置你的shell腳本絕對路徑,例如0 1 * * * /usr/local/del_file.sh,註意哦!!!要給你的腳本添加可執行許可權啊!

  chmod +x del_file.sh

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • springboot的優質開發體驗,能夠更快速的搭建生產環境的項目。這篇文章就是一個超級簡單的入門springboot項目。包含了一些簡單的理論和超簡單Controller層 【工具】 IDEA 全稱IntelliJ IDEA SpringBoot 【創建項目】打開IDEA編輯器,新建項目,然後選擇 ...
  • MyBatis源碼解析 反射模塊 1. 前言 ​ 該模塊位於 包中,MyBatis在進行參數處理、結果映射等操作時,會涉及大量的反射操作。Java 中的反射雖然功能強大,但是代碼編寫起來比較複雜且容易出錯,為了簡化反射操作的相關代碼,MyBatis提供了專門的反射模塊,它對常見的反射操作做了進一步封 ...
  • this語句:用於構造函數之間進行相互調用 this(屬性) this()語句只能定義在構造函數的第一行。因為初始化動作要先執行。 ...
  • Thread.join中使用Object.wait實現: //java.lang.Thread public final synchronized void join(long millis) throws InterruptedException { long base = System.curr ...
  • MyBatis源碼解析 解析器模塊 1. 前言 在MyBatis中涉及多個xml文件,解析這些xml文件自然離不開解析器。本文就來分析一下解析器模塊。 2. 準備工作 xml常見的解析方式分為以下三種: DOM ( Document Object Model)解析方式 SAX (Simple API ...
  • MyBatis 源碼分析 項目總覽 1.概述 本文主要大致介紹一下MyBatis的項目結構。 引用參考資料《MyBatis技術內幕》 此外,https://mybatis.org/mybatis 3/zh/index.html MyBatis官方也提供了很不錯的中文文檔。對於使用中有碰到一些問題,可 ...
  • MyBatis源碼解析 搭建調試環境 1. 相關工具 Maven Git JDK1.8 IntelliJ IDEA 2. 源碼拉取 一般來說,我們直接從https://github.com/mybatis/mybatis 3 Fork到自己的倉庫中,為什麼要Fork呢?我們在之後的源碼分析中,我們可 ...
  • Redis支持RDB與AOF兩種持久化機制,持久化可以避免因進程異常退出或down機導致的數據丟失問題,在下次重啟時能利用之前的持久化文件實現數據恢復。 RDB持久化 RDB持久化即通過創建快照(壓縮的二進位文件)的方式進行持久化,保存某個時間點的全量數據。RDB持久化是Redis預設的持久化方式。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...