Ls com espaços + variáveis

1

Eu quero fazer algo assim, mas não salva a variável após o término da tubulação:

fs=( )
echo ${fs[@]}
ls -A1 |
while read f
do
    echo ${fs[@]}
    fs+=( "$f" )
    echo ${fs[@]}
done
echo "All files/dirs: "${fs[@]}

Com os arquivos 1, 2 e 3 no diretório atual, recebo esta saída:

# Output:


1
1
1 2
1 2
1 2 3
All files/dirs: 

Como mantenho o valor da variável fs depois que a tubulação termina?

Atualização:

  • Não é possível usar *, porque eu preciso de arquivos ocultos
  • não pode usar apenas fs = ($ (ls)), enquanto, por vezes, os nomes dos ficheiros / dir terão espaços neles
por Tyilo 19.07.2011 / 21:21

3 respostas

9

Não analise a saída de ls .

A maneira de listar todos os arquivos em um diretório no shell é simplesmente * .

for f in *; do …

Em shells com suporte à matriz (bash, ksh, zsh), você pode atribuir diretamente a lista de arquivos a uma variável de matriz: fs=(*)

Isso omite arquivos de ponto, o que vai contra o uso de ls -A . No bash, defina a opção dotglob primeiro para incluir arquivos de pontos e defina nullglob para ter uma matriz vazia se o diretório atual estiver vazio:

shopt -s dotglob nullglob
fs=(*)

Em ksh, use FIGNORE=".?(.)"; fs=(~(N)*) . Em zsh, use os qualificadores D e N glob: fs=(*(DN)) . Em outras conchas, isso é mais difícil; sua melhor aposta é incluir cada um dos padrões * (arquivos não pontuais), .[!.]* (arquivos de ponto único, não incluindo . e arquivos de ponto duplo) e ..?* (arquivos de ponto duplo, não incluindo .. em si) e verifique se há vazio.

set -- *; [ -e "$1" ] || shift
set .[!.]* "$@"; [ -e "$1" ] || shift
set ..?* "$@"; [ -e "$1" ] || shift
for x; do …  # short for for x in "$@"; do …

Eu explicaria melhor o que estava errado em sua tentativa também. O principal problema é que cada lado de um canal é executado em um subprocesso, portanto, as atribuições para fs no loop estão ocorrendo em um subprocesso e nunca são passadas para o processo pai. (Esse é o caso na maioria dos shells, incluindo bash; as duas exceções são ATT ksh e zsh, onde o lado direito de um pipeline é executado no shell pai.) Você pode observar isso ativando um subprocesso externo e organizando-o para imprimir o ID do processo dos seus pais¹¹²:

sh -c 'echo parent: $PPID'
{ sh -c 'echo left: $PPID >/dev/tty'; echo $? >/dev/null; } |
{ sh -c 'echo right: $PPID >/dev/tty'; echo $? >/dev/null; }

Além disso, seu código teve dois problemas de confiabilidade:

  • Não analise a saída de ls .
  • read manipula espaços em branco e barras invertidas; para analisar linhas, você precisa de while IFS= read -r line; do …

Para os momentos em que você precisa analisar linhas e usar o resultado, coloque todo o processamento de dados em um bloco.

producer … | {
  while IFS= read -r line; do
    …
  done
  consumer
}

Note que $$ não mostra nada: é o ID do processo principal do shell, ele não muda em subshells.
² Em algumas versões bash, se você chamar apenas sh em um lado do canal, poderá ver o mesmo ID do processo, porque o bash otimiza uma chamada para um processo externo. O fluff com as chaves e echo $? anulam essa otimização.

    
por 19.07.2011 / 22:43
-2

Esse código quer que você queira -

#!/bin/sh
echo *

O que exatamente você está tentando fazer aqui?
Você também pode fazer

var=*
echo $var

Se você precisar de mais do que isso, recomendo ir ao stackoverflow.

    
por 19.07.2011 / 21:41
-3

Você realmente precisa do cano? Você pode facilmente fazer isso por:

fs=( )
echo ${fs[@]}
for file in 'ls'
do
        echo $file
        fs+=$file
done
echo $fs
echo "All files/dirs: "${fs[@]}
    
por 19.07.2011 / 22:08