【问题标题】:Using bash I need to remove trailing spaces before a file extension in file names使用 bash 我需要在文件名中的文件扩展名之前删除尾随空格
【发布时间】:2015-05-27 04:46:33
【问题描述】:

我有数百个文件看起来类似于:

"QDN34 Unit5 mark-up - Judy .pdf"  
"QDN34 Unit7 mark-up - Judy .pdf"  
"file with two character ext .ai"  
"file with dot. trailing space and no ext "  
"file with no ext"

请注意,除了最后一个之外,所有文件的末尾都有一个空格,不包括相关的文件扩展名。

我需要在文件名中保留空格(不理想)并删除那些尾随空格。
结果应该是:

"QDN34 Unit5 mark-up - Judy.pdf"
"QDN34 Unit7 mark-up - Judy.pdf"  
"file with two character ext.ai"  
"file with dot. trailing space and no ext"  
"file with no ext"

到目前为止,我有:

newName=$(find . -type f -name \*\ .* | cut -d'.' -f2 | sed 's/\ $//' | sed 's/^\/*//')
extens=$(find . -type f -name \*\ .* | sed 's@.*/.*\.@.@')
oldName=$(find . -type f -iname \*\ .* | sed 's/^\.\/*//')
for f in "$oldName" ; do mv -nv "$oldName" "$newName""$extens" ; done

但是我收到了看起来索引不匹配的错误。感觉我应该使用数组,但我不确定如何。

输出是:

mv: rename file with two character ext .ai  
QDN34 Unit5 mark-up - Judy .pdf  
QDN34 Unit7 mark-up - Judy .pdf to file with two character ext  
QDN34 Unit5 mark-up - Judy  
QDN34 Unit7 mark-up - Judy.ai  
.pdf  
.pdf: No such file or directory

【问题讨论】:

    标签: bash sed cut


    【解决方案1】:

    您可以尝试使用prename 实用程序和以下perl regular expression

    user@host $ prename 's/\s(\..+$)/$1/g' *
    
    • \s - 表示空格字符
    • \. - 表示点
    • . - 表示一个字符
    • +$ - 表示将前一个符号重复到行尾
    • $1 - 表示替换为表达式 在()

    如果您没有此实用程序,您可以使用以下 bash 脚本:

    # you can replace the "*" in the line below
    # with any necessary find or ls command to rename only necessary files
    for old_name in *;
    do
        new_name=$(echo "$old_name"|sed 's/ \(\.[^ \t]*$\)/\1/g')
        echo "\"$old_name\" --> \"$new_name\""
        mv "$old_name" "$new_name" 
    done
    

    【讨论】:

    • 很遗憾,我的 Isilon 或 OS X 上没有 prename
    • 非常好的方法!我发现唯一有用的更改是将+ 添加到空间变量,因此它可以处理多个尾随空格。即prename 's/\s+(\..+$)/$1/g' *
    【解决方案2】:
    #!/bin/bash
    IFS=$(echo -en "\n\b") && for a in $(ls -f1 *); do 
    file_ext="${a##*.}"
    if [[ ! -z "${file_ext}" && ${#file_ext} -lt 4 ]]; then 
     file_base="${a%.*}"
    else 
     file_base="${a}" 
     file_ext=""
    fi 
    [ "${file_base:$((${#file_base}-1)):1}" = " " ] && \
     file_base="${file_base% *}"
    new_file="${file_base}${file_ext:+.}${file_ext}" 
    if [ ! "${new_file}" = "${a}" ]; then 
     echo "mv -nv \"$a\" \"${new_file}\"" 
     #mv -nv "${a}" "${file_base}.${file_ext}"
    fi 
    done 
    

    您可以在要重命名它们的目录中安全地运行此 bash 脚本以查看它会做什么,然后如果它会完成您想要的操作,您可以取消注释真正的移动命令并重新运行它。

    根据 OP cmets 更新 - 此脚本认为 . 与右侧最多 3 个(小于 4 个)的文件扩展名有关,更改它以反映您的数据集。

    【讨论】:

    • 我担心的是我需要处理任何扩展名的文件,这些文件可能在“.ext”之前有一个尾随空格。在上面的示例中,我有多种文件类型需要更正。也不是每个文件扩展名都符合 3 个字符。
    • 我更新了答案,这适用于您提供的示例数据 - 您可能需要针对扩展数据集进行调整。
    【解决方案3】:

    试试这个:

    find . -type f | while read i; do mv "$i" "$(sed 's/ \.\([a-z0-9]*\)$/\.\1/' <<< "$i")"; done;
    

    对于每个文件(从当前目录递归),删除文件扩展名前的空格(使用sed),然后用新名称重命名原始文件(sed 命令的结果)。

    【讨论】:

    • 需要将I 选项添加到 sed 以允许处理这样的扩展:*.JPG。也许用[ \t] 替换模式会更好,因为扩展中可能包含一些数字。
    • @Michael 我认为您对I 的建议意味着它将被放置在这里...)$/\.\1/I' 但这似乎不起作用。我哪里错了?
    • @chop 对,I 必须放在那里。在没有看到整个模式的情况下很难说什么是错的。试试这个简单的模式,不管有没有Iecho "ABSabs" | sed 's/abs/xyz/Ig'g 表示替换所有匹配的模式。一定在工作。更好的模式是[^ \t]*,因为它考虑了除空格和制表符之外的所有符号。
    • @Michael 我刚刚尝试了您的建议。看起来在 Mac OS X 上,I 不起作用。我收到sed: 1: "s/abs/xyz/Ig": bad flag in substitute command: 'I'
    【解决方案4】:

    Bash 解决方案

    这也是 Bash 解决方案可能有用的地方。以下测试在脚本中使用 extsz 设置的扩展 (max 4 chars + .)。如果找到扩展名,脚本会从文件名中删除空格,然后将文件从旧名称移动到新名称(实际移动注释如下)。它依靠参数扩展/子字符串替换来操作空格和文件名:

    #!/bin/bash
    
    declare -i extsz=-5     # extension w/i last 4 chars
    
    ## trim leading/trailing whitespace
    function trimws {
        [ -z "$1" ] && return 1
        local strln="${#1}"
        [ "$strln" -lt 2 ] && return 1
        local trimstr=$1
        trimstr="${trimstr#"${trimstr%%[![:space:]]*}"}"  # remove leading whitespace characters
        trimstr="${trimstr%"${trimstr##*[![:space:]]}"}"  # remove trailing whitespace characters
        printf "%s" "$trimstr"
        return 0
    }
    
    ## for each filename read from stdin
    while read -r ffname || test -n "$ffname" ; do
    
        ## test for extension and set 'ext' if present
        for ((i=$extsz; i<0; i++)); do
            [ "${ffname:(i):1}" == '.' ] && { ext=${ffname:(i)}; break; }
        done
    
        ## if extension, move the file to name w/o trailing space w/orig extension
        if [ -n "$ext" ]; then
    
            fname="${ffname%.*}"          # separate filename from extension
            fnwosp="$(trimws "$fname")"   # trim whitespace from filename
    
            printf "   renaming :  '%s' -> '%s'\n" "$ffname" "${fnwosp}${ext}"
            #mv "$ffname" "${fnwosp}${ext}"  # commented for testing
    
        else
            ## if no extension, just trim whitespace and move
            printf "   renaming :  '%s' -> '%s'\n" "$ffname" "$(trimws "$ffname")"
            # mv "$ffname" "$(trimws "$ffname")"
        fi
    
        unset ext       # unset 'ext' for next iteration
    
    done
    
    exit 0
    

    输入

    $ cat dat/wfname.txt
    QDN34 Unit5 mark-up - Judy .pdf
    QDN34 Unit7 mark-up - Judy .pdf
    file with two character ext .ai
    file with dot. trailing space and no ext
    file with no ext
    

    输出

    $ bash fixfilenames.sh <dat/wfname.txt
       renaming :  'QDN34 Unit5 mark-up - Judy .pdf' -> 'QDN34 Unit5 mark-up - Judy.pdf'
       renaming :  'QDN34 Unit7 mark-up - Judy .pdf' -> 'QDN34 Unit7 mark-up - Judy.pdf'
       renaming :  'file with two character ext .ai' -> 'file with two character ext.ai'
       renaming :  'file with dot. trailing space and no ext' -> 'file with dot. trailing space and no ext'
       renaming :  'file with no ext' -> 'file with no ext'
    

    注意:当从stdin 读取时,shell 会去除不带扩展名的文件名的尾随空格。

    将文件名读取为参数

    为了说明从没有扩展名的文件名末尾删除空格,有必要引用文件名并将其作为参数读取。如果这是您需要的,这里有一个替代品。无论如何,将文件名作为参数而不是从标准输入中批量读取可能更有意义:

    #!/bin/bash
    
    declare -i extsz=-5     # extension w/i last 4 chars
    
    ## trim leading/trailing whitespace
    function trimws {
        [ -z "$1" ] && return 1
        local strln="${#1}"
        [ "$strln" -lt 2 ] && return 1
        local trimstr=$1
        trimstr="${trimstr#"${trimstr%%[![:space:]]*}"}"  # remove leading whitespace characters
        trimstr="${trimstr%"${trimstr##*[![:space:]]}"}"  # remove trailing whitespace characters
        printf "%s" "$trimstr"
        return 0
    }
    
    ## test at least 1 command line argument
    [ $# -gt 0 ] || {
        printf "error: insufficient input.  usage: %s <filename>\n" "${0##*/}"
        exit 1
    }
    
    ## for each of the filenames give as arguments
    for ffname in "$@"; do
    
        ## test for extension and set 'ext' if present
        for ((i=$extsz; i<0; i++)); do
            [ "${ffname:(i):1}" == '.' ] && { ext=${ffname:(i)}; break; }
        done
    
        ## if extension, move the file to name w/o trailing space w/orig extension
        if [ -n "$ext" ]; then
    
            fname="${ffname%.*}"          # separate filename from extension
            fnwosp="$(trimws "$fname")"   # trim whitespace from filename
    
            printf "   renaming :  '%s' -> '%s'\n" "$ffname" "${fnwosp}${ext}"
            #mv "$ffname" "${fnwosp}${ext}"  # commented for testing
    
        else
    
            ## if no extension, just trim whitespace and move
            printf "   renaming :  '%s' -> '%s'\n" "$ffname" "$(trimws "$ffname")"
            # mv "$ffname" "$(trimws "$ffname")"
    
        fi
    
        unset ext
    
    done
    
    exit 0
    

    示例

    $ bash fixfilenames.sh 'testfile w end space '
       renaming :  'testfile w end space ' -> 'testfile w end space'
    
    $ bash fixfilenames.sh 'file with two character ext .ai'
       renaming :  'file with two character ext .ai' -> 'file with two character ext.ai'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-08
      • 1970-01-01
      • 2017-02-23
      • 2018-10-22
      • 1970-01-01
      • 1970-01-01
      • 2018-07-04
      相关资源
      最近更新 更多