在影像處理流程中,縮圖生成和地理位置資訊解析是常見的需求。利用 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
#### 內容解密:
- 引數解析:使用
getopts解析命令列引數,支援-e(精確縮放)、-f(比例縮放)和-s(移除EXIF資訊)。 - 迴圈處理檔案:對每個輸入的圖片檔案進行縮圖生成。
- 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
#### 內容解密:
- EXIF資訊提取:使用
identify命令提取圖片的EXIF資訊,並過濾出GPS相關資訊。 - GPS資訊解析:透過多個
cut和scriptbc命令解析和計算GPS資訊,轉換為人類可讀的格式。 - 輸出結果:使用
echon和echo輸出轉換後的GPS座標。
日期計算實戰:使用 GNU date 提升效率
在處理日期相關的任務時,GNU date 提供了一個強大的工具來簡化複雜的計算。本文將探討如何利用 GNU date 來判斷閏年、計算特定日期是星期幾,以及計算日期之間的間隔天數。
判斷閏年
判斷一個年份是否為閏年,可以使用以下簡單的命令:
if [ $(date -d 12/31/$year +%j) -eq 366 ]; then
echo "$year 是閏年"
fi
這段程式碼利用 date 命令計算指定年份的最後一天是當年的第幾天。如果結果是 366,則該年份為閏年。
程式碼解析:
date -d 12/31/$year +%j:計算指定年份的最後一天是當年的第幾天。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
程式碼解析:
- 首先檢查輸入引數是否正確。
- 使用
date --version檢查系統是否支援 GNU date。如果支援,直接使用date命令計算指定日期是星期幾。 - 如果不支援 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 到今天。"
程式碼解析:
- 首先檢查輸入引數和 GNU date 的可用性。
- 計算指定日期到當年年底的天數。
- 計算中間年份的天數。
- 加上當年已經過去的天數,得出總計天數。
日期計算指令碼:計算指定日期與當前日期之間的天數
介紹
在處理日期相關的任務時,計算兩個日期之間的天數是一項常見的需求。本篇文章將探討一個用於計算未來指定日期與當前日期之間天數的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。
啟用開發者模式
進入設定: 首先,開啟 Windows 10 的「設定」應用。
搜尋開發者模式: 在設定中搜尋「開發者模式」。
選擇開發者模式: 在「使用開發人員功能」部分,選擇「開發者模式」。
當選擇開發者模式時,Windows 可能會警告您這樣做可能會使您的裝置面臨風險。這是因為在開發者模式下,您可以從非核準的網站安裝程式。然而,只要您保持謹慎,這個風險是可以接受的。
確認並安裝必要的軟體: 繼續後,Windows 將下載並安裝一些額外的軟體,這可能需要幾分鐘。
啟用 Windows Subsystem for Linux
- 開啟或關閉 Windows 功能: 搜尋「開啟或關閉 Windows 功能」並開啟對應的視窗。
- 啟用 Windows Subsystem for Linux: 在列表中找到「Windows Subsystem for Linux (Beta)」並勾選它。點選「確定」後,Windows 將提示您重新啟動系統以完全啟用 Linux 子系統。
安裝 Bash
- 開啟命令提示字元: 在開始功能表中搜尋「命令提示字元」並開啟它。
- 輸入 bash 指令: 在命令提示字元中輸入
bash,然後按 Enter。系統將提示您安裝 Bash 軟體。 - 完成安裝: 輸入
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命令替換檔名中的指定文字,並重新命名檔案。