返回文章列表

影像處理與地理定位資訊解析

本文探討如何使用 ImageMagick 處理影像,包含批次生成縮圖以及解析影像中的 EXIF 和 GPS 地理定位資訊,並提供相關 Shell 指令碼範例與說明。文章也涵蓋了 GNU date 的應用,示範如何判斷閏年、計算特定日期是星期幾以及計算日期之間的間隔天數,提供實用的日期計算指令碼範例。

影像處理 Shell指令碼

在影像處理流程中,縮圖生成和地理位置資訊解析是常見的需求。利用 ImageMagick 可以有效率地批次處理圖片縮放,並擷取 EXIF 中的 GPS 資料。這些技術能應用於相簿管理、地圖示記等場景。同時,精確的日期計算對於許多應用至關重要。GNU date 提供了強大的日期處理功能,可以簡化閏年判斷、星期計算和日期間隔計算等任務,提升指令碼的效率和可靠性。

影像處理與地理定位資訊解析

在數位影像處理領域中,ImageMagick 是一款非常強大的工具,不僅能夠處理影像縮放、裁剪等基本操作,還能夠解析影像中的EXIF資訊。特別是在現代智慧型手機和數位相機中,照片往往包含了GPS地理定位資訊。本文將探討如何利用 ImageMagick 提取和分析這些資訊。

影像縮圖批次處理指令碼

在開始之前,我們先來看一個簡單的指令碼,用於批次生成不同尺寸的縮圖。

縮圖生成指令碼解析

#!/bin/bash
# thumbnails - 生成不同尺寸的縮圖
if [ $# -eq 0 ]; then
  echo "用法: $0 (-e|-f) 縮圖尺寸 圖片檔案 [圖片檔案] ..."
  exit 1
fi

while getopts ":ef:s" opt; do
  case $opt in
    e) exact=true ;;
    f) exact=false ;;
    s) strip=true ;;
    \?) echo "無效選項: -$OPTARG"; exit 1 ;;
  esac
done

shift $((OPTIND-1))
size=$1
shift

for file in "$@"; do
  if [ "$exact" = true ]; then
    convert "$file" -resize "$size"! "${file%.*}_${size}.jpg"
  else
    convert "$file" -resize "$size>" "${file%.*}_${size}.jpg"
  fi
  
  if [ "$strip" = true ]; then
    mogrify -strip "${file%.*}_${size}.jpg"
  fi
done

#### 內容解密:

  1. 引數解析:使用 getopts 解析命令列引數,支援 -e(精確縮放)、-f(比例縮放)和 -s(移除EXIF資訊)。
  2. 迴圈處理檔案:對每個輸入的圖片檔案進行縮圖生成。
  3. ImageMagick 命令:使用 convert 命令進行圖片縮放,使用 mogrify 命令移除EXIF資訊。

地理定位資訊解析指令碼

接下來,我們將重點放在解析照片中的GPS地理定位資訊。

GPS資訊提取與轉換指令碼

#!/bin/bash
# geoloc - 將圖片中的GPS資訊轉換為可用的地理定位字串

tempfile="/tmp/geoloc.$$"
trap "rm -f $tempfile" 0 1 15

if [ $# -eq 0 ]; then
  echo "用法: $0 圖片檔案" >&2
  exit 1
fi

for filename in "$@"; do
  identify -format "%[EXIF:*]" "$filename" | grep GPSL > $tempfile
  
  latdeg=$(head -1 $tempfile | cut -d, -f1 | cut -d= -f2)
  latdeg=$(scriptbc -p 0 $latdeg)
  latmin=$(head -1 $tempfile | cut -d, -f2)
  latmin=$(scriptbc -p 0 $latmin)
  latsec=$(head -1 $tempfile | cut -d, -f3)
  latsec=$(scriptbc $latsec)
  
  # ... (省略其他GPS資訊提取與計算)
  
  echon "座標: $latdeg ${latmin}' ${latsec}\" $latorientation, "
  echo "$longdeg ${longmin}' ${longsec}\" $longorientation"
done

exit 0

#### 內容解密:

  1. EXIF資訊提取:使用 identify 命令提取圖片的EXIF資訊,並過濾出GPS相關資訊。
  2. GPS資訊解析:透過多個 cutscriptbc 命令解析和計算GPS資訊,轉換為人類可讀的格式。
  3. 輸出結果:使用 echonecho 輸出轉換後的GPS座標。

日期計算實戰:使用 GNU date 提升效率

在處理日期相關的任務時,GNU date 提供了一個強大的工具來簡化複雜的計算。本文將探討如何利用 GNU date 來判斷閏年、計算特定日期是星期幾,以及計算日期之間的間隔天數。

判斷閏年

判斷一個年份是否為閏年,可以使用以下簡單的命令:

if [ $(date -d 12/31/$year +%j) -eq 366 ]; then
    echo "$year 是閏年"
fi

這段程式碼利用 date 命令計算指定年份的最後一天是當年的第幾天。如果結果是 366,則該年份為閏年。

程式碼解析:

  1. date -d 12/31/$year +%j:計算指定年份的最後一天是當年的第幾天。
  2. if [ $(...) -eq 366 ]:檢查結果是否等於 366,以判斷是否為閏年。

計算特定日期是星期幾

要計算過去某個特定日期是星期幾,可以使用以下指令碼:

#!/bin/bash
# dayinpast:給定日期,報告當天是星期幾

if [ $# -ne 3 ]; then
    echo "用法: $(basename $0) 月 日 年" >&2
    exit 1
fi

date --version > /dev/null 2>&1
if [ $? -ne 0 ]; then
    # 使用 ncal 作為替代方案
    dayofweek="$(ncal $1 $3 | grep "$2[^0-9]" | cut -c1-2)"
    case $dayofweek in
        Su) echo "那天是星期日" ;;
        Mo) echo "那天是星期一" ;;
        Tu) echo "那天是星期二" ;;
        We) echo "那天是星期三" ;;
        Th) echo "那天是星期四" ;;
        Fr) echo "那天是星期五" ;;
        Sa) echo "那天是星期六" ;;
    esac
else
    date -d $1/$2/$3 +"那天是 %A"
fi

程式碼解析:

  1. 首先檢查輸入引數是否正確。
  2. 使用 date --version 檢查系統是否支援 GNU date。如果支援,直接使用 date 命令計算指定日期是星期幾。
  3. 如果不支援 GNU date,則使用 ncal 命令來查詢日曆,並解析出當天的星期。

計算日期之間的間隔天數

要計算兩個日期之間的間隔天數,可以使用以下指令碼:

#!/bin/bash
# daysago:計算指定日期距離今天的天數

if [ $# -ne 3 ]; then
    echo "用法: $(basename $0) 月 日 年" >&2
    exit 1
fi

date="$(which gdate)"
if [ $? -ne 0 ]; then
    echo "抱歉,本指令碼需要 GNU date。" >&2
    exit 1
fi

startmon=$1; startday=$2; startyear=$3
eval $($date "+thismon=%m;thisday=%d;thisyear=%Y;dayofyear=%j")

# 計算指定日期到當年年底的天數
startdatefmt="$startmon/$startday/$startyear"
daysleftinyear=$(( $(date -d "12/31/$startyear" +%j) - $(date -d $startdatefmt +%j) ))

# 計算中間年份的天數
daysbetweenyears=0
tempyear=$(( $startyear + 1 ))
while [ $tempyear -lt $thisyear ]; do
    daysbetweenyears=$(( $daysbetweenyears + $(date -d "12/31/$tempyear" +%j) ))
    tempyear=$(( $tempyear + 1 ))
done

# 當年已經過去的天數
dayofyear=$($date +%j)

# 總計天數
totaldays=$(( $daysleftinyear + $daysbetweenyears + $dayofyear ))
echo "$totaldays 天已經過去了,從 $startmon/$startday/$startyear 到今天。"

程式碼解析:

  1. 首先檢查輸入引數和 GNU date 的可用性。
  2. 計算指定日期到當年年底的天數。
  3. 計算中間年份的天數。
  4. 加上當年已經過去的天數,得出總計天數。

日期計算指令碼:計算指定日期與當前日期之間的天數

介紹

在處理日期相關的任務時,計算兩個日期之間的天數是一項常見的需求。本篇文章將探討一個用於計算未來指定日期與當前日期之間天數的Shell指令碼——daysuntil。該指令碼能夠根據使用者輸入的月份、日和年份,計算出距離該日期還有多少天。

指令碼解析

1. 日期驗證與初始化

date="$(which gdate)"
function daysInMonth {
  case $1 in
    1|3|5|7|8|10|12 ) dim=31 ;;
    4|6|9|11 ) dim=30 ;;
    2 ) dim=29 ;;
    * ) dim=-1 ;;
  esac
}

function isleap {
  leapyear=$($date -d 12/31/$1 +%j | grep 366)
}

內容解密:

  • daysInMonth函式根據輸入的月份傳回該月的天數。對於二月,預設為29天,具體是否為閏年將在後續驗證。
  • isleap函式檢查指定的年份是否為閏年。它透過計算該年最後一天是第幾天(Julian day)來判斷,若結果包含366則表示是閏年。

2. 輸入引數檢查與處理

if [ $# -ne 3 ] ; then
  echo "Usage: $(basename $0) mon day year"
  exit 1
fi

$date --version > /dev/null 2>&1
if [ $? -ne 0 ] ; then
  echo "Sorry, but $(basename $0) can't run without GNU date." >&2
  exit 1
fi

eval $($date "+thismon=%m;thisday=%d;thisyear=%Y;dayofyear=%j")
endmon=$1; endday=$2; endyear=$3

內容解密:

  • 首先檢查輸入引數是否正確,daysuntil需要三個引數:月份、日和年份。
  • 驗證GNU date命令是否可用,否則指令碼無法正常執行。
  • 使用date命令取得當前日期的月份、日、年份和Julian day,並將輸入的日期引數指定給相應變數。

3. 日期合法性檢查

daysInMonth $endmon
if [ $endday -lt 0 -o $endday -gt $dim ] ; then
  echo "Invalid: Month #$endmon only has $dim days." >&2
  exit 1
fi

if [ $endmon -eq 2 -a $endday -eq 29 ] ; then
  isleap $endyear
  if [ -z "$leapyear" ] ; then
    echo "Invalid: $endyear wasn't a leapyear; February had 28 days." >&2
    exit 1
  fi
fi

內容解密:

  • 呼叫daysInMonth函式檢查輸入的日是否在該月的有效範圍內。
  • 特別檢查二月29日是否為閏年,若不是則提示無效日期。

4. 計算日期差異

if [ $endyear -eq $thisyear ] ; then
  totaldays=$(( $($date -d "$endmon/$endday/$endyear" +%j) - $($date +%j) ))
else
  # 跨年計算邏輯
fi

echo "There are $totaldays days until the date $endmon/$endday/$endyear."

內容解密:

  • 當目標年份與當前年份相同時,直接計算兩個日期的Julian day差值。
  • 若跨年,則分別計算起始年剩餘天數、中間年份總天數和目標年已過天數,最後相加得到總天數。

使用範例

$ daysuntil 1 1 2025
There are XXX days until the date 1/1/2025.

只需簡單地輸入月份、日和年份,即可獲得距離該日期的天數。對於需要進行日期規劃或提醒的使用者來說,這是一個非常實用的工具。

未來改進方向

  • 結合daysago指令碼,形成一個統一的日期計算工具,既能計算過去日期距今的天數,也能計算未來日期距今的天數。
  • 增加對不同日期格式的支援,提高使用者使用的靈活性。

在 Windows 10 上啟用開發者模式與安裝 Bash

微軟在 Windows 10 中加入了對 Linux 的支援,允許使用者在 Windows 環境下執行 Bash shell。為了使用這一功能,使用者首先需要啟用開發者模式並安裝 Windows Subsystem for Linux。

啟用開發者模式

  1. 進入設定: 首先,開啟 Windows 10 的「設定」應用。

  2. 搜尋開發者模式: 在設定中搜尋「開發者模式」。

  3. 選擇開發者模式: 在「使用開發人員功能」部分,選擇「開發者模式」。

    當選擇開發者模式時,Windows 可能會警告您這樣做可能會使您的裝置面臨風險。這是因為在開發者模式下,您可以從非核準的網站安裝程式。然而,只要您保持謹慎,這個風險是可以接受的。

  4. 確認並安裝必要的軟體: 繼續後,Windows 將下載並安裝一些額外的軟體,這可能需要幾分鐘。

啟用 Windows Subsystem for Linux

  1. 開啟或關閉 Windows 功能: 搜尋「開啟或關閉 Windows 功能」並開啟對應的視窗。
  2. 啟用 Windows Subsystem for Linux: 在列表中找到「Windows Subsystem for Linux (Beta)」並勾選它。點選「確定」後,Windows 將提示您重新啟動系統以完全啟用 Linux 子系統。

安裝 Bash

  1. 開啟命令提示字元: 在開始功能表中搜尋「命令提示字元」並開啟它。
  2. 輸入 bash 指令: 在命令提示字元中輸入 bash,然後按 Enter。系統將提示您安裝 Bash 軟體。
  3. 完成安裝: 輸入 y 以開始下載和安裝。安裝完成後,您將被提示輸入 Unix 使用者名稱和密碼。

使用 Bash

安裝完成後,您就可以在 Windows 10 中使用 Bash 了。只需開啟命令提示字元,輸入 bash,即可進入 Bash 環境。

微軟的 Bash Shell 與 Linux 發行版的比較

雖然在 Windows 10 上執行 Bash 是一個有趣的功能,但對於需要全面使用 Linux 的使用者來說,雙重啟動或在虛擬機器中執行 Linux 發行版可能是更好的選擇。

附錄 B:額外的 Shell 指令碼

大量重新命名檔案的指令碼

系統管理員經常需要處理大量檔案的重新命名工作。手動重新命名大量檔案是不切實際的,因此編寫一個 shell 指令碼來自動完成這項任務是非常有用的。

bulkrename 指令碼

#!/bin/bash
# bulkrename--Renames specified files by replacing text in the filename

printHelp() {
  echo "Usage: $0 -f find -r replace FILES_TO_RENAME*"
  echo -e "\t-f The text to find in the filename"
  echo -e "\t-r The replacement text for the new filename"
  exit 1
}

while getopts "f:r:" opt; do
  case "$opt" in
    r) replace="$OPTARG";;
    f) match="$OPTARG";;
    ?) printHelp;;
  esac
done

shift $(( $OPTIND - 1 ))

if [ -z "$replace" ] || [ -z "$match" ]; then
  echo "You need to supply a string to find and a string to replace"
  printHelp
fi

for i in "$@"; do
  newname=$(echo "$i" | sed "s/$match/$replace/")
  mv "$i" "$newname" && echo "Renamed file $i to $newname"
done

內容解密:

  • printHelp() 函式:用於列印指令碼的使用方法。
  • while 迴圈:使用 getopts 解析命令列選項,-f-r 分別指定要尋找和替換的文字。
  • shift 指令:將命令列引數指標向前移動,以便處理剩餘的檔案列表。
  • if 陳述式:檢查是否提供了必要的引數(要尋找和替換的文字)。
  • for 迴圈:遍歷檔案列表,使用 sed 命令替換檔名中的指定文字,並重新命名檔案。