我第一次编写一个有点复杂的 shell 脚本,下面是它应该做的事情:
clientid
在启动过程中,它通过查看文件来找出我的内容host-mapping.txt
。如果我找不到clientid
我的主机名,那么我需要以非零状态代码退出 shell 脚本并记录错误消息。- 现在,一旦我有了有效的
clientid
,我将从文件primary files
中提取primary-mappings.txt
并secondary files
从secondary-mappings.txt
文件中提取该有效的clientid
。如果出于某种原因,我无法从该文件中找到该文件的主文件或辅助文件clientid
,那么我将从 shell 脚本退出并记录一条错误消息。 - 现在,一旦我拥有有效的主文件和辅助文件,我将开始使用from
clientid
并行复制这些文件。所有主要文件将转到文件夹,所有辅助文件将转到文件夹。如果文件不存在于远程服务器上的文件夹中,那么它应该存在于文件夹中。gnu-parallel
local_server
primary
secondary
hold1
hold2
- 现在,一旦复制了所有文件,我将在最后进行验证,以确保这两个文件夹中存在所有主要文件和辅助文件,
clientid
但如果出于某种原因,我找不到这些文件,那么我想退出 shell带有消息的脚本,告诉我缺少哪些文件。
下面是我的脚本,它完成了工作,但我想看看是否有更好或更有效的方法来完成上述事情,因为这是我第一次编写小复杂的脚本,所以想检查一下。到目前为止,如果我找不到该脚本primary
或secondary
文件,我没有退出 shell 脚本的机制clientid
,而且如果在验证阶段某些文件丢失,我也没有退出 shell 脚本的机制。
#!/bin/bash
path=/home/goldy/scripts
mapfiles=(primary-mappings.txt secondary-mappings.txt)
hostfile=host-mapping.txt
machines=(machine1769.abc.host.com proctek5461.def.host.com letyrs87541.pqr.host.com)
# folders on local box where to copy files
primary=/data01/primary
secondary=/data02/secondary
# folders on remote servers from where to copy files
export hold1=/data/snapshot/$1
export hold2=/data/snapshot/$2
date1=$(date +"%s")
# this will tell me what's my clientid given my current hostname
getProperty () {
prop_value=$(hostname -f)
prop_key=`cat $path/$hostfile | grep "$prop_value" | cut -d'=' -f1`
echo $(echo $prop_key | tr -dc '0-9')
}
# if I can't find clientid for my hostname, then I will log a message
# and exit out of shell script with non zero status code
clientid=$(getProperty)
[ -z "$clientid" ] && { echo "cannot find clientid for $(hostname -f)"; exit 1; }
# now once I have valid clientid, then I will get primary and secondary mapping
# from the "host-mapping.txt" file
declare -a arr
mappingsByClientID () {
id=$1 # 1 to 5
file=$path/${mapfiles[$2]} # 0 to 1
arr=($(sed -r "s/.*\b${id}=\[([^]\]+).*/\1/; s/,/ /g" $file))
echo "${arr[@]}"
}
# assign output of function to an array
pri=($(mappingsByClientID $clientid 0))
snd=($(mappingsByClientID $clientid 1))
echo "primary files: ${pri[@]}"
echo "secondary files: ${snd[@]}"
# figure out which machine you want to use to start copying files from
case $(hostname -f) in
*abc.host.com)
local_server=("${machines[0]}")
;;
*def.host.com)
local_server=("${machines[1]}")
;;
*pqr.host.com)
local_server=("${machines[2]}")
;;
*) echo "unknown host: $(hostname -f), exiting." && exit 1 ;;
# ?
esac
export local="$local_server"
# deleting files before we start copying
find "$primary" -maxdepth 1 -type f -exec rm -fv {} \;
find "$secondary" -maxdepth 1 -type f -exec rm -fv {} \;
do_copy() {
el=$1
primsec=$2
(scp -C -o StrictHostKeyChecking=no goldy@"$local":"$hold1"/hello_monthly_"$el"_999_1.data "$primsec"/. > /dev/null 2>&1) || (scp -C -o StrictHostKeyChecking=no goldy@"$local":"$hold2"/hello_monthly_"$el"_999_1.data "$primsec"/. > /dev/null 2>&1)
}
export -f do_copy
# copy files in parallel
parallel -j "$3" do_copy {} $primary ::: ${pri[@]} &
parallel -j "$3" do_copy {} $secondary ::: ${snd[@]} &
wait
echo "all files copied"
# this is for verification to see all files got copied or not
# in primary and secondary folder
set -- "$primary" "$secondary"
typeset -n array
for array in pri snd; do
for num in "${array[@]}"; do
name="hello_monthly_${num}_999_1.data"
if [ ! -f "$1/$name" ]; then
{ echo "$name" not found in "$1" >&2 && exit 1; }
fi
done
shift
done
date2=$(date +"%s")
diff=$(($date2-$date1))
echo "Total Time Taken - $(($diff / 3600)) hours and $(((diff/60) % 60)) minutes and $(($diff % 60)) seconds elapsed."
下面是我的host-mapping.txt
文件,其中会有更多条目。这里的值是一个有效的主机名,键将是字符串“k”,后跟一些数字,该数字应该存在于映射文件中。
k1=machineA.abc.com
k2=machineB.abc.com
k3=machineC.def.com
k4=machineD.pqr.com
k5=machineO.abc.com
下面是我的示例映射文件:
Primary_mappings.txt
{1=[343, 0, 686, 1372, 882, 196], 2=[687, 1, 1373, 883, 197, 736, 1030, 1569], 3=[1374, 2, 884, 737, 198, 1570], 4=[1375, 1032, 1424, 3, 885, 1228], 5=[1033, 1425, 4, 200, 886]}
secondary_mappings.txt
{1=[1152, 816, 1488, 336, 1008], 2=[1153, 0, 817, 337, 1489, 1009, 1297], 3=[1, 1154, 1490, 338], 4=[1155, 2, 339, 1491, 819, 1299, 1635], 5=[820, 1492, 340, 3, 1156]}
例如:clientid
1 有343, 0, 686, 1372, 882, 196
主文件和1152, 816, 1488, 336, 1008
次文件。对于其他人来说clientids
也是如此。
答案1
我有一些建议,所以我想我应该写一个答案。
primary_mappings.txt
关于和 存在不一致,它们在脚本中secondary_mappings.txt
被称为primary-mappings.txt
和。secondary-mappings.txt
您当然应该重命名这些文件(或更改脚本的名称)。我将创建两个函数来处理记录到 STDERR 和退出程序。这样做的优点是可读性更好,不易出错,并且允许您从函数调用中退出(它回答我没有退出 shell 脚本的机制):
trap "exit 1" TERM export TOP_PID=$$ log_error () { echo "$1">&2; } log_error_and_exit () { echo "$1">&2; kill -s TERM $TOP_PID }
您似乎想要将错误记录到 STDERR,但有时您会写入 STDOUT。假设您只是忘记了一些
>&2
,那么通过使用我们新创建的函数很容易将其标准化:A。
[ -z "$clientid" ] && { echo "cannot find clientid for $(hostname -f)"; exit 1; }
变成:
[ -z "$clientid" ] && { log_error_and_exit "Cannot find ClientID for $(hostname -f)"; }
b.
*) echo "unknown host: $(hostname -f), exiting." && exit 1 ;;
变成:
*) log_error_and_exit "Unknown host: $(hostname -f), exiting." ;;
您似乎忘记执行此操作:
如果出于某种原因,我无法从该文件中找到该 clientid 的主文件或辅助文件,那么我将从 shell 脚本退出并记录一条错误消息。
mappingsByClientID () { id=$1 # 1 to 5 file=$path/${mapfiles[$2]} # 0 to 1 if [[ $(< $file) != *" $1="* ]]; then log_error_and_exit "ClientID $1 out of range for $file"; fi arr=($(sed -r "s/.*\b${id}=\[([^]\]+).*/\1/; s/,/ /g" $file)) echo "${arr[@]}" }
您目前不这样做:
如果出于某种原因,我找不到这些文件,那么我想退出 shell 脚本,并显示消息告诉我缺少哪些文件。
由于
exit
一旦发现某个文件丢失,您将永远不会收到多个文件的报告!解决方案是创建一个变量来跟踪错误状态,并在验证结束时检查它:# this is for verification to see all files got copied or not # in primary and secondary folder set -- "$primary" "$secondary" typeset -n array errors=false for array in pri snd; do for num in "${array[@]}"; do name="hello_monthly_${num}_999_1.data" if [ ! -f "$1/$name" ]; then { log_error "$name not found in $1" && errors=true; } fi done shift done if [ "$errors" = true ]; then exit 1 fi