Como obter a lista de arquivos em um diretório em um script de shell?

159

Estou tentando obter o conteúdo de um diretório usando o shell script.

Meu script é:

for entry in `ls $search_dir`; do
    echo $entry
done

Onde $search_diré um caminho relativo. No entanto, $search_dircontém muitos arquivos com espaços em branco em seus nomes. Nesse caso, esse script não é executado conforme o esperado.

Eu sei que poderia usar for entry in *, mas isso só funcionaria para o meu diretório atual.

Sei que posso mudar para esse diretório, usar e for entry in *depois voltar, mas minha situação particular me impede de fazer isso.

Eu tenho dois caminhos relativos $search_dire $work_dir, e eu tenho que trabalhar em ambos simultaneamente, lendo-os criando / excluindo arquivos neles etc.

Então, o que eu faço agora?

PS: Eu uso bash.

jrharshath
fonte

Respostas:

274
for entry in "$search_dir"/*
do
  echo "$entry"
done
Ignacio Vazquez-Abrams
fonte
4
Você pode explicar por for entry in "$search_dir/*"que não funciona? Por que precisamos colocar /*fora das aspas?
precisa saber é o seguinte
6
@ Mrgloom: Porque você precisa deixar o shell glob o curinga.
Ignacio Vazquez-Abrams
não findseria mais rápido?
Alexej Magura 14/03
4
A solução fornece o caminho completo. E se eu apenas quisesse listar o que estava no diretório atual?
ThatsRightJack
1
@mrgloom se você quiser fazê-lo, você pode conseguir isso comfor entry in "${search_dir}/*"
funilrys
28

As outras respostas aqui são ótimas e respondem à sua pergunta, mas este é o principal resultado do google para "bash get list of files in directory" (que eu estava procurando salvar uma lista de arquivos), então pensei em publicar um responda a esse problema:

ls $search_path > filename.txt

Se você deseja apenas um determinado tipo (por exemplo, arquivos .txt):

ls $search_path | grep *.txt > filename.txt

Observe que $ search_path é opcional; ls> filename.txt fará o diretório atual.

tegan
fonte
Não é necessário usar o grep para obter apenas arquivos .txt: `ls $ search_path / *. Txt> filename.txt '. Mais importante, porém, não se deve usar a saída do comando ls para analisar os nomes dos arquivos.
Victor Zamanian
1
@VictorZamanian, você pode explicar por que não devemos usar a saída lspara analisar nomes de arquivos? Nunca ouvi falar disso antes.
samurai_jane
1
@samurai_jane Há muitos links para fornecer sobre esse tópico, mas aqui está um primeiro resultado de pesquisa: mywiki.wooledge.org/ParsingLs . Eu até vi uma pergunta aqui no SO alegando que os motivos para não analisar a saída de ls eram BS e foi muito elaborador sobre isso. Mas as respostas / respostas ainda afirmavam que era uma má idéia. Dê uma olhada: unix.stackexchange.com/questions/128985/…
Victor Zamanian
26

Esta é uma maneira de fazer isso onde a sintaxe é mais simples para eu entender:

yourfilenames=`ls ./*.txt`
for eachfile in $yourfilenames
do
   echo $eachfile
done

./é o diretório de trabalho atual, mas pode ser substituído por qualquer caminho que
*.txtretorna algo.txt.
Você pode verificar o que será listado facilmente digitando o lscomando diretamente no terminal.

Basicamente, você cria uma variável que yourfilenamescontém tudo o que o comando list retorna como um elemento separado e, em seguida, passa por ela. O loop cria uma variável temporária eachfileque contém um único elemento da variável pela qual está fazendo loop, nesse caso, um nome de arquivo. Isso não é necessariamente melhor que as outras respostas, mas acho intuitivo porque já estou familiarizado com o lscomando e a sintaxe do loop for.

rrr
fonte
Isso funciona bem para um script rápido e informal ou uma linha, mas será interrompido se um nome de arquivo contiver novas linhas, ao contrário das soluções baseadas em glob.
Soren Bjornstad
@SorenBjornstad obrigado pelo conselho! Eu não sabia que novas linhas eram permitidas nos nomes de arquivos - que tipo de arquivo poderia tê-las? Tipo, isso é algo que ocorre normalmente?
rrr
As novas linhas nos nomes de arquivos são ruins por esse motivo e, até onde eu sei, não há motivos legítimos para usá-los. Eu nunca vi um na natureza. Dito isto, é totalmente possível construir nomes de arquivos maliciosamente com novas linhas, de forma a explorar isso. (Por exemplo, imagine um diretório contendo arquivos A, BeC . Você cria arquivos chamados B\nCe D, em seguida, optar por excluí-los. Software que não lidar com esse direito pode acabar excluindo preexistente arquivos B e C, em vez mesmo se você não tem permissão para fazer isso.)
Soren Bjornstad 4/18/18
19
for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done
ghostdog74
fonte
11
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"
Noel Yap
fonte
1
Eu sei que isso é muito antigo, mas eu não consigo obter o último xargs -0 -i echo "{}"comando, quer me explicar um pouco? Em particular, o que a -i echo "{}"peça faz? Também li na manpágina que -iestá obsoleta agora e devemos usar -Iinsted.
drkg4b
1
-isubstitui {}com o arg.
Noel Yap
Obrigado! Isso é útil, também para os lentos como eu, acho que {}é a string que é substituída pelas correspondências pelo findcomando.
Drkg4b
3
por que você usa xargs? por padrão, findimprime o que encontra ... você pode excluir tudo -print0.
gniourf_gniourf
1
Fazer isso não lidaria bem com entradas de arquivos com espaços.
9139 Noel Yap
4
$ pwd; ls -l
/home/victoria/test
total 12
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  a
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  c
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'c d'
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  d
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_a
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'e; f'

$ find . -type f
./c
./b
./a
./d
./c d
./e; f

$ find . -type f | sed 's/^\.\///g' | sort
a
b
c
c d
d
e; f

$ find . -type f | sed 's/^\.\///g' | sort > tmp

$ cat tmp
a
b
c
c d
d
e; f

Variações

$ pwd
/home/victoria

$ find $(pwd) -maxdepth 1 -type f -not -path '*/\.*' | sort
/home/victoria/new
/home/victoria/new1
/home/victoria/new2
/home/victoria/new3
/home/victoria/new3.md
/home/victoria/new.md
/home/victoria/package.json
/home/victoria/Untitled Document 1
/home/victoria/Untitled Document 2

$ find . -maxdepth 1 -type f -not -path '*/\.*' | sed 's/^\.\///g' | sort
new
new1
new2
new3
new3.md
new.md
package.json
Untitled Document 1
Untitled Document 2

Notas:

  • . : pasta atual
  • remover -maxdepth 1para pesquisar recursivamente
  • -type f: encontre arquivos, não diretórios ( d)
  • -not -path '*/\.*' : não retorne .hidden_files
  • sed 's/^\.\///g': remova o anexo ./da lista de resultados
Victoria Stuart
fonte
0

Aqui está outra maneira de listar arquivos dentro de um diretório (usando uma ferramenta diferente, não tão eficiente quanto algumas das outras respostas).

cd "search_dir"
for [ z in `echo *` ]; do
    echo "$z"
done

echo *Produz todos os arquivos do diretório atual. O forloop itera sobre cada nome de arquivo e é impresso em stdout.

Além disso, se estiver procurando por diretórios dentro do diretório, coloque-o dentro do forloop:

if [ test -d $z ]; then
    echo "$z is a directory"
fi

test -d verifica se o arquivo é um diretório.

SnoopDogg
fonte
0

A resposta aceita não retornará o prefixo dos arquivos com a. Para fazer isso, use

for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
  echo "$entry"
done
TrevTheDev
fonte
-1

Na versão Linux, eu trabalho com (x86_64 GNU / Linux) os seguintes trabalhos:

for entry in "$search_dir"/*
do
  echo "$entry"
done
Andrushenko Alexander
fonte
-1: é idêntico à resposta aceita, exceto que não cita as variáveis. Não citar $search_dirsignifica que ele quebra se houver um espaço no nome do diretório. (Neste caso, você pode começar afastado com não citar $entry, mas que seria melhor prática para citá-lo.)
Soren Bjornstad
Você está errado. Minha solução não é a mesma, mesmo quando semelhante à resposta aceita. E não está errado, funciona. Aqui está o que eu tentei em diferentes sistemas unix, incluindo o Mac OS, e funcionou em todos eles. O arquivo com espaços em branco, mesmo quando possui espaços em branco à esquerda no nome, é exibido corretamente. search_dir =. para entrada em $ search_dir / *; faça eco da entrada $; done ./ aa aaa.txt ./add ./apm_tables.txt Você pode votar nas soluções apenas quando provar que a solução NÃO FUNCIONA ou produz resultados incorretos.
22419 Andrushenko Alexander
Se você ler meu comentário um pouco mais de perto, verá que o problema é com espaços em branco no nome do diretório , não no nome do arquivo . Isso é diferente do problema em que o questionador se deparou, mas ainda significa que o código pode ser quebrado sem querer! Aqui está um exemplo do que não funciona: mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done. O loop é executado duas vezes, uma vez na impressão xe na segunda y/*, o que presumivelmente não é o resultado esperado.
Soren Bjornstad 15/03/19
E considero as variáveis ​​indevidamente não citadas perigosamente erradas, o que é motivo para um voto negativo. Considere este código para eliminar quaisquer subdiretórios do diretório de pesquisa, deixando os arquivos: search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; done. Se também houver um xdiretório no diretório atual, ele será excluído sumariamente, apenas porque você não citou sua variável!
Soren Bjornstad 15/03/19
Bem, você sempre pega um código criado para uma tarefa, altera a tarefa levemente e o código fica errado. Para a pergunta feita, minha solução proposta funciona. Mas ... como desenvolvedor, tenho que concordar com seus argumentos: se podemos tornar um código mais robusto e geral, devemos fazê-lo. Então vou adicionar as qoutes. Isso tornará minha resposta idêntica à resposta dada acima, mas por uma questão de discussão (que pode ser útil para alguém) também deixo a resposta.
Andrushenko Alexander
-1

Basta digitar este comando simples:

ls -d */
Michael Sisko
fonte
Você poderia dar alguma explicação @Michael Sisko
Vipul
ls (lista) -d (lista apenas diretórios) * / (de qualquer padrão, a barra invertida restringe a lista apenas aos diretórios).
Michael Sisko