Script para remover espaços e minúsculas em nomes de arquivos

7

Eu estou tentando escrever um script que irá substituir espaços com "-" e fazer todas as letras minúsculas para todos os arquivos no diretório atual.

for x in 'ls'
    do
        if [ ! -f $x ]; then
            continue
        fi

        lc = 'echo $x | tr '[A-Z]' '[a-z]''
        if [ $lc != $x ]; then
            mv $x $lc
        fi
    done

find -name "* *" -type f | rename 's/ /-/g'

Eu recebo a seguinte saída:     chamar: renomear de para arquivos ...

No entanto, os nomes não estão mudando, por exemplo: 252680610243-Analyzed Sample2 2Jul12.txt

Alterei as permissões com chmod 706 , isso estaria causando o problema? O que estou perdendo aqui?

Aqui está a saída de bash -x lower.sh :

+ for x in ''\''ls'\'''
+ '[' '!' -f ls ']'
+ continue
+ find -name '* *' -type f
+ rename 's/ /-/g'
call: rename from to files...
    
por Joe 29.03.2013 / 14:42

5 respostas

8

No Debian e nos derivados (incluindo o Ubuntu), isto é fácil com uma simples chamada para rename :

$ touch 'A B'
$ rename 'tr/ A-Z/-a-z/' -- *
$ ls
a-b

Mas o seu rename é um utilitário diferente com o mesmo nome .

    
por 29.03.2013 / 15:01
6

Existem vários problemas com o seu script.

for x in 'ls'

Você está interagindo com uma lista que contém uma palavra: ls . Você provavelmente quis escrever 'ls' (com backticks), para analisar a saída de ls . Não analise a saída de ls : isso não funcionará se os nomes dos arquivos contiverem caracteres especiais, como espaços. O shell já possui uma maneira integrada de listar os arquivos em um diretório: curingas. Então escreva for x in * .

        if [ ! -f $x ]; then

Isso não funcionará se o nome do arquivo contiver espaço em branco e outros caracteres especiais, porque quando você escreve $x fora de aspas, o resultado é dividido em palavras separadas (e cada palavra é interpretada como um padrão curinga) . Para evitar esse efeito, coloque $x entre aspas duplas: if [ ! -f "$x" ]; then . Você pode se lembrar de regras complexas ou apenas sempre use aspas duplas em torno da variável substituições.

Várias outras linhas foram quebradas devido à falta de aspas duplas. Basta colocá-los em volta de todas as substituições de variáveis.

       lc = 'echo $x | tr '[A-Z]' '[a-z]''

Isso não funcionará devido aos espaços em torno do sinal de igual: esta linha executa o comando lc , passando = como seu primeiro argumento (e outros argumentos). Uma atribuição no shell não deve ter espaço em branco ao redor do sinal = .

Você não precisa de colchetes em torno dos argumentos de tr . Aqui, o comando funciona porque você está dizendo para substituir [ por [ e ] por ] além das letras minúsculas.

Eu recomendo usar parênteses em dólar $(…) em vez de backticks '…' para substituição de comando. Eles são equivalentes, exceto que backticks têm regras complexas quando se trata de citar coisas dentro dele, enquanto $(…) tem uma sintaxe muito intuitiva. Em suma, esse comando deve ser: lc=$(echo "$x" | tr A-Z a-z)

find -name "* *" -type f | rename 's/ /-/g'

Sua mensagem de erro mostra que você tem o comando rename do pacote util-linux e não o Script Perl encontrado no Debian e nos derivados (incluindo o Ubuntu). A sintaxe que você usou só faz sentido com o script Perl.

Se você vai usar o script Perl, ele pode mudar o caso para minúsculas, então você não precisa fazer isso antes. E você não precisa chamar find a menos que queira renomear arquivos em subdiretórios também (o que sua primeira parte não suporta). Se você quiser renomear todos os arquivos no diretório atual, basta escrever:

prename 'tr/A-Z /a-z-/' -- *

Se você quiser apenas renomear arquivos regulares (mas não subdiretórios, links simbólicos, etc.), use find :

find . -type f -maxdepth 1 -exec prename 'tr/A-Z /a-z-/' {} +

Se você quiser atuar em arquivos em subdiretórios também, e os nomes dos diretórios podem conter espaços ou letras maiúsculas, você deve tomar cuidado para deixá-los em paz. Como você está no Linux, você pode usar -execdir para chamar prename com um argumento que não contenha um nome de diretório.

find . -type f -execdir prename 'tr/A-Z /a-z-/' {} +

E se você não tiver o utilitário Perl rename ? O utilitário util-linux rename não o ajudará aqui. Você pode colocar mais uma substituição no seu loop.

for x in ./*; do
  if [ -f "$x" ]; then
    y=$(echo "$x" | tr 'A-Z-' 'a-z ')
    mv "$x" "$y"
  fi
done

Com bash, você não precisa chamar tr , você pode usar suas construções de substituição de strings.

for x in ./*; do
  if [ -f "$x" ]; then
    lc=${x,,}            # convert all letters to lowercase
    y=${lc// /-}         # replace spaces by hyphens
    if [ "$x" != "$y" ]; then
      mv "$x" "$y"
    fi
  fi
done

Se você quiser atuar em arquivos em subdiretórios, também pode usar um glob recursivo (ativado pela opção globstar ). Mais uma vez, tome cuidado para deixar a parte do diretório sozinha, caso ela possa conter letras maiúsculas ou espaços.

shopt -s globstar
for x in ./**/*; do
  if [ -f "$x" ]; then
    old_basename=${x##*/}
    new_basename=${old_basename,,}
    new_basename=${new_basename// /-}
    if [ "$new_basename" != "$old_basename" ]; then
      mv "${x%/*}/$old_basename" "${x%/*}/$new_basename"
    fi
  fi
done

Em zsh, coloque autoload -U zmv no seu .zshrc e você pode usar o zmv com o qualificador da glob . para corresponder apenas arquivos regulares e construções de expansão de parâmetro para renomear:

zmv -Q '*(.)' '${(L)f// /-}'

Para atuar em arquivos em subdiretórios também:

zmv -Qw '**/*(.)' '$1${(L)2// /-}'
    
por 29.03.2013 / 21:45
3

O principal problema é que você não está interagindo com seus arquivos: você está interagindo com uma única string, "ls" . Você provavelmente pretendia usar backticks em vez de aspas simples. No entanto, não analisa ls .

Você também precisa citar suas variáveis, especialmente porque você sabe que elas contêm espaço em branco.

Apenas no bash, você pode fazer isso:

declare -l lc    # whatever is stored in $lc is automatically lower cased
for x in *; do
    lc=${x//[[:space:]]/}  # replace all whitespace with nothing
    [ "$lc" != "$x" ] && mv -- "$x" "$lc"
done
    
por 29.03.2013 / 15:28
2

Você pode estar tendo problemas com o comando "mv" e espaços. Eu rodei o arquivo original com aspas duplas.

Este script funcionou para mim.

#!/bin/bash

for f in *
do
    g=$(echo $f | sed -e 's/\s/\-/g' | tr A-Z a-z)
    mv "$f" $g
done
    
por 29.03.2013 / 15:07
2

No Bash 4 $ {var ,} converte var em minúsculas:

for f in *; do f2=${f,,}; mv "$f" "${f2// /-}"; done
    
por 29.03.2013 / 21:25

Tags