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 .
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...
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 .
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// /-}'
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
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
No Bash 4 $ {var ,} converte var em minúsculas:
for f in *; do f2=${f,,}; mv "$f" "${f2// /-}"; done