Não é possível usar arquivos com caracteres especiais no shell

4

Eu quero percorrer um diretório e, para cada arquivo que não é um diretório, quero anexar os nomes e separá-los por vírgula.

files=""
for f in $(ls ~/Downloads | grep -v ^d)
        do

                files+="$f"","

        done 
        echo "$files"

aqui a pasta de downloads tem arquivos como

este: 01 - Agar Mujhse Mohabbat Hai - www.downloadming.com.mp3

Produção esperada: 01 - Agar Mujhse Mohabbat Hai - www.downloadming.com.mp3

Resultado real: 01, -, Agar, Mujhse, Mohabbat, Hai, - www.downloadming.com.mp3

Todas as soluções são bem-vindas.

    
por cafebabe1991 08.05.2014 / 18:36

4 respostas

13

Não analise ls

files=()

cd ~/Downloads
for file in *; do
    [[ -d $file ]] && continue
    files+=( "$file" )
done

printf "%s\n" "${files[@]}"

comma_separated=$(IFS=,; echo "${files[*]}")
echo "$comma_separated"

Como você viu, for f in $(ls) itera as palavras na saída, não as linhas . Salve ls para quando você quiser que os olhos humanos vejam os arquivos e use os padrões de nome de arquivo do shell quando quiser que seu script faça algo com nomes de arquivos.

    
por glenn jackman 08.05.2014 / 19:00
2

Solução mais curta: find ~/Downloads -type f -not -name "d*" -printf "%f,"

    
por Jos 08.05.2014 / 19:04
1

Use while read em vez de for

A maneira mais segura que encontrei para iterar listas de nomes de arquivos é canalizar para while read desta forma:

find ~/Downloads -type f | while read filename; do
  echo "found file called $filename";
done

Como você pode ver, ele coloca o nome do arquivo inteiro (separando por linhas em vez de espaço em branco) no nome da variável que você fornece, aqui $filename .

A partir da pergunta original, no entanto, se o que é realmente desejado é uma lista de arquivos separados por vírgula, você não precisa nem repetir, apenas use tr para tr anslate line separadores em vírgulas:

find ~/Downloads -type f | tr '\n' ','
    
por Phil H 09.05.2014 / 14:49
1

Este é um problema do Separador de Campo Interno. Pelo contrário, não é um problema, foi projetado dessa maneira. Ele divide a entrada pelo espaço em branco, assim como nos parâmetros posicionais.

Na página do manual do Bash:

  

para nome [[ em [ palavra ...]]; ] faça lista ; concluído

     
    

A lista de palavras após em é expandida, gerando uma lista de itens. A variável nome é definida para cada elemento desta lista, por sua vez, e lista é executada a cada vez. Se o em palavra é                   omitido, o comando for executa lista uma vez para cada parâmetro posicional definido. O status de retorno é o status de saída do último comando que é executado. Se o                   a expansão dos itens após em resulta em uma lista vazia, nenhum comando é executado e o status de retorno é 0.

  

Isso significa que ele expande os resultados da expressão em uma lista de palavras. É aqui que entra o separador de campo interno, $ IFS. Por padrão, o $ IFS é definido para espaços em branco (espaço, tabulação e nova linha). Isso significa que um campo consiste em tudo até (mas não incluindo) esses caracteres de espaço em branco. Como os nomes de arquivos consistem em espaços, eles estão tratando-os como vários campos diferentes e executando o loop uma vez para cada campo. A maneira de corrigir isso é ajustar o valor de $ IFS.

Então, você precisa ajustar o $ IFS para algo diferente do espaço em branco. Usando seu método atual, no entanto, não haveria nenhuma maneira de dizer onde um nome de arquivo termina e o outro começa, a menos que todos tenham extensões de arquivo. Uma maneira mais fácil de fazer isso seria usar o utilitário find da seguinte forma:

#Save the original IFS
oldifs=$IFS
#Set the new IFS to the ASCII null character \x00
IFS=$(echo -ne "\x00")

files="" #To prevent scoping issues

#Use the find utility to find and print just regular files (not directories),
#and not going further than ~/Downloads (remove '-maxlength 1' to make the
#search fully recursive), then print the filenames in the format
#file1\x00file2\x00file3\x00file4...

#However, since our new IFS is now \x00, the boundary upon which these filenames are
#separated now works as expected
for f in $(find ~/Downloads -maxdepth 1 -type f -print0)
do
  files="$files,$f"
done
echo "$files"

#Reset IFS
IFS=$oldifs

Eu também gostaria de salientar que sua declaração ls ~/Downloads | grep -v ^d provavelmente não faz o que você espera. Parece que você estava tentando filtrar diretórios, mas o que está realmente acontecendo aqui é que ele está filtrando os arquivos que começam com 'd'. Isso ocorre porque você não está usando a forma longa de ls like ls -l . ls -l imprime a saída como:

drwxrwxrw- user group SomeDirectory size date_modified filename

wheras 'ls' imprime apenas os nomes dos arquivos:

SomeDirectory
file1
file2
file3
    
por Chuck R 08.05.2014 / 19:12

Tags