Shell 实战大全

变量

shell中的变量,一般用 ${dir} 还是 $dir 会更好一些,两种有什么异同?

在 Bash shell 中,${dir}$dir 都可以用来引用变量。它们的主要区别在于如何处理变量名后面紧跟着的字符。以下是它们的异同点:

  1. 使用大括号 {} 的情况

    • ${dir}:大括号 {} 可以明确地标识变量名的边界,从而避免与后面紧跟的文本混淆。这在变量名后面跟着其他字符时特别有用,比如在字符串连接时或在数组元素访问时。
    • 例如,${dir}ectory 可以确保在变量名 dir 后面立即跟着 ectory,而不会被解释为一个名为 directory 的变量。
  2. 不使用大括号 {} 的情况

    • $dir:直接使用 $ 符号来引用变量,通常适用于简单的变量引用,没有后续字符或者后续字符已经被明确分隔的情况下。在这些情况下,不使用大括号也是允许的,比如在简单的赋值、条件判断或输出中。

综上所述,使用 ${dir} 可以提高代码的可读性和可维护性,特别是在变量名后面紧跟着其他字符时。但在某些简单的情况下,可以直接使用 $dir,这更加简洁明了。选择使用哪种形式主要取决于代码的上下文和个人偏好。

遍历

你可以将这三个目录路径放入一个数组中,然后使用循环来遍历数组中的每个目录。以下是相应的 Shell 脚本示例:

#!/bin/bash

# 定义目录数组
directories=("/app/dat" "/app/ok" "/app/ddl")

# 循环遍历目录数组
for dir in "${directories[@]}"; do
    echo "处理目录: $dir"
    # 遍历当前目录下的文件
    for file in "$dir"/*; do
        if [ -f "$file" ]; then
            echo "处理文件: $file"
            # 在这里执行具体的业务处理操作
        fi
    done
done

在这个脚本中,我们首先定义了一个名为 directories 的数组,其中包含了三个目录的路径。然后,我们使用一个嵌套的 for 循环来遍历每个目录。在内部循环中,我们遍历当前目录下的文件,并执行特定的业务处理操作。

你可以在 # 在这里执行具体的业务处理操作 的注释下方编写你的业务处理逻辑。

函数

确实,上述的 parse_mapping() 方法在可读性上可能不是最佳选择。我们可以改进这个方法,使其更易读和易于理解。以下是一个优化后的版本:

#!/bin/bash

# 解析映射关系函数
parse_mapping() {
    local table_name="$1"
    local mapping_file="$2"

    # 检查映射文件是否存在
    if [ ! -f "$mapping_file" ]; then
        echo "错误:映射文件 '$mapping_file' 不存在。"
        return 1
    fi

    # 查找指定表名的映射关系
    local mapping=$(grep "^$table_name=" "$mapping_file")

    if [ -n "$mapping" ]; then
        local partition_field="${mapping#*=}"
        echo "表名: $table_name,分区字段: $partition_field"
    else
        echo "未找到与 '$table_name' 相关的映射关系。"
        return 1
    fi
}

# 调用解析映射关系函数
parse_mapping "T_78_TBL" "mapping.txt"

这个优化后的方法改进了可读性,并添加了一些错误处理的逻辑。以下是改进的主要点:

  1. 将表名和映射文件作为函数的参数,以提高函数的灵活性和重用性。
  2. 添加了对映射文件是否存在的检查,以避免尝试解析不存在的文件。
  3. 使用 grep 命令来查找指定表名的映射关系,并对结果进行判断处理。
  4. 使用 ${variable#pattern} 语法从匹配的字符串中提取分区字段,避免了使用复杂的循环结构。

通过这些改进,函数变得更易读、更灵活,同时保留了与原始函数相同的功能。这样可以提高代码的可维护性和可读性。

在Shell脚本中,函数的返回值是通过标准输出传递的,可以通过命令替换或使用 $() 语法来接收函数的返回值。在这种情况下,parse_mapping() 函数可以通过输出所需的结果,然后在调用它的地方通过命令替换来接收返回值。

例如,如果我们想要在脚本中捕获 parse_mapping() 函数的输出,可以像这样调用它:

result=$(parse_mapping "T_78_TBL" "mapping.txt")
echo "$result"

这将执行 parse_mapping() 函数,并将输出保存到 result 变量中。然后,我们可以使用 echo 命令打印出 result 变量的值。

另外,如果函数需要传递多个返回值,则可以通过标准输出打印多个值,并使用适当的分隔符(如空格或换行符)来区分它们。在调用函数时,可以使用命令替换和 read 命令来分别读取这些返回值。

更改目录权限

根据你提供的信息,挂载目录/nas的权限设置为drwxrwxrwx,所有者和所属组都是app。这意味着所有用户都具有读、写和执行该目录的权限。

为了确保数据库服务能够正常使用这个挂载目录,你可能需要进行以下操作:

  1. 更改目录所有者
    确保目录的所有者是数据库服务所使用的用户(通常是postgres)。你可以使用chown命令更改目录的所有者。例如:

    sudo chown -R postgres:postgres /nas
  2. 设置适当的权限
    将目录的权限设置为只允许所有者读、写和执行,而其他用户没有任何权限。这可以通过以下命令实现:

    sudo chmod -R 700 /nas

执行以上操作后,/nas目录将具有适当的所有者和权限设置,以确保数据库服务可以正常访问和操作其中的文件。请确保在更改权限之前备份重要的数据,并确保这些更改不会影响其他系统或服务的正常运行。

实战

文件下载

download_files.sh

#!/bin/bash

####################.############$########1

#步骤:
#step0:从远程服务器(阿里云服务器)下载文件
#sep1:将dat文件转为 ore 文件
#step2:将 ore 文件上传到HDFS
#step3:load data hdfs文件到Hive库调度:
#crontab 定时调度#/15 12-14 每天中午12-14点之间,每隔15分钟执行一次)    
#crontab #/15 12-14syne_data.sh >> xxx.1og(按天分割)
#其他:
#windows开发在linux执行报错换行符问,可通过该命令转换:dos2unix syne_file_to_hive.sh
#file_name="ECLDB_t_c_i9_ecl_final_regult_h_20240320_F.dat"
#################################################################

#------下载远程文件BEGIN ----
#ANSI颜色定义 
RED='\033[0;31m'
NC='\033[0m No Color'
DELIMITER="\u0001"#文本分隔符 
etldate=$(date'+%Y%m%d')
#etldate="20240331"
echo " >>>>>>>>>>> 开始下载s{etldate)的文件<<<<<< "
start_time="$(date +%s)"
#远程服务器连接信息 
IP="" 
PORT=22
USER=""
PASSSTR="" 
SECSTR="待获取"
PASSWORD=$(eoch"$SECSTR" | openss1 enc -d -aes-256-cbc -a -a1t-pbkde2 -paa= pass:$DASSSTR | tail -n1)
#远程FTP服务器目录
BASE_SRCDIR="/bdpdata/BYD-00-STAGE/ML/WHLXOADB/AML"
本地服务器基目录
BASE_DESDIR="/app/nfs_eyne/AML"

#本地服务器目录(需要同步det、ok、ddI)
#mkdir -p /app/nfs_=ync/AML/dat
#mkdir -p /app/nfs_sync/AML/ok
#mkdir -p /app/nfs_sync/AML/ddI
#chmod -R 755 /app/nfs_gync/AML/dat
#chmod -R 755 /app/nfs_syne/AML/ok 
#chmod -R 755 /app/nfs_syne/AML/ddl
#业务目录
dat dir="dat"
ok_dir="ok"
ddl_dir="ddl"
#创建本地目录 
dir_arr=("$dat_dir" "$ok_dir" "$ddl_dir")   
for dir in "${dit arr[@]}";do   
   #拼接路径,如/app/nfs_sync/AML/dat/20240401    
   cur_desdir="${BASE_DESDIR}/${dir}/${etldate}"    
   cur_sredir="${BASE_SRCDIR}/${diz}/${etldate}"    

   #判断目录是否存在。如果不存在,则创建  
    if[!-d seur desdir ]; then
    mkdir -p $cur_desdir    
    echo "> mkdir -p scur_desdir"   
    fi
done

    # 全路径   
    cur_srcdir_ok="${BASE_SRCDIR}/${ok_dir}/${etldate}"
    cur_srcdir_ddl="${BASE_SRCDIR}/${ddl_dir}/${etldate}"   
    cur_srcdir_dat="${BASE_SRCDIR}/${dat_dir}/${etldate}"
    cur desdir_ok="${BASE_DESDIR}/${ok_dir}/${etldate}"
    cur_desdir_ddl="${BASE_DESDIR}/*{ddl_d1z}/${etldate}"   
    cur_desdir_dat="s{BASE_DESDIR}/*{dat_dix}/${etidate}"   
    # 检查远程目录(ok)是否存在    
lftp -e "cd scur_aredir_ok;exit" -u "$USER","$PASSWORD" $IP 
#获取上一个命令的退出状态码,并判断目录是否存在,存在则下载ok文件,不存在则退出程序 
STATUS=$?   
if [ $STATUS -eq 0 ];then
    lftp -u "${UEER},${PASSWORD}" sftp://"${iP}:${PORT}"<<EOF
    set net:reconnect-interval-base 5
    set net:max-retries 3 
    set net:timeout 10m
    set xrer:clobber on
    set sftp:connect-program "ssh -a-x-0 StrietHostKeyChecking=no"
     et  et  
    cd "$(cur_arcdir_ok)"
    lcd "$(cur.deadir_ok)"
    mirzor --ume-pget-n-s --continde overbose
    bye 
EOF

#遍历下载下来的ok文件 
for ok file in "$cur_dendir_ok"/*;do
    if [ -f "$ok_file" l;then
        #提取文件名
        # filename="ECLDB_t_e_i9_ecl_final_result_h_20240320_F.dat" 
        filename=$ (basename"$ok file") 
        #echo "filename=$filename"
        # 解析ok、ddi文件名
        dat_ filename="${filename/%.ok/.dat}" 
        ddl_filename="${filename/t.ok/.ddl}"
        #echo "dat_filename=$dat_filename"  
        #echo "ddl_filename=$ddl_filename"  
        #判断远程服务器dat路径是否存在,存在则获取文件列表,并下载存在ok文件的相应的dat文件 
        lftp -e "cd scur_aredir_dat;exit "-u "sUSER","$PASSWORD" $IP 
        dat_file_status=$?
        if [ "$dat_file_status" -eq 0 ];then
            echo"开始下载$dat_filename"
            lftp -u "${USER},${PASSWORD}" sftp://"${IP}:${PORT}"<<EOF
            set net:reconnect-interval-base5 
            set net:max-retries 3
            set net:timeout 10m
            set xfer:clobber on
            set sttp:connect-progrem "ssh -a -x -o StrietHostKeyChecking=no" 
            cd "${cur gredir dat}"
            pget -n 5 "$dat_filename" -o "$cur_desdir _dat" 
            bye 
EOF
            download_dat_flag=$?    
            #获取下载结果状态并判断下载结果,下载完成则输出结果,下载失败输出结果并退出程序    
            if [ "$download_dat_flag" -eq 0 ];then  
                echa ">>>>$dat_Eilenane download complete"
            e1se
                echo ">>>>sdat_filenamedowmload tailed,exit"
                exit 1              
            fi  
        else    
            echo"$(sur_aredir_dat): no such file,exit"  
            exit 1  
        fi

        #判断远程服务器da1路径是否存在,存在则下载ox文件的相应的ddl文件,不存在则退出
        lftp -e "cd $cur_srcdir_ddl;exit " -u "$USER","$PASSWORD" $IP 
        ddl_file_status=$?
        if [ "$ddl file statua" -eq 0 ];then
            echo"开始下载$didl_filename"
            lftp -u "${USER},${PASSWORD}" sftp://"${IP}:${PORT}" <<EOF
            set net:reconnect-interval-base 5 
            set net:max-retries 3 
            set net:timeout 10m 
            set xfer:clobber on
            set sftp:connect-program "ssh -a -x -o StrictHostKeyChecking=no" 
            cd "${cur $redir ddl}"
            pget -n 5 "$ddl_filename” -o "$scur dendir_ddl" 
            bye
EOF
        #获取下载结果状态并判断下载结果,下载完成则输出结果,下载失败输出结果并出程序 
            download_ ddl_flag=$?
            if [ "$download_ddl_flag" -eq 0 ];then
                echo ">>>>$ddl_filename download complete" 
            else
                echo ">>>>$ddl_filename download failed,exit" 
                exit 1 
            fi
        else
            echo "&(eur_aredir_ddl):no such file,exit" 
            exit 
        fi
    fi  
done
end time=$ (date +%s)
cost_time=$(end_time - start_time))
echo " >>>>>>>>>>> Daimload +$(etldate) files complated, cnes time $coat_time s <<<<<"
#   下截远程文件 END  
exit 1

为者常成,行者常至