Como usar espaços na função “for” [duplicata]

3

O problema é que estou fazendo um script para a classe, e a pergunta é: "Faça um script que nomeie todos os arquivos no diretório atual e diga-nos o nome e as permissões que eles têm", então eu fiz o seguinte script:

#!/bin/bash

for name in 'ls -l . | awk {print''}'
do
echo "The file $name has the following permissions: 'ls -l . | grep $name | cut -d" " -f1'"
done

Ok, o script funciona um pouco, mas o problema é que ele não mostrará todos os arquivos na lista ls -l porque estou imprimindo o 9th column (o nome do arquivo), mas se ele tiver um espaço, a palavra depois que o espaço está em column 10 , então eu tentei assim:

#!/bin/bash

for name in 'ls -l . | awk {print','}'
do
echo "The file $name has the following permissions: 'ls -l . | grep $name | cut -d" " -f1'"
done

Mas quando faço isso, o problema é que a variável $ name fará referência à primeira coluna e à segunda coluna como palavras separadas, e mostrará ambas como se fossem arquivos diferentes.

Eu também tentei escapar dos espaços em ls usando ls -lb , mas o comando grep não funcionará, e se eu usar grep '$name\s' ele irá copiar literalmente o $ name \ s para que ele não seja pesquise qualquer coisa no comando ls ...

Talvez seja muito fácil resolver esse problema, mas já passei por isso por mais de 5 horas e nada ... Qualquer ajuda é apreciada!

    
por xBeiker 07.03.2018 / 08:58

2 respostas

8

Não analise ls ! Em vez disso, use um glob :

for name in ./*; do

Existem outras maneiras de melhorar o script também.

  1. No loop, novamente, não analise ls . Em vez disso, use stat para obter as permissões diretamente. 1
  2. Use a nova sintaxe de substituição de comandos $(...) em vez de backticks reprovados '...'
  3. Defina uma variável $perms para tornar a sequência de impressão um pouco mais limpa e mais fácil de ler.

Então:

for name in ./*; do
    perms="$(stat -c "%A" "$name")"
    echo "The file $name has the following permissions: $perms"
done

Notas de rodapé:

  1. Se stat não existisse, e você tivesse que obter as permissões de ls , você daria a ele o nome do arquivo como um argumento e usaria o -d flag para que ele não caísse em diretórios ou remeter links simbólicos.

    ls -ld "$name" | cut -d" " -f1
    

    Mas, novamente, você não deve analisar ls . Este método falha se qualquer um dos nomes de arquivo contiver novas linhas.

por wjandrea 07.03.2018 / 09:18
8

Definitivamente não é um bom plano escrever coisas como

for stuff in 'ls'; ...

A análise da saída de ls é uma ideia muito ruim porque a saída de ls é apenas texto e é uma representação não confiável dos nomes reais dos seus arquivos.

Para fazer coisas com arquivos use shell globs .

for stuff in *; ...

Em vez de * , você pode usar ./* , o que significa a mesma coisa, mas é mais seguro porque todos os caminhos começam com ./ (o diretório atual) em vez do símbolo suspeito com o qual seus nomes começam, o que poderia ser um que faz com que o shell execute uma expansão (como ~ ) (embora você definitivamente deva "citar" suas variáveis para evitar expansões de shell indesejadas) ou uma que o comando que você está tentando usar pode tentar interpretar especialmente (como como - ). Você também pode usar * e não ./* , mas adicione -- depois de todas as opções aos comandos executados no loop, para evitar que os nomes de arquivos que começam com - sejam interpretados como opções. Isso dá um caminho limpo, mas se você quiser usar ./* , às vezes, você pode limpar o caminho usando "$(basename $var)" em comandos ou outros truques. Esteja ciente de que nem todos os comandos suportam -- para indicar o final das opções.

Em vez de tentar analisar o que você deseja em ls -l , use um comando especificamente para exibir metadados de arquivo e peça apenas a saída desejada.

stat -c "%n %A" file

deve dar o que você quer. Se você quiser exibir uma mensagem, basta escrever em:)

Veja como eu faria isso:

for file in *; do stat -c 'The permissions of %n are: %A' -- "$file"; done
    
por Zanna 07.03.2018 / 09:18