Como faço para o shell reconhecer os nomes de arquivos retornados por um comando 'ls -A' e esses nomes contêm espaços?

3

Esta é a parte do script que estou usando para renomear os arquivos retornados por ls -A :

for FILE in 'ls -A'
do
        ext=${FILE##*.}
        NUM=$(($NUM+1))
        NEWFILE='echo _$NUM.$ext | sed 's/ /_/g''

        mv "$FILE" "$NEWFILE"
done

Mas não consegue encontrar nomes com espaço! O parâmetro $ 2 não pode ser a causa do erro, porque eu pisei um nome sem espaço como um parâmetro, e esse script usa esse nome e renomeia todos os arquivos da pasta para que eles sejam numerados e não modifiquem a extensão do arquivo. Infelizmente, ele não renomeia arquivos com espaços no nome. Alguém pode me ajudar?

    
por Patterson 14.03.2014 / 12:59

5 respostas

2

ls -A para mim escreve vários nomes de arquivos em uma linha, separados por espaços em branco. Se você tentou adicionar -1 como em ls -A1 , isso produziria um nome de arquivo por linha e poderia funcionar melhor para você.

Eu me deparo com os mesmos problemas com espaços no nome do arquivo, espeicamente ao usar o find, mas separar nomes com um caractere nulo manipula espaços & amp; novas linhas em nomes de arquivos:
find (...) -print0 | xargs -0 (do stuff here)

Mas, se você quiser apenas renomear arquivos, você pode considerar man rename como:

For example, to rename all files matching "*.bak" to strip the extension,
you might say
     rename 's/\.bak$//' *.bak
To translate uppercase names to lower, you'd use
     rename 'y/A-Z/a-z/' *

Ou para uma solução de gui para alguns diretórios, o Thunar tem uma boa interface para renomear arquivos múltiplos que pode fazer numeração como você está descrevendo também. Eu tentei alguns nomes de arquivos com espaços combinados com find para Thunar e parece funcionar:
find . -type f -print0 | xargs -0 thunar -B

    
por Xen2050 14.03.2014 / 14:05
9

for itera em palavras , as palavras são delimitadas por espaço em branco. Você não deve iterar sobre a saída de ls , você deve usar * .* :

for file in * .* ; do
    if [[ $file = . || $file = .. ]] ; then
        continue
    fi
    # ...
done
    
por choroba 14.03.2014 / 13:27
6

Você pode incluir arquivos ocultos ('arquivos de ponto') no shell glob bash '*' definindo a opção de shell dotglob

shopt -s dotglob
for file in *
do 
  echo "$file"
done

por exemplo. para um diretório que contém file , file with spaces e .hidden file (o último dos quais está oculto e tem um espaço), isso produz

file
file with spaces
.hidden file

Você pode querer adicionar a opção nullglob também para evitar uma condição de erro no caso de o diretório estar vazio - veja o excelente BashFAQ / 004 . Lembre-se de citar a variável "$file" e também é recomendável não usar letras maiúsculas para os nomes das variáveis.

    
por steeldriver 14.03.2014 / 14:05
1

Caso você ainda não tenha descoberto, analisar ls é uma Idéia ruim ® . A menos que você saiba que seus nomes de arquivos sempre serão saudáveis (você geralmente não pode), você precisa ser capaz de lidar com nomes de arquivos contendo:

  • espaços e tabulações
  • espaços ou guias consecutivos
  • novas linhas ( \n )
  • retornos de carro ( \r )
  • barras invertidas ( \ )

Todas as opções acima são permitidas pelo kernel do Linux (e garantem que você dirija seu sysadmin). Os dois métodos a seguir podem lidar com qualquer combinação dos itens acima. Estou usando cp file directory/ como um comando de exemplo):

  1. Use a opção find e seu -exec , o {} será substituído por cada arquivo ou diretório encontrado.

    find . -exec cp {} directory/
    
  2. Pipe os resultados do find para xargs como strings separadas nulas ( -print0 ), diga xargs para ler nulo separado ( -0 ) e diga para substituir {} por cada nome de arquivo / diretório ( -I {} ).

    find . -print0 | xargs -0 -I {} cp {} directory/
    
  3. Use o shell sozinho, para corresponder também aos dotfiles, ative dotglob

     shopt -s dotglob
     for i in *; do cp -v "$i" directory/; done
    
  4. Combine o poder de find e a versatilidade do shell

    find . -print0 | while IFS= read -r -d '' i; do cp "$i" directory/; done
    

    O IFS= desativa a divisão em espaços, o -r desativa escapes de barra invertida (permite que barras invertidas sejam tratadas literalmente), o -d '' define o separador de registro (linhas, se desejar) como nulo.

por terdon 15.03.2014 / 05:01
0

Que tal:

ls -A | while read fname
do
    echo "$fname" # your code goes here
done

Piping ls força o envio de 1 nome de arquivo por linha, read aceita a linha na variável fname .

    
por efwolcott 14.03.2014 / 19:46