IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Linux下使用Bash遍历文件夹语法解析

    chancel发表于 2024-06-14 00:00:00
    love 0
    <![CDATA[

    1. 概述

    经常需要使用bash批量处理文件格式,每次都要查询一些基础语法很麻烦

    所以以遍历文件夹目录处理图片为例总结一些使用Bash遍历处理文件夹时常见的语法

    2. 遍历

    Bash遍历的方法比较简单,与大部分编程语言的for循环没有明显区别

    Bash
    #/bin/sh
    #author: chancel.yang
    
    for file in ./*
    do
        if test -f "$file"
        then
            echo -e "$file"
        fi
    done
    

    脚本输出如下

    Bash
    ➜ bash demo.sh  
    ./a.png  
    ./b.png  
    ./c.jpg  
    ./demo.sh
    

    这种遍历在大部分情况下都很实用

    3. 路径处理

    上面的输出中会发现执行的脚本demo.sh也在其中,而且只能针对当前路径进行处理

    那么试试添加自定义路径,然后将demo脚本放到其他位置

    Bash
    #/bin/sh  
    #author: chancel.yang  
    
    path="/mnt/SDA/TMP/test"  
    
    for file in $path/*  
    do  
    if test -f "$file"
    then  
        echo -e "$file"
    fi  
    done
    

    输出如下

    Bash
    ➜ bash /mnt/SDA/TMP/demo.sh  
    /mnt/SDA/TMP/test/a.png  
    /mnt/SDA/TMP/test/b.png  
    /mnt/SDA/TMP/test/c.jpg
    

    在实际处理中,可以将$path调整为入参,脚本如下

    Bash
    #/bin/sh  
    #author: chancel.yang  
    for file in $1/*  
    do  
    if test -f "$file"
    then  
        echo -e "$file"
    fi  
    done
    

    执行如下

    Bash
    ➜ bash demo.sh "/mnt/SDA/TMP/test"
    /mnt/SDA/TMP/test/a.png
    /mnt/SDA/TMP/test/b.png
    /mnt/SDA/TMP/test/c.jpg
    

    $0是文件名是名称,如demo.sh

    除了$[number]获取传递进来的参数外,还有一些常见的特殊参数值可以直接使用

    参数 说明
    $# 参数总个数
    $$ 当前PID号

    4. 提取文件名

    上面添加自定义路径之后,输出 $file 也携带了路径信息,实际使用时多要用到文件名,扩展名等

    下面演示如何提取文件路径、文件名(带扩展名)、文件名、扩展名

    Bash
    #/bin/sh  
    #author: chancel.yang  
    
    path="/mnt/SDA/TMP/test"  
    
    for file in $path/*  
    do  
    if test -f "$file"
    then
        echo "dirname: $(dirname $file)"
        echo "filename: $(basename $file)"  
        filename=$(basename $file)  
        echo "filename(suffix): ${filename%%.*}"
        echo "suffix: ${filename#*.}"
    fi  
    done
    

    执行后输出如下

    Bash
    ➜ bash demo.sh  
    dirname: /mnt/SDA/TMP/test  
    filename: a.png  
    filename(suffix): a  
    suffix: png  
    dirname: /mnt/SDA/TMP/test  
    filename: b.png  
    filename(suffix): b  
    suffix: png  
    dirname: /mnt/SDA/TMP/test  
    filename: c.jpg  
    filename(suffix): c  
    suffix: jpg
    

    如果后缀有多个,如a.tar.gz,其他不常见的提取如下

    Bash
    filename='a.tar.gz'
    # a.tar
    ${file%.*}
    # gz
    ${file##*.}
    

    5. 条件判断

    5.1. test与[]

    Bash提供test关键字进行条件判断,偶尔也会看到if [ string1 != string2 ]

    上面俩个写法是等价的,即test与[两者是等价的,都是Bash中的关键字,区别是[要求最后一个参数必须是]

    由于[是一个关键字,而]是一个参数,所以必须有空格,[string1 != string2]这种没有空格的写法是错误的

    由于两者是等价的,所以以下针对test进行说明,将脚本中的test condition等价替换成[ condition ]也是成立的

    5.2. 判断运算符

    以下是常见的条件判断示例脚本

    Bash
    #/bin/sh
    #author: chancel.yang
    
    a=100
    b=200
    c=100
    
    echo -e "a="$a", b="$b", c="$c
    
    if test "$a" -eq "$c"
    then
        echo -e "a = c"
    fi
    
    if test "$a" -ne "$b"
    then
        echo -e "a != b"
    fi
    
    if test "$b" -gt "$a"
    then
        echo -e "b > a"
    fi
    
    
    dirname=`pwd`
    filename=$dirname/demo.sh
    
    echo -e "dirname: "$dirname", filename: "$filename
    
    if test -d "$dirname"
    then
        echo -e $dirname" is a folder"
    fi
    if test -f "$filename"
    then
        echo -e $filename" is a file"
    fi
    

    执行输出如下

    Bash
    ➜ bash demo.sh
    a=100, b=200, c=100
    a = c
    a != b
    b > a
    dirname: /mnt/SDA/TMP, filename: /mnt/SDA/TMP/demo.sh
    /mnt/SDA/TMP is a folder
    /mnt/SDA/TMP/demo.sh is a folder
    

    条件判断数值类型时,其常见运算符参数列表如下

    参数 说明
    -eq 相等
    -ne 不相等
    -gt >
    -ge >=
    -lt <
    -le <=

    判断条件文件时,其常见运算符参数列表如下

    参数 说明
    -e 文件存在
    -r 文件存在且可读
    -w 文件存在且可写
    -x 文件存在且可执行
    -s 文件存在且内容不为空
    -d 是目录
    -f 是文件

    文件与数值内容的判断相对简单,接下来看看比较复杂的字符串判定

    在test中,判断字符串为空时,-z表示当字符串为空时返回True,-n表示字符串不为空时返回True

    Bash
    #/bin/sh
    #author: chancel.yang
    
    string_1=""
    string_2="hello"
    
    if test -z "$string_1"
    then
        echo -e "string_1 is null"
    fi
    
    if test -n "$string_2"
    then
        echo -e "string_2 is not null"
    fi
    

    执行输出如下

    Bash
    ➜ bash demo.sh  
    string_1 is null  
    string_2 is not null
    

    使用test进行判断时,推荐使用双引号括起来变量,否则因为变量内容带空格而达不到预期的执行效果

    考虑到变量如路径可能是用户输入的,除了双引号来避免变量路径中带有空格外,还可以使用[[]]来避免内容拆分

    5.3. 特殊的[[]]

    [与test判断时都会对内容进行拆分,如test="go to"会被识别成go与to两个参数

    与[不同,[[与]]均是命令,]]并不是参数

    以下代码演示两者的不同

    Bash
    #/bin/sh
    #author: chancel.yang
    
    a="go to"
    
    # 此处会报错
    if test $a == "go to"
    then
        echo -e "$a == 'go to'"
    fi
    
    # 此处添加双引号可以正确判断
    if test "$a" == "go to"
    then
        echo -e "$a == 'go to'"
    fi
    
    # 此处无需双引号也可以正确判断
    if [[ $a == "go to" ]]
    then
        echo -e "$a == 'go to'"
    fi
    

    输出如下

    Bash
    ➜  bash demo.sh
    demo.sh: line 7: test: too many arguments
    go to == 'go to'
    go to == 'go to'
    

    6. 图片转换例子

    到这里,写一个遍历脚本的基本语法已经齐全,下面是一个转换指定目录中所有文件为webp图片的遍历脚本,以供参考

    Bash
    #/bin/sh
    #author: chancel.yang
    #date: 2022-06-14
    show_help() {
        echo "$0 [-h|-?|--help] [--who me] [--why hhh]"
        echo "-h|-?|--help    显示帮助"
        echo "--path          文件夹路径"
    }
    
    while [[ $# -gt 0 ]]; do
        case $1 in
        -h | -\? | --help)
            show_help
            exit 0
            ;;
        --in)
            in="${2}"
            shift
            ;;
        --out)
            out="${2}"
            shift
            ;;
        *)
            echo -e "Error: $0 invalid option '$1'\nTry '$0 --help' for more information.\n" >&2
            exit -1
            ;;
        esac
        shift
    done
    
    if !(test "$in")
    then
        echo "Please run --help for usage"
        exit
    fi
    if !(test "$out")
    then
        echo "Please run --help for usage"
        exit
    fi
    
    if !(test -d "$in")
    then
        echo "Error: $in is not a folder"
        exit
    fi
    
    if test -f "$out"
    then
        echo "Error: $out is a file"
        exit
    fi
    
    if [ ! -d "$out" ]; then
      mkdir $out
    fi
    
    for file in $in/*
    do
        filename=$(basename $file)
        if test -f "$file"
        then
            /usr/bin/ffmpeg -i $file -c:v libwebp $out/${filename%%.*}.webp
            echo "$file convert to $out/$filename.webp"
        fi
    done
    
    echo -e "Convert success"
    

    参考资料

    • Bash技巧:对比 test、[、[[ 判断字符串是否为空的用法



沪ICP备19023445号-2号
友情链接