【问题标题】:How to clean up multiple file names using bash?如何使用 bash 清理多个文件名?
【发布时间】:2019-04-16 05:15:38
【问题描述】:

我有。包含 ~250 个 .txt 文件的目录。这些文件中的每一个都有这样的标题:

Abraham Lincoln [December 01, 1862].txt

George Washington [October 25, 1790].txt

等等……

但是,对于读入 python 来说,这些文件名很糟糕,我想遍历所有文件名以将它们更改为更合适的格式。

我尝试过类似的方法来更改跨多个文件共享的单个变量。但是我不知道应该如何迭代这些文件并更改它们的名称格式,同时仍然保持相同的信息。

理想的输出应该是这样的

1861_12_01_abraham_lincoln.txt

1790_10_25_george_washington.txt

等等……

【问题讨论】:

  • 发布您的尝试(代码)并解释失败的原因和原因
  • “读入 python 的文件名很糟糕” - 为什么?
  • 正如 Amadan 所说,Python 没有理由在使用带有空格和方括号的文件名时遇到问题。

标签: string bash date filenames


【解决方案1】:

请尝试简单(乏味)的 bash 脚本:

#!/bin/bash

declare -A map=(["January"]="01" ["February"]="02" ["March"]="03" ["April"]="04" ["May"]="05" ["June"]="06" ["July"]="07" ["August"]="08" ["September"]="09" ["October"]="10" ["November"]="11" ["December"]="12")

pat='^([^[]+) \[([A-Za-z]+) ([0-9]+), ([0-9]+)]\.txt$'
for i in *.txt; do
    if [[ $i =~ $pat ]]; then
        newname="$(printf "%s_%s_%s_%s.txt" "${BASH_REMATCH[4]}" "${map["${BASH_REMATCH[2]}"]}"  "${BASH_REMATCH[3]}" "$(tr 'A-Z ' 'a-z_' <<< "${BASH_REMATCH[1]}")")"
        mv -- "$i" "$newname"
    fi
done

【讨论】:

  • 不错。不知道 BASH_REMATCH。打算用 sed 写点东西。
  • 您可以通过使用printf -v newname "%s_%s_%s_%s.txt" ... 来提高效率,因此不需要命令替换的fork()、FIFO 创建等。
【解决方案2】:
for file in *.txt; do
    # extract parts of the filename to be differently formatted with a regex match
    [[ $file =~ (.*)\[(.*)\] ]] || { echo "invalid file $file"; exit; }

    # format extracted strings and generate the new filename
    formatted_date=$(date -d "${BASH_REMATCH[2]}" +"%Y_%m_%d")
    name="${BASH_REMATCH[1]// /_}"  # replace spaces in the name with underscores
    f="${formatted_date}_${name,,}" # convert name to lower-case and append it to date string
    new_filename="${f::-1}.txt"     # remove trailing underscore and add `.txt` extension

    # do what you need here
    echo $new_filename
    # mv $file $new_filename
done 

【讨论】:

    【解决方案3】:

    我喜欢把文件名拆开,然后再放回去。

    GNU date 也可以解析出时间,这比使用sed 或大的case 语句将“October”转换为“10”更简单。

    #! /usr/bin/bash
    
    if [ "$1" == "" ] || [ "$1" == "--help" ]; then
        echo "Give a filename like \"Abraham Lincoln [December 01, 1862].txt\" as an argument"
        exit 2
    fi
    
    filename="$1"
    
    # remove the brackets
    filename=`echo "$filename" | sed -e 's/[\[]//g;s/\]//g'`
    
    # cut out the name
    namepart=`echo "$filename" | awk '{ print $1" "$2 }'`
    
    # cut out the date
    datepart=`echo "$filename" | awk '{ print $3" "$4" "$5 }' | sed -e 's/\.txt//'`
    
    # format up the date (relies on GNU date)
    datepart=`date --date="$datepart" +"%Y_%m_%d"`
    
    # put it back together with underscores, in lower case
    final=`echo "$namepart $datepart.txt" | tr '[A-Z]' '[a-z]' | sed -e 's/ /_/g'`
    
    echo mv \"$1\" \"$final\"
    

    编辑:从 Bourne shell 转换为 BASH。

    【讨论】:

    • 请注意,应使用= 而不是==,以养成与/bin/sh 兼容的习惯。见the POSIX specification for test and [
    • ...强烈推荐使用$( )而不是反引号;除其他外,反斜杠在 $( ) 内部的工作更一致,而它们在反引号内的行为与其在它们外部的行为不同(必然是因为如果您嵌套命令替换,则需要使用反斜杠来转义内部反引号,而 $( ) 原生嵌套)。 1970 年代的 Bourne 可能不支持使用 $( ),但在现代系统上 /bin/sh 是 POSIX sh 而不是传统的 Bourne;自 1991 年 POSIX.2 最初发布以来,$( ) 一直是 POSIX sh 标准的一部分。
    • @CharlesDuffy 我基本上同意你的 cmets。但是在这里我专门使用 bash,当我这样做时,我使用 ==。对于执行,我更喜欢使用反引号,因为 $() 语法用于太多相似的东西,我发现反引号更容易识别它是什么,而 $(something) 需要更仔细的阅读。但是,是的,有时反斜杠会很痛苦。
    • == 的使用是合理的,但反引号确实不行。请参阅BashFAQ #82 以获得完整的讨论——$( ) 无疑是更一致的语法,因此更易于阅读。另见wiki.bash-hackers.org/syntax/expansion/…
    猜你喜欢
    • 2012-05-01
    • 2012-02-12
    • 2017-04-08
    • 2022-11-17
    • 2013-03-27
    • 2016-06-29
    • 1970-01-01
    • 1970-01-01
    • 2010-09-23
    相关资源
    最近更新 更多