menu E4b9a6's blog
rss_feed
E4b9a6's blog
有善始者实繁,能克终者盖寡。

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

作者:E4b9a6, 创建:2021-03-09, 字数:5755, 已阅:298, 最后更新:2021-03-09

这篇文章更新于 1353 天前,文中部分信息可能失效,请自行甄别无效内容。

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"会被识别成goto两个参数

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

以下代码演示两者的不同

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"

参考资料


[[replyMessage== null?"发表评论":"发表评论 @ " + replyMessage.m_author]]

account_circle
email
web_asset
textsms

评论列表([[messageResponse.total]])

还没有可以显示的留言...
gravatar
[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[getEnviron(messageItem.m_environ)]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[getEnviron(messageItem.m_environ)]]